为子表插入创建触发器会返回令人困惑的错误

时间:2014-12-05 17:05:53

标签: postgresql triggers plpgsql dynamic-sql postgresql-9.3

我正在尝试编写一个触发器函数,它将值输入到单独的子表中,但是我收到的错误是我以前没见过的。

以下是设置示例:

-- create initial table
CREATE TABLE public.testlog(
    id serial not null,
    col1 integer,
    col2 integer,
    col3 integer,
    name text
);

-- create child table
CREATE TABLE public.testlog_a (primary key(id)) INHERITS(public.testlog);

-- make trigger function for insert
CREATE OR REPLACE FUNCTION public.test_log() RETURNS trigger AS
$$
DECLARE
    qry text;
BEGIN
    qry := 'INSERT INTO public.testlog_' || NEW.name || ' SELECT ($1).*';

    EXECUTE qry USING NEW.*;

    RETURN OLD;
END
$$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;

-- add function to table
CREATE TRIGGER test_log_sorter BEFORE INSERT
ON public.testlog FOR EACH ROW
EXECUTE PROCEDURE public.test_log();

和查询:

INSERT INTO public.testlog (col1, col2, col3, name) values (1, 2, 3, 'a');

错误消息:

[Err] ERROR:  query "SELECT NEW.*" returned 5 columns
CONTEXT:  PL/pgSQL function test_log() line 7 at EXECUTE statement

5列正是我要它返回的内容,所以显然有些东西我不理解,但错误信息似乎毫无意义。

有人可以解释我为什么会这样做吗?

3 个答案:

答案 0 :(得分:2)

您的解决方案修复了行类型NEW变量的传递。但是,您的代码中存在一个偷偷摸摸的SQL注入漏洞,SECURITY DEFINER函数中的特别危险。用户输入必须永远不会转换为未转义的SQL代码。

像这样消毒:

CREATE OR REPLACE FUNCTION trg_test_log()
  RETURNS trigger AS
$$
BEGIN
    EXECUTE 'INSERT INTO public.' || quote_ident('testlog_' || NEW.name)
         || ' SELECT ($1).*'
    USING NEW;

    RETURN NULL;
END
$$
LANGUAGE plpgsql SECURITY DEFINER;

此外:

  • OLD未在INSERT触发器中定义。
  • 您不需要变量。在plpgsql中,赋值相对较贵。

答案 1 :(得分:1)

EXECUTE qry USING NEW.*传递NEW.*作为查询的参数。由于NEW.*会返回五列,因此查询应包含$1$2$3$4$5,以便绑定五列

您期待一个参数($1),其中包含五列。我相信如果你把这条线改为

EXECUTE qry USING NEW;

它会像你期望的那样工作。

答案 2 :(得分:1)

关于Robert M. Lefkowitz'回答,答案很简单:NEW而不是NEW.*

CREATE OR REPLACE FUNCTION public.test_log() RETURNS trigger AS
$$
DECLARE
    qry text;
BEGIN
    qry := 'INSERT INTO public.testlog_' || NEW.name || ' SELECT ($1).*';

    EXECUTE qry USING NEW;

    RETURN OLD;
END
$$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 100;

感谢。