Triggers, functions and column names: a poser - Mailing list pgsql-general

From Grant Table
Subject Triggers, functions and column names: a poser
Date
Msg-id 200111170135.CAA26861@post.webmailer.de
Whole thread Raw
Responses Re: Triggers, functions and column names: a poser
List pgsql-general
here's a nice trigger problem for the weekend ;-)

I have a database (7.1.3) with a number of tables, to some of which
I wish to apply some form of logging / auditing.
(The reasons for this are manifold and related to the
business logic of the organisation which owns the database).

Specifically, for certain tables on INSERT, UPDATE or DELETE I
want to write the following to a seperate logging table:
the name of the table (relation); the action performed;
the primary key of the row affected; and a timestamp.

Rather than create a seperate RULE for each action on
each table (pain to maintain) I would like to create a
PL/PgSQL function to be called by triggers for the relevant tables.


This is the function:

    CREATE FUNCTION update_log()
      RETURNS opaque
      AS '
        DECLARE
          qid INTEGER;
        BEGIN
          IF TG_OP = ''DELETE''
            THEN qid := OLD.p_id;
            ELSE qid := NEW.p_id;
          END IF;
          INSERT INTO change_log
            (row_operation, table_name, id, created)
            VALUES(TG_OP, TG_RELNAME, qid, now());
          RETURN new;
      END;'
    LANGUAGE 'plpgsql';


This is the table 'change_log':

    CREATE TABLE change_log (
      id            integer,
      table_name    varchar(32),
      row_operation varchar(7),
      created       timestamp,
      CHECK (row_operation in ('INSERT','UPDATE','DELETE'))
    )


For a (simplified) table like this:

    CREATE TABLE product (
      p_id          SERIAL PRIMARY KEY,
      p_name        varchar(64)
    )

I would create the following trigger:

    CREATE TRIGGER trigger_product_log
      AFTER INSERT OR UPDATE OR DELETE
      ON product
      FOR EACH ROW
      EXECUTE PROCEDURE update_log();


So far so good. Unfortunately each table's primary
key is labelled differently, i.e. _not_ 'id'. I would
therefore like to find a way to determine the value
written into change_log.id without having to hard-wire
the row name into the function.

(I could of course rebuild the database with all
 relevant primary keys  renamed as 'id', and
 rewrite the overlying application, but I think I'd
 rather open up that large writhing can of worms in
 the corner now past its open-by-date ;-)

I'd guess there are three approaches to doing this:

1) provide the id from the trigger, e.g. something
   like
       CREATE TRIGGER trigger_product_log
         (...)
         EXECUTE PROCEDURE update_log(p_id);

   so that update_log finds the id in TG_ARGV[0];

2) provide the column name from the trigger, e.g.
       CREATE TRIGGER trigger_product_log
         (...)
         EXECUTE PROCEDURE update_log('p_id')

   so that update_log can do something like this:
        (...)
        DECLARE
          qid INTEGER;
        BEGIN
          qid := NEW.TG_ARGV[0]
        (...)

3) use some 'magic' function which fetches the primary
   key of the row referred to by 'OLD' or 'NEW'...

Alas I haven't found any documentation or anything in
the various archives on how to do this, and random
"guess-a-syntax" attempts have also proved remarkably unsuccessful.

Is what I am trying to do possible, and if so how; or am
I barking up the wrong line of enquiry entirely?


Any advice gratefully accepted

yrs

Grant Table
grant.table@easypublish.de

"Table Design by Name and By Nature"

pgsql-general by date:

Previous
From: Shane Wegner
Date:
Subject: Unsigned types
Next
From: Tom Lane
Date:
Subject: Re: Delete Performance