在 Postgres 查询期间设置临时全局变量

Pho*_*ocs 4 database postgresql

是否可以在查询期间设置可由 TRIGGER 过程捕获的变量(仅对相关查询有效)?

例如,我想记录查询执行者的ID(current_user始终相同)。所以我会做这样的事情:

tbl_executor (
  id   PRIMARY KEY,
  name VARCHAR
);
tbl_log (
  executor REFERENCE tbl_executor(id),
  op VARCHAR
);
tbl_other ...

CREATE TRIGGER t AFTER INSERT OR UPDATE OR DELETE ON tbl_executor 
FOR EACH ROW 
EXECUTE PROCEDURE (INSERT INTO tbl_log VALUES( ID_VAR_OF_THIS_QUERY ,TG_OP))
Run Code Online (Sandbox Code Playgroud)

现在,如果我运行如下查询:

INSERT INTO tbl_other 
VALUES(.......) - and set ID_VAR_OF_THIS_QUERY='id of executor' -
Run Code Online (Sandbox Code Playgroud)

我得到以下结果:

           tbl_log
-----------------------------
id                | op      |
-----------------------------
'id of executor'  | 'INSERT'|
Run Code Online (Sandbox Code Playgroud)

我希望我已经提出了这个想法...而且我认为这几乎不可行...但是有人可以帮助我吗?

Erw*_*ter 5

回答问题

您可以像这样SET自定义选项):

SET myvar.role_id = '123';
Run Code Online (Sandbox Code Playgroud)

但这需要一个面值。还有这个功能set_config()引用手册:

set_config(setting_name, new_value, is_local)...设置参数并返回新值

set_config将参数设置setting_namenew_value。如果is_localtrue,则新值将仅适用于当前交易。

SHOW相应地,用或读取选项值current_setting()。有关的:

但是您的触发器位于错误的表 ( tbl_executor) 上,并且语法错误。看起来像Oracle代码,你可以直接提供代码CREATE TRIGGER。在 Postgres 中,你首先需要一个触发函数:

所以:

CREATE OR REPLACE FUNCTION trg_log_who()
  RETURNS trigger AS
$func$
BEGIN
   INSERT INTO tbl_log(executor, op)
   VALUES(current_setting('myvar.role_id')::int, TG_OP);  -- !

   RETURN NULL;  -- irrelevant for AFTER trigger     
END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

您的示例设置需要 a 类型转换::int
然后:

CREATE TRIGGER trg_log_who
AFTER INSERT OR UPDATE OR DELETE ON tbl_other  -- !
FOR EACH ROW EXECUTE PROCEDURE trg_log_who();  -- !
Run Code Online (Sandbox Code Playgroud)

最后,id从表中获取tbl_executor数据来设置变量:

BEGIN;
SELECT set_config('myvar.role_id', id::text, true)   -- !
FROM   tbl_executor
WHERE  name = current_user;

INSERT INTO tbl_other VALUES( ... );
INSERT INTO tbl_other VALUES( ... );
--  more?
COMMIT;
Run Code Online (Sandbox Code Playgroud)

设置to的第三个参数 ( is_local)以使其按请求为会话本地。(相当于。)set_config()trueSET LOCAL

但为什么每按声明来写似乎更合理?

...
FOR EACH STATEMENT EXECUTE PROCEDURE trg_foo();
Run Code Online (Sandbox Code Playgroud)

不同的方法

除此之外,我会考虑一种不同的方法:一个返回 aid列默认值的简单函数:

CREATE OR REPLACE FUNCTION f_current_role_id()
  RETURNS int LANGUAGE sql STABLE AS
'SELECT id FROM tbl_executor WHERE name = current_user';

CREATE TABLE tbl_log (
  executor int DEFAULT f_current_role_id() REFERENCES tbl_executor(id)
, op VARCHAR
);
Run Code Online (Sandbox Code Playgroud)

然后,在触发函数中,忽略该executor列;将自动填充:

...
   INSERT INTO tbl_log(op) VALUES(TG_OP);
...
Run Code Online (Sandbox Code Playgroud)

current_user请注意和之间的区别session_user。看: