是否可以在查询期间设置可由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'|
我希望我已经提出了这个主意...我认为这几乎不可行...但是有谁可以帮助我吗?
答案 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)
您可以SET
(customized 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_name
为new_value
,则新值仅适用于当前交易。
相应地,使用SHOW
或current_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);
...
之间的区别。参见: