Postgres触发问题“ERROR:query”SELECT NEW。*“返回12列”

时间:2015-02-26 22:21:40

标签: postgresql triggers plpgsql dynamic-sql

我在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;

没有问题。在区域模式中,我有大量的子表。我不知道为什么它现在不能正常工作,突然间。

1 个答案:

答案 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注入漏洞 smallintzone)和char(1)band)看起来并不危险。但即使是一个角色也可以杀死你的陈述。想想'或&#39 ;;'。如果您以后忘记了洞并将列的类型更改为textvarchar,则您可以对任何恶作剧持开放态度。
从不只是将用户输入连接到代码中 详细说明:

如果您的旧触发功能"一直成功"并且它永远不会改变",那么它就是

  • 仅用于包含单列
  • 的表格
  • 从未真正执行过。
  • (或也许某些旧版本默默地占用了第一列而丢弃了其余部分?但是你没有提到版本升级,我用9.1进行了测试但是它失败了,我没有&# 39; t回想旧版本的问题。)

PL / pgSQL只是表面上验证SQL命令的语法。您的代码将通过测试。它甚至适用于具有单列的表。该例外不会出现,直到用更宽的表格执行。

相关: