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

标签 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 列正是我想要它返回的内容,所以显然有些东西我不理解,但错误消息似乎没有意义。

谁能解释一下为什么我会得到这个?

最佳答案

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

像这样 sanitizer :

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 中的赋值相对昂贵。

关于postgresql - 为子表插入创建触发器会返回令人困惑的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27321091/

相关文章:

sql - 重构 PostgreSQL 查询以返回百分比而不是计数

postgresql - 如何在 Postgres 9.5 中替换多个特殊字符

sql - 返回由 plpgsql 函数插入的记录

regex - 是否可以使用正则表达式对 PL/PGSQL 中的文本进行标记化?

java - 类型为“没有时区的时间戳”的数据库中的UTC时间作为响应显示在UTC中

c - 为什么 PostgreSQL 数组访问在 C 中比在 PL/pgSQL 中快得多?

sqlite - SQLite:表约束和触发器

collections - Mongodb 自动写入上限集合

c# - 单击任何应用程序中的文本框时显示虚拟键盘 C#

sql - 在 PL/pgSQL 中执行此查询的更好方法