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

时间:2019-04-14 13:27:13

标签: 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))

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

INSERT INTO tbl_other 
VALUES(.......) - and set ID_VAR_OF_THIS_QUERY='id of executor' -

我得到以下结果:

           tbl_log
-----------------------------
id                | op      |
-----------------------------
'id of executor'  | 'INSERT'|

我希望我已经提出了这个主意...我认为这几乎不可行...但是有谁可以帮助我吗?

2 个答案:

答案 0 :(得分:2)

一个选项是创建一个共享表来保存此信息。由于它是每个连接的,因此主键应为pg_backend_pid()


    create table connection_global_vars(
       backend_pid bigint primary key,
       id_of_executor varchar(50)
    );
    insert into connection_global_vars(backend_pid) select pg_backend_pid() on conflict do nothing;
    update connection_global_vars set id_of_executor ='id goes here' where backend_pid = pg_backend_pid();


    -- in the trigger: 

CREATE TRIGGER t AFTER INSERT OR UPDATE OR DELETE ON tbl_executor 
FOR EACH ROW 
EXECUTE PROCEDURE (INSERT INTO tbl_log VALUES( (select id_of_executor from connection_global_vars where backend_pid = pg_backend_pid()) ,TG_OP))

另一种选择是创建一个临时表(每个连接都存在)。

 create temporary table if not exists connection_global_vars(
       id_of_executor varchar(50)
    ) on commit delete rows;
    insert into connection_global_vars(id_of_executor) select null where not exists (select 1 from connection_global_vars);
    update connection_global_vars set id_of_executor ='id goes here';



    -- in the trigger: 

CREATE TRIGGER t AFTER INSERT OR UPDATE OR DELETE ON tbl_executor 
FOR EACH ROW 
EXECUTE PROCEDURE (INSERT INTO tbl_log VALUES( (select id_of_executor from connection_global_vars where backend_pid = pg_backend_pid()) ,TG_OP))

特别是对于PostgreSQL,它可能不会对性能产生太大影响,除了未记录的临时表可能只是稍快一点​​。

如果您由于无法识别单个行表而遇到性能问题,则可以运行分析。

答案 1 :(得分:2)

回答问题

您可以SETcustomized option)像这样:

cat

但这需要一个 literal 值。还有功能@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (MainActivity.isDarkMode) { return inflater.inflate(R.layout.layout_dark, container, false); } else { return inflater.inflate(R.layout.layout_light, container, false); } } Quoting the manual:

  

SET myvar.role_id = '123'; ...设置参数并返回新值

     

set_config()将参数set_config(setting_name, new_value, is_local)设置为set_config。如果setting_namenew_value,则新值仅适用于当前交易。

相应地,使用SHOWcurrent_setting()读取选项值。相关:

但是您的触发器使用错误的语法在错误的表(is_local)上。看起来像Oracle代码,您可以在其中直接向true提供代码。在Postgres中,您首先需要 触发函数

所以:

tbl_executor

您的示例设置需要类型强制转换为CREATE TRIGGER
然后:

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;

最后,从表::int中提取CREATE TRIGGER trg_log_who AFTER INSERT OR UPDATE OR DELETE ON tbl_other -- ! FOR EACH ROW EXECUTE PROCEDURE trg_log_who(); -- ! 来设置变量:

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; )设置为is_local,使其成为 session-local 。 (相当于set_config()。)

但是为什么要每行?按声明进行修改似乎更合理?

true

不同的方法

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

...
FOR EACH STATEMENT EXECUTE PROCEDURE trg_foo();

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

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
);

请注意executor... INSERT INTO tbl_log(op) VALUES(TG_OP); ... 之间的区别。参见: