3

让我们使用一个测试表:

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 TABLEdate_test(pkey) values(1);`

然后我执行如下更新:

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

现在日期按预期插入。但val场依旧NULL。它不像1人们所期望的那样(或者更确切地说不像我所期望的那样)。

我究竟做错了什么?触发器中的 RAISE NOTICE 表明 NEW 仍然是我所期望的。触发器中不允许UPDATE使用 s吗?关于postgres 触发器BEFORE UPDATE的一条评论似乎表明,如果 BEFORE UPDATE 触发器中有 UPDATE 语句,则原始 UPDATE 将被覆盖。有人可以帮我吗?

编辑

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

4

2 回答 2

2

我不确定,但您的触发器可以进行递归调用 - 它会从 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;
  ...
于 2013-05-12T18:32:02.457 回答
2

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

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

相反,NEW直接更改值。不幸的是,您不能使用动态列名来执行此操作;您只需要自定义触发器或AFTER在行已更改后使用触发器进行更新。

于 2013-05-13T01:22:12.673 回答