Reputation: 211
I'm on Postgres 9.2 and I'm having issues with a trigger inserting into partitions. It was working for quite awhile, where i was running this .sql script for almost a year, where it was successful all the way to as of last week. Today, I tried to run it, and I was getting an error from the trigger.
ERROR: query "SELECT NEW.*" returned 12 columns CONTEXT: PL/pgSQL function ft_test_insert_trigger() line 3 at EXECUTE statement.
Here's what my base(parent) table looks like:
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))
);
And this one just one of its child tables:
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);
And this is the trigger:
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();
I have been copying data from a file w/ x amount of records into a temp table to filter / clean out some data:
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
);
When I do an insert into the table from the temporary table
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;
I get the error quoted above.
I'm not sure what the issue is exactly. The trigger never changed. The data seems valid. I did a
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;
with no issues. In zone schema, I have a large number of children table for it. I can't figure out why it's not working now, all of a sudden.
Upvotes: 1
Views: 3346
Reputation: 657202
The problem is , which is invalid syntax (for tables with more than one column).USING NEW.*
The USING
clause of plpgsql's EXECUTE
statement expects simple expressions. You cannot decompose a row type into n values in this position. Each expression must return a single value.
Pass the whole row instead:
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;
While being at it, I fixed a potential SQL injection hole.
A smallint
(zone
) and a char(1)
(band
) don't look dangerous. But even a single character can kill your statement. Think of '
or ';'. And if you later forget about the hole and change the type of the column to text
or varchar
you are wide open to any mischief.
Never just concatenate user input into code.
Details:
If your old trigger function "was successful all the way" and it "never changed", then it was either
PL/pgSQL only superficially verifies the syntax of SQL commands. Your code would pass the test. It even works for tables with a single column. The exception won't show up until executed with a wider table.
Related:
Upvotes: 1