cha*_*ni2 5 postgresql trigger dynamic-sql plpgsql postgresql-9.5
我得到了要在 ( BEFORE UPDATE) 触发器中设置的列的名称,我想将其设置为该OLD值并忽略传入的任何内容。我尝试了以下操作:
CREATE OR REPLACE FUNCTION prevent_column_update() RETURNS TRIGGER AS $$
DECLARE
col TEXT := TG_ARGV[0];
BEGIN
EXECUTE format('SELECT ($1).%I INTO ($2).%I', col, col) USING OLD, NEW;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
并使用如下:
CREATE TRIGGER request_protect_date_price_value
BEFORE UPDATE OF date_price ON requests
FOR EACH ROW EXECUTE PROCEDURE prevent_column_update('date_price');
Run Code Online (Sandbox Code Playgroud)
但是当更新它失败时:
ERROR: syntax error at or near "("
LINE 1: SELECT ($1).date_price INTO ($2).date_price
^
QUERY: SELECT ($1).date_price INTO ($2).date_price
Run Code Online (Sandbox Code Playgroud)
错误是该INTO子句不是SQL 命令的一部分。它是 plpgsql 命令的一部分EXECUTE。
动态字段名目前是不可能的,无论是在 SQL 还是 PL/pgSQL 中。但是有一些方法可以解决这个限制:
您可以(ab)使用内置的JSON 函数json_populate_record()来实现类似的技巧,但目前没有记录在案,可能会在 Postgres 的未来版本中删除。
确定的方法是使用附加hstore模块的记录#=操作符。每个数据库安装一次模块
CREATE EXTENSION IF NOT EXISTS hstore;
Run Code Online (Sandbox Code Playgroud)
然后:
CREATE OR REPLACE FUNCTION prevent_column_update()
RETURNS TRIGGER AS
$func$
DECLARE
_col text := quote_ident(TG_ARGV[0]);
_old_val text;
_new_val text;
BEGIN
EXECUTE format('SELECT $1.%1$I, $2.%1$I', _col)
INTO _old_val, _new_val -- part of plpgsql command
USING OLD, NEW;
IF _old_val IS DISTINCT FROM _new_val THEN -- only if it actually changed
NEW := NEW #= hstore(_col, _old_val); -- hstore operator #=
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
请注意,hstore使用文本字符串进行操作。其他数据类型的值被强制转换为text和返回,这适用于我能想到的任何数据类型。但它可能会导致某些类型的问题(例如浮点数的舍入错误)。
这个触发器定义使案例完整:
CREATE TRIGGER tbl_upbef_nope
BEFORE UPDATE ON tbl -- your table here
FOR EACH ROW
EXECUTE PROCEDURE prevent_column_update('date_price');
Run Code Online (Sandbox Code Playgroud)
在此示例中,列名区分大小写,因为它是作为字符串而不是标识符传递的。
只需为每个表编写一个没有动态 SQL 的新普通触发器函数。更少的麻烦,更好的性能。字节码重复的项目符号:
CREATE OR REPLACE FUNCTION prevent_column_update()
RETURNS TRIGGER AS
$func$
BEGIN
NEW.date_price:= OLD.date_price; -- unconditionally
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
扳机:
CREATE TRIGGER tbl_upbef_nope
BEFORE UPDATE OF date_price ON tbl -- your column and table here
FOR EACH ROW EXECUTE PROCEDURE prevent_column_update(); -- no param
Run Code Online (Sandbox Code Playgroud)
我将检查移到触发器本身,因此除非更新列,否则该函数甚至不会执行。请注意,这可以通过同一个表上的其他触发器来规避,因为(引用手册):
仅当至少列出的列之一被提及作为
UPDATE命令的目标时,触发器才会触发。
因此,如果您不能排除此类额外的触发器,请UPDATE无条件地触发触发器并检查触发器函数中的更改。
| 归档时间: |
|
| 查看次数: |
6392 次 |
| 最近记录: |