在触发器函数中插入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,,,) 

RECORDtypes似乎丢失了: 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小提琴。
也可以使用VALUESexpression式。

重点

  • 在必要时使用format()或quote_ident()引用标识符,并防范SQL注入 。
    这是必要的 ,即使有你自己的表名!
  • 模式 – 限定表名。 根据当前的search_path设置 ,裸表名可能会另外parsing为另一个不同模式中同名的另一个表。
  • dynamicDDL语句使用EXECUTE
  • USING子句安全地传递
  • 请参阅有关在plpgsql中执行dynamic命令的精细手册。
  • 请注意, RETURN OLD; 在触发器function是需要触发BEFORE DELETE 。 手册中的细节在这里。

您在几乎成功的版本中收到错误消息 ,因为OLDEXECUTE 不可见 。 如果您想像尝试一样连接分解行的各个值,则必须使用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;