问题描述
db.task(t => {
return t.none('set search_path to myschema').then(() => {
return t.any('select * from mytable').then(results => {
return t.none('set search_path to originalschema').then(() => {
return results
})
})
})
})
db.task() 之外的查询,碰巧在 db.task() 内的两次 search_path 更改之间运行,是否可以实际访问“myschema”而不是“originalschema”中的数据?
解决方法
db.task() 之外的查询,碰巧在 db.task() 内的两次 search_path 更改之间运行,是否可以实际访问“myschema”而不是“originalschema”中的数据?
没有
SET search_path
是基于会话的操作,即它仅适用于当前连接,任务在其整个执行期间专门分配该连接。
一旦任务完成,它就会释放连接回池。此时,任何获得相同连接的查询都将使用替代模式,除非是另一个任务再次设置模式。如果您只在一项任务中设置架构,这会变得很棘手,通常不建议这样做。
这里应该是这样的:
- 如果您只想在一项任务中访问特殊情况架构,最好在查询中明确指定架构名称。
- 如果您想为整个应用动态设置自定义架构,最好使用 Initialization Options 的选项
schema
。这将通过所有新连接自动传播架构。 - 如果您想静态设置架构,请there are queries for setting schema permanently。
添加:
如果您有一个非常特殊的情况,即您有一个任务需要在替代架构中运行可重用查询,那么您可以在任务的开头设置架构,然后将其恢复到默认架构结束,因此以后获取该连接的任何其他查询都不会尝试使用错误的架构。
额外:
下面的示例创建您自己的 task
方法(我称之为 taskEx
),在整个协议中保持一致,它接受新选项 schema
,以在任务中设置可选架构:
const initOptions = {
extend(obj) {
obj.taskEx = function () {
const args = pgp.utils.taskArgs(arguments); // parse arguments
const {schema} = args.options;
delete args.options.schema; // to avoid error thrown
if (schema) {
return obj.task.call(this,args.options,t => {
return t.none('SET search_path to $1:name',[schema])
.then(args.cb.bind(t,t));
});
}
return obj.task.apply(this,args);
}
}
});
const pgp = require('pg-promise')(initOptions);
因此您可以在代码中的任何位置使用:
const schema = 'public';
// or as an array: ['public','my_schema'];
db.taskEx({schema},t => {
// schema set inside task already;
});
请注意,taskEx
实现假定架构是完全动态的。如果它是静态的,则没有必要在每次任务执行时重新发出 SET search_path
,并且您可能只想根据以下检查对新连接执行此操作:
const isFreshConnection = t.ctx.useCount === 0;
但是,在这种情况下,您最好使用初始化选项 schema
代替,如前所述。