我正在尝试用SQL8.2编写一个触发器函数,它将动态地使用TG_TABLE_NAME来生成和执行PostgreSQL语句。我可以找到更高版本的PostgreSQL的各种示例,但由于一些要求,我被困在8.2版本上。下面是我的函数,它可以工作,但几乎不是动态的:
CREATE OR REPLACE FUNCTION cdc_TABLENAME_function() RETURNS trigger AS $cdc_function$
DECLARE
op cdc_operation_enum;
BEGIN
op = TG_OP;
IF (TG_WHEN = 'BEFORE') THEN
IF (TG_OP = 'UPDATE') THEN
op = 'UPDATE_BEFORE';
END IF;
INSERT INTO cdc_test VALUES (DEFAULT,DEFAULT,op,DEFAULT,DEFAULT,OLD.*);
ELSE
IF (TG_OP = 'UPDATE') THEN
op = 'UPDATE_AFTER';
END IF;
INSERT INTO cdc_test VALUES (DEFAULT,DEFAULT,op,DEFAULT,DEFAULT,NEW.*);
END IF;
IF (TG_OP = 'DELETE') THEN
RETURN OLD;
ELSE
RETURN NEW;
END IF;
END;
按照目前的编写方式,我必须为每个表编写一个单独的触发器函数。我想使用TG_TABLE_NAME来动态构建我的INSERT语句,并且只使用'cdc_‘作为它的前缀,因为所有的表都遵循相同的命名约定。然后,我可以让每个表调用的每个触发器只有一个函数。
发布于 2011-10-01 09:25:29
几年前我也在找同样的东西。一个触发器函数就可以控制所有的触发器!我在usenet列表上询问,尝试了各种方法,但都无济于事。在这件事上的共识是这是不可能做到的。PostgreSQL 8.3或更早版本的缺点。
Since PostgreSQL 您只需:
EXECUTE 'INSERT INTO ' || TG_RELID::regclass::text || ' SELECT ($1).*'
USING NEW;
使用pg 8.2时,您会遇到一个问题:
NEW
/ OLD
的列。您需要知道在编写触发器时function.NEW
/ OLD
在EXECUTE
.EXECUTE .. USING
not born中不可见的列名。然而,这里有一个诀窍。
系统中的每个表名都可以作为同名的复合类型。因此,您可以创建一个以NEW
/ OLD
为参数的函数并执行该函数。您可以在每个触发器事件上动态创建和销毁该函数:
触发器功能:
CREATE OR REPLACE FUNCTION trg_cdc()
RETURNS trigger AS
$func$
DECLARE
op text := TG_OP || '_' || TG_WHEN;
tbl text := quote_ident(TG_TABLE_SCHEMA) || '.'
|| quote_ident(TG_TABLE_NAME);
cdc_tbl text := quote_ident(TG_TABLE_SCHEMA) || '.'
|| quote_ident('cdc_' || TG_TABLE_NAME);
BEGIN
EXECUTE 'CREATE FUNCTION f_cdc(n ' || tbl || ', op text)
RETURNS void AS $x$ BEGIN
INSERT INTO ' || cdc_tbl || ' SELECT op, (n).*;
END $x$ LANGUAGE plpgsql';
CASE TG_OP
WHEN 'INSERT', 'UPDATE' THEN
PERFORM f_cdc(NEW, op);
WHEN 'DELETE' THEN
PERFORM f_cdc(OLD, op);
ELSE
RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP;
END CASE;
EXECUTE 'DROP FUNCTION f_cdc(' || tbl || ', text)';
IF TG_OP = 'DELETE' THEN
RETURN OLD;
ELSE
RETURN NEW;
END IF;
END
$func$ LANGUAGE plpgsql;
触发器:
CREATE TRIGGER cdc
BEFORE INSERT OR UPDATE OR DELETE ON my_tbl
FOR EACH ROW EXECUTE PROCEDURE trg_cdc();
表名必须像用户输入一样对待。使用quote_ident()
防御SQL注入。
然而,这样您就可以为每个单独的触发器事件创建和删除一个函数。这是一个相当大的开销,我不会这么做。你将不得不用吸尘器清理一些目录表格。
中间立场
PostgreSQL支持function overloading。因此,每个具有相同基本名称(但参数类型不同)的表可以共存一个函数。您可以采取中间做法,在创建触发器的同时为每个表创建一次f_cdc(..)
,从而大大减少噪音。这是每个表的一个小函数。您必须观察表定义的更改,但是表不应该经常更改。从触发器函数中去掉CREATE
和DROP FUNCTION
,得到一个小巧、快速、优雅的触发器。
我可以想象我自己在第8.2页中这样做。除了我再也看不到自己在pg 8.2中做任何事情。它有reached end of life in December 2011。也许你终究还是可以升级的。
发布于 2011-10-03 07:26:44
几年前我也问过类似的问题。
看一下这个问题,看看它是否给了你任何有用的想法:
Inserting NEW.* from a generic trigger using EXECUTE in PL/pgsql
https://stackoverflow.com/questions/7519044
复制相似问题