pg.ddx.io  pgsql-sql@postgresql.org mailing list archive  
help / color / mirror / Atom feed
Advice for generalizing trigger functions
7+ messages / 4 participants
[nested] [flat]

* Advice for generalizing trigger functions
@ 2007-12-26 00:20 Richard Broersma Jr <rabroersma@yahoo.com>
  2007-12-26 18:11 ` Re: Advice for generalizing trigger functions chester c young <chestercyoung@yahoo.com>
  2007-12-27 17:51 ` Re: Advice for generalizing trigger functions Erik Jones <erik@myemma.com>
  2007-12-29 02:09 ` Re: Advice for generalizing trigger functions Marcin Stępnicki <mstepnicki@gmail.com>
  0 siblings, 3 replies; 7+ messages in thread

From: Richard Broersma Jr @ 2007-12-26 00:20 UTC (permalink / raw)
  To: pgsql-sql

I've created quite a few functions that log modifications to various history tables. (the history table has the same name as the base table but is prefixed by the 'History.' schema.) The only difference between functions I can find is the table name.

Is there any way to generalize these myriad of functions into one?


Below is a sample of a typical logging trigger function.

Regards,
Richard Broersma Jr.


CREATE OR REPLACE FUNCTION "project"."log_managers_ops"()
RETURNS trigger AS
$BODY$
BEGIN


     IF TG_OP IN ( 'UPDATE', 'DELETE' ) THEN
     
          UPDATE History.Managers AS M
             SET endts = now()
           WHERE M.manager_id = OLD.manager_id
             AND now() BETWEEN M.startts AND M.endts;
             
     end IF;
     
     
     IF TG_OP IN ( 'UPDATE', 'INSERT' ) THEN
     
     INSERT INTO History.Managers
          VALUES ( now()::timestamptz, 'INFINITY'::timestamptz, NEW.*);

          RETURN NEW;
          
     END IF;

     RETURN OLD;

END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER;




^ permalink  raw  reply  [nested|flat] 7+ messages in thread

* Re: Advice for generalizing trigger functions
  2007-12-26 00:20 Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
@ 2007-12-26 18:11 ` chester c young <chestercyoung@yahoo.com>
  2007-12-26 20:20   ` Re: Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
  2 siblings, 1 reply; 7+ messages in thread

From: chester c young @ 2007-12-26 18:11 UTC (permalink / raw)
  To: Richard Broersma Jr <rabroersma@yahoo.com>; +Cc: pgsql-sql

--- Richard Broersma Jr <rabroersma@yahoo.com> wrote:

> I've created quite a few functions that log modifications to various
> history tables. (the history table has the same name as the base
> table but is prefixed by the 'History.' schema.) The only difference
> between functions I can find is the table name.

the problem is that prepared code is referenced by oid, not name.  so
any structural references need to by dynamic.

what I do for change log is to have one change log table with table_id
and column_id attributes that refer by to my internal meta_table and
meta_column tables.  this always works and is in the end, I have found,
a bit more flexible, allowing you to search for changed columns, for
example.

but I still generate the change triggers.  in this case from in my
meta_table and meta_column tables I note which table/columns I want
changes tracked.  those can be changed at any time, but the change log
triggers need to be recompiled.



      ____________________________________________________________________________________
Never miss a thing.  Make Yahoo your home page. 
http://www.yahoo.com/r/hs



^ permalink  raw  reply  [nested|flat] 7+ messages in thread

* Re: Advice for generalizing trigger functions
  2007-12-26 00:20 Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
  2007-12-26 18:11 ` Re: Advice for generalizing trigger functions chester c young <chestercyoung@yahoo.com>
@ 2007-12-26 20:20   ` Richard Broersma Jr <rabroersma@yahoo.com>
  0 siblings, 0 replies; 7+ messages in thread

From: Richard Broersma Jr @ 2007-12-26 20:20 UTC (permalink / raw)
  To: chester c young <chestercyoung@yahoo.com>; +Cc: pgsql-sql

--- On Wed, 12/26/07, chester c young <chestercyoung@yahoo.com> wrote:

> what I do for change log is to have one change log table with table_id
> and column_id attributes that refer by to my internal meta_table and
> meta_column tables.  this always works and is in the end, I have found,
> a bit more flexible, allowing you to search for changed columns, for
> example.

I agree that EAV history tables are far more flexible than creating separate history tables for each base table.  Especially since this design choice does not require modifications when tables are created, altered, or dropped.  However, I was looking to utilize functionality that an EAV history table can't provide.

For example, I replace joins to the Project.Managers table with joins to the History.Managers table and with a timestamp of interest to reproduce query results of the database's state that existed in "times passed".

If I didn't need to regularly provide this functionality, I would certainly use an EAV history table.

Regards,
Richard Broersma Jr.



^ permalink  raw  reply  [nested|flat] 7+ messages in thread

* Re: Advice for generalizing trigger functions
  2007-12-26 00:20 Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
@ 2007-12-27 17:51 ` Erik Jones <erik@myemma.com>
  2007-12-27 18:03   ` Re: Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
  2 siblings, 1 reply; 7+ messages in thread

From: Erik Jones @ 2007-12-27 17:51 UTC (permalink / raw)
  To: Richard Broersma Jr <rabroersma@yahoo.com>; +Cc: pgsql-sql


On Dec 25, 2007, at 6:20 PM, Richard Broersma Jr wrote:

> I've created quite a few functions that log modifications to  
> various history tables. (the history table has the same name as the  
> base table but is prefixed by the 'History.' schema.) The only  
> difference between functions I can find is the table name.
>
> Is there any way to generalize these myriad of functions into one?
>
>
> Below is a sample of a typical logging trigger function.
>
> Regards,
> Richard Broersma Jr.
>
>
> CREATE OR REPLACE FUNCTION "project"."log_managers_ops"()
> RETURNS trigger AS
> $BODY$
> BEGIN
>
>
>      IF TG_OP IN ( 'UPDATE', 'DELETE' ) THEN
>
>           UPDATE History.Managers AS M
>              SET endts = now()
>            WHERE M.manager_id = OLD.manager_id
>              AND now() BETWEEN M.startts AND M.endts;
>
>      end IF;
>
>
>      IF TG_OP IN ( 'UPDATE', 'INSERT' ) THEN
>
>      INSERT INTO History.Managers
>           VALUES ( now()::timestamptz, 'INFINITY'::timestamptz,  
> NEW.*);
>
>           RETURN NEW;
>
>      END IF;
>
>      RETURN OLD;
>
> END;
> $BODY$
> LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER;

TG_TABLE_NAME will have the name of the table the trigger was fired  
on.  With that and using EXECUTE for your INSERT statements, you'll  
probably be set.

Erik Jones

Software Developer | Emma®
erik@myemma.com
800.595.4401 or 615.292.5888
615.292.0777 (fax)

Emma helps organizations everywhere communicate & market in style.
Visit us online at http://www.myemma.com





^ permalink  raw  reply  [nested|flat] 7+ messages in thread

* Re: Advice for generalizing trigger functions
  2007-12-26 00:20 Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
  2007-12-27 17:51 ` Re: Advice for generalizing trigger functions Erik Jones <erik@myemma.com>
@ 2007-12-27 18:03   ` Richard Broersma Jr <rabroersma@yahoo.com>
  2007-12-27 18:22     ` Re: Advice for generalizing trigger functions Erik Jones <erik@myemma.com>
  0 siblings, 1 reply; 7+ messages in thread

From: Richard Broersma Jr @ 2007-12-27 18:03 UTC (permalink / raw)
  To: Erik Jones <erik@myemma.com>; +Cc: pgsql-sql

--- On Thu, 12/27/07, Erik Jones <erik@myemma.com> wrote:

> TG_TABLE_NAME will have the name of the table the trigger
> was fired  on.  With that and using EXECUTE for your INSERT
> statements, you'll  probably be set.

True the table name is the easy part, but how do I determine the Primary Key components of the table that will also be needed in the SQL string that will be executed?

Regards,
Richard Broersma Jr.



^ permalink  raw  reply  [nested|flat] 7+ messages in thread

* Re: Advice for generalizing trigger functions
  2007-12-26 00:20 Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
  2007-12-27 17:51 ` Re: Advice for generalizing trigger functions Erik Jones <erik@myemma.com>
  2007-12-27 18:03   ` Re: Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
@ 2007-12-27 18:22     ` Erik Jones <erik@myemma.com>
  0 siblings, 0 replies; 7+ messages in thread

From: Erik Jones @ 2007-12-27 18:22 UTC (permalink / raw)
  To: Richard Broersma Jr <rabroersma@yahoo.com>; +Cc: pgsql-sql

On Dec 27, 2007, at 12:03 PM, Richard Broersma Jr wrote:

> --- On Thu, 12/27/07, Erik Jones <erik@myemma.com> wrote:
>
>> TG_TABLE_NAME will have the name of the table the trigger
>> was fired  on.  With that and using EXECUTE for your INSERT
>> statements, you'll  probably be set.
>
> True the table name is the easy part, but how do I determine the  
> Primary Key components of the table that will also be needed in the  
> SQL string that will be executed?

That depends on how generalized you want this trigger function to  
be.  If you have a set number of tables you can branch on the table  
name to determine the id column.   That's brittle and would require  
updating the the function every time you want to use if for a new  
table type but if there's a limited number of tables it may work for  
you.  Another way to go would be to the table name to join across  
pg_class, pg_attribute, and pg_constraint.  Take a look at the table  
layouts for those in the manual and it should be pretty clear how to  
do that.

Erik Jones

Software Developer | Emma®
erik@myemma.com
800.595.4401 or 615.292.5888
615.292.0777 (fax)

Emma helps organizations everywhere communicate & market in style.
Visit us online at http://www.myemma.com





^ permalink  raw  reply  [nested|flat] 7+ messages in thread

* Re: Advice for generalizing trigger functions
  2007-12-26 00:20 Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
@ 2007-12-29 02:09 ` Marcin Stępnicki <mstepnicki@gmail.com>
  2 siblings, 0 replies; 7+ messages in thread

From: Marcin Stępnicki @ 2007-12-29 02:09 UTC (permalink / raw)
  To: pgsql-sql


Dnia 25-12-2007, Wt o godzinie 16:20 -0800, Richard Broersma Jr pisze:
> I've created quite a few functions that log modifications to various history tables. 
> (the history table has the same name as the base table but is prefixed by the 'History.' schema.)
>  The only difference between functions I can find is the table name.
> 
> Is there any way to generalize these myriad of functions into one?
> 

Maybe just try http://pgfoundry.org/projects/audittrail2/ ?

Or there's something my friend wrote some time ago (I use audittrail
now, but I think that this function works with reasonably recent
PostgreSQL releases). Sorry for Polish comments, I've just taken it out
from old repository.


CREATE OR REPLACE FUNCTION create_history_table(text, text)
  RETURNS text AS
$BODY$
DECLARE
    tabela ALIAS FOR $1;
    query TEXT;
    fields RECORD;
    grupa ALIAS FOR $2;
BEGIN
    query := '';

-- poczatek definicji zapytania tworzacego historie
    query := 'CREATE TABLE "H' || tabela || '"(\r\n';

-- petelka wyciagajaca pola tabeli do ktorej tworzona jest historia
    FOR fields IN 
        SELECT a.attname AS name,
               format_type(a.atttypid, a.atttypmod) AS type
          FROM pg_attribute a 
         WHERE a.attrelid = ( SELECT oid FROM pg_class WHERE relname = tabela ) 
           AND a.attnum > 0
        LOOP
        
        query := query || fields.name || ' ' || fields.type || ',\r\n';

    END LOOP; 

-- doklejenie pol dotyczacych historii
    query := query || 'log_usename text default current_user, '
                   || 'log_time timestamp default now(), '
                   || 'log_event text default '''' '
                   || ');\r\n';

-- ustawienie uprawnien do zapisu i odczytu z tabeli historii
    query := query || 'GRANT SELECT, INSERT ON "H' || tabela || '" TO GROUP "' || grupa || '";\r\n';

--    EXECUTE query;
--    query := '';
-- definicja funkcji dla triggera historii
    query := query || 'CREATE FUNCTION "H' || tabela || '"() RETURNS trigger AS\r\n'
                   || '$$\r\n'
                   || 'begin\r\n'
                   || 'if ( tg_op = ''INSERT'' ) then\r\n'
                   || '    INSERT INTO "H' || tabela || '" (\r\n';

    FOR fields IN 
        SELECT a.attname AS name
--               format_type(a.atttypid, a.atttypmod) AS type
          FROM pg_attribute a 
         WHERE a.attrelid = ( SELECT oid FROM pg_class WHERE relname = tabela ) 
           AND a.attnum > 0
        LOOP
        
        query := query || '    ' || fields.name || ', \r\n';

    END LOOP; 

    query := query || '    log_event ) VALUES (\r\n';

    FOR fields IN 
        SELECT a.attname AS name
--               format_type(a.atttypid, a.atttypmod) AS type
          FROM pg_attribute a 
         WHERE a.attrelid = ( SELECT oid FROM pg_class WHERE relname = tabela ) 
           AND a.attnum > 0
        LOOP
        
        query := query || '    NEW.' || fields.name || ', \r\n';

    END LOOP; 

    query := query || '    ''I'');\r\n';

    query := query || 'end if;\r\n'
                   || 'if tg_op = ''UPDATE'' then\r\n'
                   || '    if OLD.id != NEW.id then\r\n'
                   || '        UPDATE "H' || tabela || '" SET id = NEW.id WHERE id = OLD.id;\r\n'
                   || '    end if;\r\n'
                   || '    INSERT INTO "H' || tabela || '"(\r\n';

    FOR fields IN 
        SELECT a.attname AS name
--               format_type(a.atttypid, a.atttypmod) AS type
          FROM pg_attribute a 
         WHERE a.attrelid = ( SELECT oid FROM pg_class WHERE relname = tabela ) 
           AND a.attnum > 0
        LOOP
        
        query := query || '    ' || fields.name || ', \r\n';

    END LOOP; 

    query := query || '    log_event ) VALUES (\r\n';

    FOR fields IN 
        SELECT a.attname AS name
--               format_type(a.atttypid, a.atttypmod) AS type
          FROM pg_attribute a 
         WHERE a.attrelid = ( SELECT oid FROM pg_class WHERE relname = tabela ) 
           AND a.attnum > 0
        LOOP
        
        query := query || '    NEW.' || fields.name || ', \r\n';

    END LOOP; 

    query := query || '    ''U'');\r\n';

    query := query || 'end if;\r\n'
                   || 'if tg_op = ''DELETE'' then\r\n'
                   || '    INSERT INTO "H' || tabela || '"(';

    FOR fields IN 
        SELECT a.attname AS name
--               format_type(a.atttypid, a.atttypmod) AS type
          FROM pg_attribute a 
         WHERE a.attrelid = ( SELECT oid FROM pg_class WHERE relname = tabela ) 
           AND a.attnum > 0
        LOOP
        
        query := query || '    ' || fields.name || ', \r\n';

    END LOOP; 

    query := query || '    log_event ) VALUES (\r\n';

    FOR fields IN 
        SELECT a.attname AS name
--               format_type(a.atttypid, a.atttypmod) AS type
          FROM pg_attribute a 
         WHERE a.attrelid = ( SELECT oid FROM pg_class WHERE relname = tabela ) 
           AND a.attnum > 0
        LOOP
        
        query := query || '    OLD.' || fields.name || ', \r\n';

    END LOOP; 

    query := query || '    ''D'');\r\n';

    query := query || 'end if;\r\n'
                   || 'return NEW;\r\n'
                   || 'end;\r\n'
                   || '$$ LANGUAGE ''PLPGSQL'';';

    query := query || 'CREATE TRIGGER "H' || tabela || '" AFTER INSERT OR UPDATE OR DELETE ON "' || tabela || '" FOR EACH ROW EXECUTE PROCEDURE "H' || tabela || '"()';

    EXECUTE query;
    RETURN query;
END;$BODY$
  LANGUAGE 'plpgsql';





^ permalink  raw  reply  [nested|flat] 7+ messages in thread


end of thread, other threads:[~2007-12-29 02:09 UTC | newest]

Thread overview: 7+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2007-12-26 00:20 Advice for generalizing trigger functions Richard Broersma Jr <rabroersma@yahoo.com>
2007-12-26 18:11 ` chester c young <chestercyoung@yahoo.com>
2007-12-26 20:20   ` Richard Broersma Jr <rabroersma@yahoo.com>
2007-12-27 17:51 ` Erik Jones <erik@myemma.com>
2007-12-27 18:03   ` Richard Broersma Jr <rabroersma@yahoo.com>
2007-12-27 18:22     ` Erik Jones <erik@myemma.com>
2007-12-29 02:09 ` Marcin Stępnicki <mstepnicki@gmail.com>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox