views:

26

answers:

1

hi

i try to create a plpgsql trigger for postgresql 8.3 which automatically partitions a table on before insert by the id column

if the destination table doesnt exist it will be created, and the insert goes there

so i created the insert statement with the new table name like this

exec_insert := 'INSERT INTO '||TG_TABLE_SCHEMA||'.'||TG_RELNAME||'_'||destinationid||' VALUES('||NEW.*||')';
EXECUTE exec_insert;

resulting in the error:

ERROR:  NEW used in query that is not in a rule

i have 2 questions:

  1. is it even possible to use NEW in EXECUTE or is there some mistake in the statement?
  2. if its just not possible, anyone knows how to get the values out of NEW so i can use them in the statement? the only thing which comes to my mind is using information_schema to retrieve column names for the main table and then try to access NEW's values dynamically - which i also dont know how :(

thx

A: 

This setup works fine over here (version 8.4 and 9.0):

CREATE TABLE customer
(
  id bigserial NOT NULL,
  name text,
  datecreated timestamp with time zone DEFAULT now(),
  CONSTRAINT customer_pkey PRIMARY KEY (id) USING INDEX TABLESPACE pg_default
);

CREATE OR REPLACE FUNCTION test_trigger()
  RETURNS trigger AS
$BODY$
declare
    query   text;
begin
    query := 'INSERT INTO customer_' || substring(NEW.name,1,1) || ' VALUES(' || NEW.id || ','''|| NEW.name||''')';
    EXECUTE query;

    RETURN NULL;

    EXCEPTION
        WHEN undefined_table THEN
            query := 'CREATE TABLE "customer_' || substring(NEW.name,1,1) || 
            '"( CONSTRAINT "customer_' || substring(NEW.name,1,1) ||'_name_check" CHECK (lower(substring(name, 1, 1)) = ''' || substring(NEW.name,1,1) || '''::text)
            ) INHERITS (customer); CREATE INDEX "i_customer_' || substring(NEW.name,1,1) ||'_name" ON customer_' || substring(NEW.name,1,1) ||' USING btree(name);';
            EXECUTE query;

            query := 'INSERT INTO customer_' || substring(NEW.name,1,1) || ' VALUES(' || NEW.id || ','''|| NEW.name||''')';
            EXECUTE query;

            query := 'ANALYZE "customer_' || substring(NEW.name,1,1) || '"';
            EXECUTE query;

            RETURN NULL;

end;
$BODY$
  LANGUAGE plpgsql VOLATILE STRICT
  COST 100;

CREATE TRIGGER t_customer
  BEFORE INSERT OR UPDATE OR DELETE
  ON customer
  FOR EACH ROW
  EXECUTE PROCEDURE test_trigger();

CREATE TABLE customer
(
  id bigserial NOT NULL,
  name text,
  datecreated timestamp with time zone DEFAULT now(),
  CONSTRAINT customer_pkey PRIMARY KEY (id) USING INDEX TABLESPACE pg_default
);

CREATE OR REPLACE FUNCTION test_trigger()
  RETURNS trigger AS
$BODY$
declare
    query   text;
begin
    query := 'INSERT INTO customer_' || substring(NEW.name,1,1) || ' VALUES(' || NEW.id || ','''|| NEW.name||''')';
    EXECUTE query;

    RETURN NULL;

    EXCEPTION
        WHEN undefined_table THEN
            query := 'CREATE TABLE "customer_' || substring(NEW.name,1,1) || 
            '"( CONSTRAINT "customer_' || substring(NEW.name,1,1) ||'_name_check" CHECK (lower(substring(name, 1, 1)) = ''' || substring(NEW.name,1,1) || '''::text)
            ) INHERITS (customer); CREATE INDEX "i_customer_' || substring(NEW.name,1,1) ||'_name" ON customer_' || substring(NEW.name,1,1) ||' USING btree(name);';
            EXECUTE query;

            query := 'INSERT INTO customer_' || substring(NEW.name,1,1) || ' VALUES(' || NEW.id || ','''|| NEW.name||''')';
            EXECUTE query;

            query := 'ANALYZE "customer_' || substring(NEW.name,1,1) || '"';
            EXECUTE query;

            RETURN NULL;

end;
$BODY$
  LANGUAGE plpgsql VOLATILE STRICT
  COST 100;

CREATE TRIGGER t_customer
  BEFORE INSERT OR UPDATE OR DELETE
  ON customer
  FOR EACH ROW
  EXECUTE PROCEDURE test_trigger();

INSERT INTO customer(name) VALUES ('john'), ('peter');
Frank Heikens
same errorERROR: NEW used in query that is not in a ruleLINE 1: INSERT INTO set.test_123 VALUES (NEW.*)
John Doe
See the new case, works fine over here.
Frank Heikens
ure using the names of the columns hardcoded which works here too, well i tried a more general approach where the trigger could be used on any table independent of the column names/count which currently seems impossible with plpgsql as stated here http://archives.postgresql.org/pgsql-sql/2004-06/msg00198.php
John Doe
Nothing is impossible in plpgsql, you just need a little extra creativity ;) Use pg_attribute to get all columns and types to create new tables, works fine.
Frank Heikens