我不确定如何实现以下内容:
CREATE OR REPLACE FUNCTION fnJobQueueBEFORE() RETURNS trigger AS $$
DECLARE
shadowname varchar := TG_TABLE_NAME || 'shadow';
BEGIN
INSERT INTO shadowname VALUES(OLD.*);
RETURN OLD;
END;
$$
LANGUAGE plpgsql;
即。将值插入到具有动态生成名称的表中 执行上面的代码会产生:
ERROR: relation "shadowname" does not exist
LINE 1: INSERT INTO shadowname VALUES(OLD.*)
似乎建议变量不作为表名扩展/允许。我在Postgres手册中没有找到这方面的内容。
我已经尝试了EXECUTE
,如此:
EXECUTE 'INSERT INTO ' || quote_ident(shadowname) || ' VALUES ' || OLD.*;
但没有运气:
ERROR: syntax error at or near ","
LINE 1: INSERT INTO personenshadow VALUES (1,sven,,,)
RECORD
类型似乎丢失了:OLD.*
似乎被转换为字符串并得到重新分析,导致各种类型问题(例如NULL
值)。
有什么想法吗?
答案 0 :(得分:50)
format()
有一种内置的方法来转义标识符。比以前更简单:
CREATE OR REPLACE FUNCTION foo_before()
RETURNS trigger AS
$func$
BEGIN
EXECUTE format('INSERT INTO %I.%I SELECT $1.*'
, TG_TABLE_SCHEMA, TG_TABLE_NAME || 'shadow')
USING OLD;
RETURN OLD;
END
$func$ LANGUAGE plpgsql;
SQL Fiddle.
也适用于VALUES
表达式。
quote_ident()
在必要时引用标识符并防范SQL injection。search_path
setting,裸表名可能会解析为另一个模式中的另一个同名表。EXECUTE
用于动态DDL语句。USING
条款安全传递值。RETURN OLD;
需要触发器功能中的BEFORE DELETE
。 Details in the manual here. 您在几乎成功的版本中收到错误消息,因为OLD
在EXECUTE
内不可见。如果您想要像您尝试的那样连接分解行的各个值,则必须使用quote_literal()
准备每个列的文本表示,以保证有效的语法。您还必须事先知道列名称来处理它们或查询系统目录 - 这违背了您的想法,即拥有一个简单的动态触发函数......
我的解决方案避免了所有这些并发症。也简化了一下。
format()
尚不可用,所以:
CREATE OR REPLACE FUNCTION foo_before()
RETURNS trigger AS
$func$
BEGIN
EXECUTE 'INSERT INTO ' || quote_ident(TG_TABLE_SCHEMA)
|| '.' || quote_ident(TG_TABLE_NAME || 'shadow')
|| ' SELECT $1.*'
USING OLD;
RETURN OLD;
END
$func$ LANGUAGE plpgsql;
相关:
答案 1 :(得分:1)
我偶然发现了这一点,因为我正在寻找动态INSTEAD OF DELETE
触发器。感谢您提出问题和答案,我将发布Postgres 9.3的解决方案。
CREATE OR REPLACE FUNCTION set_deleted_instead_of_delete()
RETURNS TRIGGER AS $$
BEGIN
EXECUTE format('UPDATE %I set deleted = now() WHERE id = $1.id', TG_TABLE_NAME)
USING OLD;
RETURN NULL;
END;
$$ language plpgsql;