我在Postgres 9.2上,我遇到插入分区的触发器问题。它工作了很长一段时间,我运行这个.sql脚本已有近一年的时间,从上周开始它一路成功。今天,我试图运行它,我从触发器中得到错误。
ERROR: query "SELECT NEW.*" returned 12 columns CONTEXT: PL/pgSQL function ft_test_insert_trigger() line 3 at EXECUTE statement.
这是我的基础(父)表的样子:
CREATE TABLE public.test (
id bigint NOT NULL PRIMARY KEY,
name varchar(16) NOT NULL,
geometry geometry NOT NULL,
scale integer NOT NULL,
zone smallint NOT NULL,
band char(1) NOT NULL,
easting integer,
northing integer,
ancestry text,
display_name varchar(16),
created_at timestamp DEFAULT now() NOT NULL,
updated_at timestamp DEFAULT now() NOT NULL,
CONSTRAINT enforce_dims_geometry CHECK (ndims(geometry) = 2),
CONSTRAINT enforce_srid_geometry CHECK (srid(geometry) = 4326),
CONSTRAINT enforce_geotype_geometry CHECK ((geometrytype(geometry) = 'POLYGON'::text) OR (geometry IS NULL))
);
这只是其中一个子表:
CREATE TABLE zone.test_zone_6v (
id bigserial NOT NULL PRIMARY KEY,
name varchar(16) NOT NULL,
geometry geometry NOT NULL,
scale integer NOT NULL,
zone smallint NOT NULL,
band char(1) NOT NULL,
easting integer,
northing integer,
ancestry text,
display_name varchar(16),
created_at timestamp DEFAULT now() NOT NULL,
updated_at timestamp DEFAULT now() NOT NULL,
CONSTRAINT enforce_srid_geometry CHECK (srid(geometry) = 4326),
CONSTRAINT enforce_geotype_geometry CHECK ((geometrytype(geometry) = 'POLYGON'::text) OR (geometry IS NULL)),
CONSTRAINT enforce_dims_geometry CHECK (ndims(geometry) = 2),
CONSTRAINT test_zone_6v_check CHECK ((zone = 6) AND (band = 'V'::bpchar))
)
INHERITS (public.test)
TABLESPACE compressed;
ALTER TABLE zone.test_zone_6v ADD CONSTRAINT uq_test_zone_6v_on_name UNIQUE (name);
这就是触发器:
CREATE OR REPLACE FUNCTION public.ft_test_insert_trigger()
RETURNS trigger
LANGUAGE plpgsql
AS
$body$
BEGIN
EXECUTE format('INSERT INTO zone.test_zone_%s%s VALUES($1)', NEW.zone, NEW.band) USING NEW.*;
RETURN NULL;
END;
$body$
VOLATILE
COST 100;
CREATE TRIGGER trg_insert_test_trigger BEFORE INSERT
ON test FOR EACH ROW
EXECUTE PROCEDURE ft_test_insert_trigger();
我一直在将数据从具有大量记录的文件复制到临时表中以过滤/清除某些数据:
CREATE TEMPORARY TABLE import_test (
name TEXT NOT NULL
,display_name TEXT
,scale INTEGER NOT NULL
,zone INTEGER NOT NULL
,band CHAR NOT NULL
,easting INTEGER NOT NULL
,northing INTEGER NOT NULL
,geometry GEOMETRY NOT NULL
);
当我从临时表中插入表格
时INSERT INTO test (name, geometry, scale, zone, band, easting, northing, display_name)
SELECT name, geometry, scale, zone, band, easting, northing, display_name FROM import_test;
我收到上面引用的错误。
我不确定究竟是什么问题。触发器从未改变过。数据似乎有效。我做了一个
INSERT INTO zone.test_zone_6v (name, geometry, scale, zone, band, easting, northing, display_name)
SELECT name, geometry, scale, zone, band, easting, northing, display_name FROM import_test;
没有问题。在区域模式中,我有大量的子表。我不知道为什么它现在不能正常工作,突然间。
答案 0 :(得分:1)
问题是 ,这是无效的语法(对于包含多个列的表)。USING NEW.*
USING
clause of plpgsql's EXECUTE
statement需要简单的 表达式 。您无法在此位置将行类型分解为 n 值。每个表达式都必须返回一个值。
改为整行:
CREATE OR REPLACE FUNCTION public.ft_test_insert_trigger()
RETURNS trigger AS
$func$
BEGIN
EXECUTE format('INSERT INTO zone.%I SELECT ($1).*'
, 'test_zone_' || NEW.zone || NEW.band)
USING NEW;
RETURN NULL;
END
$func$ LANGUAGE plpgsql;
在此期间,我修复了潜在的 SQL注入漏洞
smallint
(zone
)和char(1)
(band
)看起来并不危险。但即使是一个角色也可以杀死你的陈述。想想'
或&#39 ;;'。如果您以后忘记了洞并将列的类型更改为text
或varchar
,则您可以对任何恶作剧持开放态度。
从不只是将用户输入连接到代码中
详细说明:
如果您的旧触发功能"一直成功"并且它永远不会改变",那么它就是
PL / pgSQL只是表面上验证SQL命令的语法。您的代码将通过测试。它甚至适用于具有单列的表。该例外不会出现,直到用更宽的表格执行。
相关: