Re: Dynamic pgplsql triggers - Mailing list pgsql-general

From Worky Workerson
Subject Re: Dynamic pgplsql triggers
Date
Msg-id ce4072df0608010702t74341632nbf839d9d1afc4fc9@mail.gmail.com
Whole thread Raw
In response to Dynamic pgplsql triggers  ("Worky Workerson" <worky.workerson@gmail.com>)
List pgsql-general
I have scanned the archives and found the following message from 2004
dealing with v7.4, however it doesn't solve all my problems:

http://archives.postgresql.org/pgsql-sql/2004-07/msg00208.php

Here is a brief example.  I have a table db with a merge trigger given
below, shamelessly stolen from Example 36-1 in the docs.  When an
insert occurs, the number_seen is updated if the id/content are the
same, otherwise the new record is inserted.

-- Note that id is not unique
CREATE TABLE db (
    id INTEGER,
    content BYTEA,
    number_seen INTEGER
);

CREATE OR REPLACE FUNCTION merge_db() RETURNS TRIGGER AS $$
  BEGIN
    UPDATE db SET number_seen = number_seen + NEW.number_seen
      WHERE id = NEW.id AND content = NEW.content;
    IF FOUND THEN
      --Update row
      RETURN NULL;
    END IF;

    RETURN NEW;
  END;
$$ LANGUAGE PLPGSQL;

CREATE TRIGGER db_merge_db BEFORE INSERT ON db FOR EACH ROW EXECUTE
PROCEDURE merge_db();

This works like a charm.  My 'db' table is getting rather large,
however, and I started to partition it based on the non-unique column
'id'

CREATE TABLE db_1 (
  CONSTRAINT partition_id_1 CHECK (id =1);
) INHERITS db;

CREATE TABLE db_2 .... db_100

However now I am at a loss as to how to create a single trigger
function that I can use to trigger all of the partition tables.
Basically, how do I set the db name to be dynamic?  I tried the naive
approach of just replacing 'db' with TG_RELNAME, i.e.:

    UPDATE TG_RELNAME SET number_seen = number_seen + NEW.number_seen
      WHERE id = NEW.id AND content = NEW.content;

but of course this gives me an error about $1, which is what I
figured, given that the table name can't be a parameter (right?):
'ERROR:  syntax error at or near "$1" at character 9'

So then I tried to make the statement dynamic, i.e.:

    EXECUTE 'UPDATE ' || TG_RELNAME || '
      SET number_seen = number_seen + NEW.number_seen
      WHERE id = NEW.id AND content = NEW.content;

but this gives the error: 'ERROR:  NEW used in query that is not in a
rule'.  This seems a little confusing as NEW should be visible to
trigger functions, but I assume that it has something to do with the
EXECUTE and how the planner couldn't pre-plan the SQL.

So I plod on, and try and make all the NEW args dynamic, i.e.:

    EXECUTE 'UPDATE ' || TG_RELNAME || '
      SET number_seen = number_seen + ' || NEW.number_seen || '
      WHERE id = ' || NEW.id || ' AND content = ' || NEW.content;

However now I get the error: 'ERROR:  operator does not exist:  bytea
|| ip4'.  I think I understand what is going on ... that bytea doesn't
have a text representation, right?

So I'm not quite sure where to go from here.  How do I make the UPDATE
statement in the trigger function operate against the table on which
the trigger was fired, while at the same time passing in the values in
NEW?

Any help would be greatly appreciated.

Thanks!



CREATE OR REPLACE FUNCTION merge_db() RETURNS TRIGGER AS $$
  BEGIN
    UPDATE db SET number_seen = number_seen + NEW.number_seen
      WHERE id = NEW.id AND content = NEW.content;
    IF FOUND THEN
      --Update row
      RETURN NULL;
    END IF;

    RETURN NEW;
  END;
$$ LANGUAGE PLPGSQL;


On 7/31/06, Worky Workerson <worky.workerson@gmail.com> wrote:
> I'm trying to trigger a whole bunch of partitions at once (initial DB
> setup) using the same plpgsql trigger.  The trigger is basically the
> merge trigger in the docs (i.e. UPDATE IF NOT FOUND RETURN NEW ...).
>
> I need to use the TG_RELNAME variable within the "UPDATE" in the
> trigger so that I can use the same function to trigger all of the
> partitions (correct?), the problem is that I can't quite figure out
> how.  I figure that I will have to use EXECUTE on a string that I
> build up, right?  The problem that I'm having with this approach is
> that some of the columns of NEW don't have a text conversion, and I'm
> getting an error whenever the trigger fires.  Is there a way around
> this and/or a better way to trigger a bunch of partitions with the
> same function?
>
> Thanks!
> -Worky
>

pgsql-general by date:

Previous
From: "Merlin Moncure"
Date:
Subject: Re: Dynamic pgplsql triggers
Next
From: "Worky Workerson"
Date:
Subject: Re: Dynamic pgplsql triggers