在触发器函数中插入dynamic表名
我不知道如何实现如下内容:
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;
即将值插入到具有dynamic生成名称的表中。
执行上面的代码产生:
ERROR: relation "shadowname" does not exist LINE 1: INSERT INTO shadowname VALUES(OLD.*)
它似乎build议variables不扩展/允许作为表名称。 我在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
types似乎丢失了: OLD.*
似乎被转换为一个string并得到重新编译,导致各种types的问题(例如NULL
值)。
有任何想法吗?
PostgreSQL 9.1或更高版本
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小提琴。
也可以使用VALUES
expression式。
重点
- 在必要时使用format()或
quote_ident()
引用标识符,并防范SQL注入 。
这是必要的 ,即使有你自己的表名! - 模式 – 限定表名。 根据当前的
search_path
设置 ,裸表名可能会另外parsing为另一个不同模式中同名的另一个表。 - dynamicDDL语句使用
EXECUTE
。 -
USING
子句安全地传递值 。 - 请参阅有关在plpgsql中执行dynamic命令的精细手册。
- 请注意,
RETURN OLD;
在触发器function是需要触发BEFORE DELETE
。 手册中的细节在这里。
您在几乎成功的版本中收到错误消息 ,因为OLD
在EXECUTE
不可见 。 如果您想像尝试一样连接分解行的各个值,则必须使用quote_literal()
来准备每个列的文本表示forms以保证有效的语法。 你还必须事先知道列名来处理它们或查询系统目录 – 这违背了你有一个简单的,dynamic的触发函数的想法…
我的解决scheme避免了所有这些复杂性 也简化了一下。
PostgreSQL 9.0或更早版本
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;
有关:
- 如何在PostgreSQL 8.2中dynamic使用TG_TABLE_NAME?
我只是偶然发现,因为我正在寻找一个dynamic的INSTEAD OF DELETE
触发器。 作为感谢您的问题和答案,我会张贴Postgres 9.3的解决scheme。
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;