用触发功能替换删除:如何返回受影响的行数?

时间:2018-03-28 18:53:32

标签: postgresql view triggers plpgsql

标准delete from foo_history where id = 123;返回文字:

DELETE 1

我创建了一个触发程序来替换删除触发器,而不是删除,设置一个存档标志:

CREATE FUNCTION archive()
  RETURNS trigger AS $$
    DECLARE
      command text := '" SET timeEnd = current_timestamp WHERE id = $1';
    BEGIN
      EXECUTE 'UPDATE "' || TG_TABLE_NAME || command USING OLD.id;
      RETURN NULL;
    END;
  $$ LANGUAGE plpgsql;

然后为名为foo_history的{​​{1}}表创建了一个视图:

foo

并在视图中添加了一个触发器:

CREATE VIEW foo AS
SELECT id, timeStart from foo_history WHERE timeEnd IS NULL;

换句话说,在删除CREATE TRIGGER foo_archive INSTEAD OF DELETE ON foo FOR EACH ROW EXECUTE PROCEDURE archive(); 记录时,请改为设置foo列 - 这样,timeEnd视图仅显示未设置foo的记录。

这很有效:

timeEnd

确实设置了delete from foo where id = 123列。但是,这个输出是:

timeEnd

我真的想要它说:

DELETE 0

作为SQL新手和Postgres新手我不知道如何更改存档功能来执行此操作。

我确实尝试将其更改为:

DELETE 1

但是我得到一个错误,我需要返回一个复合材料(无论这意味着什么)。

2 个答案:

答案 0 :(得分:2)

您已经发现RETURN OLD触发器需要 INSTEAD OF 进行DELETE操作才能使行计数在返回的命令标记中。手册:

  

行级INSTEAD OF触发器应返回NULL以指示   它没有修改视图底层的任何数据   表,或者它应该返回传入的视图行(NEW   INSERTUPDATE操作的行,或OLD DELETE行   操作)。非空返回值用于表示触发器   在视图中执行必要的数据修改。 这会   导致受命令影响的行数增加。

大胆强调我的。

但是你当前的触发器中还有一个偷偷摸摸的 SQL注入错误TG_TABLE_NAME是一个裸露的,不带引号的名称。标识符必须被视为用户输入 - 并且必须在必要时引用。在动态SQL语句中正确引用表名(或者您可能会受到影响)。一种方法是使用format()

CREATE OR REPLACE FUNCTION archive()
  RETURNS trigger AS
$func$
BEGIN
   EXECUTE format('UPDATE %I SET time_end = current_timestamp WHERE id = $1', TG_TABLE_NAME)
   USING OLD.id;
   RETURN OLD;
END
$func$  LANGUAGE plpgsql;

相关:

我还使用time_end代替timeEnd,因为我的常设建议是避免在Postgres中使用CaMeL案例标识符。相关:

答案 1 :(得分:0)

当然,我发布的第二篇文章,我有一个看似有用的想法..

而不是RETURN NULL,请使用RETURN OLD

抱歉......