postgres更新触发器执行除实际更新之外的所有其他操作

时间:2013-05-12 17:36:10

标签: postgresql triggers plpgsql

让我们使用测试表:

CREATE TABLE labs.date_test
(
  pkey int NOT NULL,
  val integer,
  date timestamp without time zone,
  CONSTRAINT date_test_pkey PRIMARY KEY (pkey)
);

我有一个如下定义的触发器功能。它是一个将日期插入表中指定列的函数。它的参数是主键,日期字段的名称和要插入的日期:

CREATE OR REPLACE FUNCTION tf_set_date()
  RETURNS trigger AS
$BODY$
DECLARE
    table_name text;
    pkey_col text := TG_ARGV[0];
    date_col text := TG_ARGV[1];
    date_val text := TG_ARGV[2];
BEGIN
    table_name := format('%I.%I', TG_TABLE_SCHEMA, TG_TABLE_NAME);
    IF TG_NARGS != 3 THEN
        RAISE 'Wrong number of args for tf_set_date()'
        USING HINT='Check triggers for table ' || table_name;
    END IF;
    EXECUTE format('UPDATE %s SET %I = %s' ||
            ' WHERE %I = ($1::text::%s).%I', 
            table_name, date_col, date_val,
            pkey_col, table_name, pkey_col )
    USING NEW;
    RAISE NOTICE '%', NEW;
    RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;

实际的触发器定义如下:

CREATE TRIGGER t_set_ready_date
  BEFORE UPDATE OF val
  ON labs.date_test
  FOR EACH ROW
  EXECUTE PROCEDURE tf_set_date('pkey', 'date', 'localtimestamp(0)');

现在说我做:INSERT INTO TABLE date_test(pkey)values(1);`

然后我按如下方式执行更新:

UPDATE labs.date_test SET val = 1 WHERE pkey = 1;

现在按预期插入日期。但val字段仍为NULL。它没有人们期望的1(或者更像我期望的那样)。

我做错了什么?触发器中的RAISE NOTICE显示NEW仍然是我所期望的。 UPDATE触发器中是否允许BEFORE UPDATE?一个commentpostgres triggers似乎表明,如果BEFORE UPDATE触发器中存在UPDATE语句,则原始UPDATE会被覆盖。有人可以帮助我吗?

修改

我正在尝试更新调用触发器的同一个表,以及调用触发器的UPDATE语句要修改的同一行。我正在运行Postgresql 9.2

2 个答案:

答案 0 :(得分:1)

我不确定,但是你的触发器可以执行递归调用 - 它会从UPDATE触发器更新相同的表。这通常是不好的做法,并且编写过于通用的触发器通常不是一个好主意。但我不知道你在做什么,也许你需要它,但你必须确定,所以你可以防止递归。

对于触发器的调试,可以很好地启动和结束函数体调试消息。可能在EXECUTE语句之后使用GET DIAGNOSTICS语句获取有关动态SQL影响的信息

DECLARE 
  _updated_rows int;
  _query text;     
BEGIN
  RAISE NOTICE 'Start trigger function xxx';
  ...
  _query := format('UPDATE ....);
  RAISE NOTICE 'dynamic sql %, %', _query, new;
  EXECUTE _query USING new;
  GET DIAGNOSICS _updated_rows = ROW_COUNT;
  RAISE NOTICE 'Updated rows %', _updated_rows;
  ...

答案 1 :(得分:1)

鉴于所有动态表名称,如果此触发器在调用触发器的同一个表上发出update,则不完全清楚。

如果是这样:那不行。您UPDATE some_table上的BEFORE触发器中无法some_table。或者,更严格地说,你可以,但是如果你更新受语句影响的任何行,那么调用触发器的结果将是不可预测的,因此通常不是一个好主意。

相反,直接更改NEW中的值。遗憾的是,您无法使用动态列名称执行此操作;您只需自定义触发器或使用AFTER触发器在行已更改后执行更新。