Chu*_*ckE 12 postgresql global-variables plpgsql postgresql-triggers
我想在会话/事务中记录用户的id,使用SET
,所以我以后可以在触发器函数中使用它来访问它current_setting
.基本上,我正在尝试从之前发布的非常相似的票证中选择n2 ,区别在于我正在使用PG 10.1.
我一直在尝试3种设置变量的方法:
SET local myvars.user_id = 4
,从而在交易中在本地设置;SET myvars.user_id = 4
从而将其设置在会话中;SELECT set_config('myvars.user_id', '4', false)
,取决于最后一个参数,将是前两个选项的快捷方式.它们都不能在触发器中使用,触发器NULL
在获取变量时接收current_setting
.这是我设计的用于对其进行故障排除的脚本(可以很容易地与postgres docker镜像一起使用):
database=$POSTGRES_DB
user=$POSTGRES_USER
[ -z "$user" ] && user="postgres"
psql -v ON_ERROR_STOP=1 --username "$user" $database <<-EOSQL
DROP TRIGGER IF EXISTS add_transition1 ON houses;
CREATE TABLE IF NOT EXISTS houses (
id SERIAL NOT NULL,
name VARCHAR(80),
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now(),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS transitions1 (
id SERIAL NOT NULL,
house_id INTEGER,
user_id INTEGER,
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now(),
PRIMARY KEY(id),
FOREIGN KEY(house_id) REFERENCES houses (id) ON DELETE CASCADE
);
CREATE OR REPLACE FUNCTION add_transition1() RETURNS TRIGGER AS \$\$
DECLARE
user_id integer;
BEGIN
user_id := current_setting('myvars.user_id')::integer || NULL;
INSERT INTO transitions1 (user_id, house_id) VALUES (user_id, NEW.id);
RETURN NULL;
END;
\$\$ LANGUAGE plpgsql;
CREATE TRIGGER add_transition1 AFTER INSERT OR UPDATE ON houses FOR EACH ROW EXECUTE PROCEDURE add_transition1();
BEGIN;
%1% SELECT current_setting('myvars.user_id');
%2% SELECT set_config('myvars.user_id', '55', false);
%3% SELECT current_setting('myvars.user_id');
INSERT INTO houses (name) VALUES ('HOUSE PARTY') RETURNING houses.id;
SELECT * from houses;
SELECT * from transitions1;
COMMIT;
DROP TRIGGER IF EXISTS add_transition1 ON houses;
DROP FUNCTION IF EXISTS add_transition1;
DROP TABLE transitions1;
DROP TABLE houses;
EOSQL
Run Code Online (Sandbox Code Playgroud)
我得出的结论是,该函数是在不同的事务和不同的(?)会话中触发的.这是可以配置的东西,所有这些都发生在同一个上下文中吗?
目前尚不清楚为什么要尝试连接NULL
,user_id
但这显然是导致问题的原因.摆脱它:
CREATE OR REPLACE FUNCTION add_transition1() RETURNS TRIGGER AS $$
DECLARE
user_id integer;
BEGIN
user_id := current_setting('myvars.user_id')::integer;
INSERT INTO transitions1 (user_id, house_id) VALUES (user_id, NEW.id);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
注意
SELECT 55 || NULL
Run Code Online (Sandbox Code Playgroud)
总是给NULL
.
正确处理自定义选项的所有可能情况:
选项尚未设置
对它的所有引用都会引发异常,包括current_setting()
除非使用第二个参数调用missing_ok
.手册:
如果没有命名设置
setting_name
,current_setting
除非missing_ok
提供错误,否则抛出错误true
.
选项设置为有效的整数文字
选项设置为无效的整数文字
选项复位(其中被烧毁到的特殊情况3)
例如,如果使用SET LOCAL
或设置自定义选项,set_config('myvars.user_id3', '55', true)
则会在事务结束时重置选项值.它仍然存在,可以引用,但它返回一个空字符串now(''
) - 无法转换为integer
.
除了你的演示中明显的错误,你需要准备所有4个案例.所以:
CREATE OR REPLACE FUNCTION add_transition1()
RETURNS trigger AS
$func$
DECLARE
_user_id text := current_setting('myvars.user_id', true); -- see 1.
BEGIN
IF _user_id ~ '^\d+$' THEN -- one or more digits?
INSERT INTO transitions1 (user_id, house_id)
VALUES (_user_id::int, NEW.id); -- valid int, cast is safe
ELSE
INSERT INTO transitions1 (user_id, house_id)
VALUES (NULL, NEW.id); -- use NULL instead
RAISE WARNING 'Invalid user_id % for house_id % was reset to NULL!'
, quote_literal(_user_id), NEW.id; -- optional
END IF;
RETURN NULL; -- OK for AFTER trigger
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
db <> 在这里小提琴
笔记:
避免使用与列名匹配的变量名.非常容易出错.一种流行的命名约定是使用下划线添加变量名称:_user_id
.
在声明时分配以保存一个作业.请注意数据类型text
.在整理出无效输入后,我们将在稍后进行投射.
如果可能,避免引发/捕获异常.手册:
包含
EXCEPTION
子句的块比没有子块的块进入和退出要贵得多.因此,请勿在不需要的情况下使用EXCEPTION
.
测试有效的整数字符串.这个简单的正则表达式只允许数字(没有领先的标志,没有空格)_user_id ~ '^\d+$'
.我为任何无效输入重置为NULL.适应您的需求.
WARNING
为了方便调试,我添加了一个可选项.
只会出现个案3.
,4.
因为自定义选项是字符串文字(类型text
),无法自动强制执行有效数据类型.
有关:
除此之外,根据您的具体要求,如果没有自定义选项,可能会有更优雅的解决方案.也许这个:
小智 5
当值不存在时,您可以捕获异常 - 这是我为使其正常工作所做的更改:
CREATE OR REPLACE FUNCTION add_transition1() RETURNS TRIGGER AS $$
DECLARE
user_id integer;
BEGIN
BEGIN
user_id := current_setting('myvars.user_id')::integer;
EXCEPTION WHEN OTHERS THEN
user_id := 0;
END;
INSERT INTO transitions1 (user_id, house_id) VALUES (user_id, NEW.id);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION insert_house() RETURNS void as $$
DECLARE
user_id integer;
BEGIN
PERFORM set_config('myvars.user_id', '55', false);
INSERT INTO houses (name) VALUES ('HOUSE PARTY');
END; $$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1905 次 |
最近记录: |