我正在尝试编写一个触发器函数,它将值输入到单独的子表中,但是我收到的错误是我以前没见过的。
以下是设置示例:
-- 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列正是我要它返回的内容,所以显然有些东西我不理解,但错误信息似乎毫无意义。
有人可以解释我为什么会这样做吗?
答案 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
触发器中定义。答案 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;
感谢。