Another one missed, quite a long time ago, too..:

On 4th of November 2016, Kevin Grittner committed patch:

Implement syntax for transition tables in AFTER triggers.
 
 
This is infrastructure for the complete SQL standard feature.  No
support is included at this point for execution nodes or PLs.  The
intent is to add that soon.
 
As this patch leaves things, standard syntax can create tuplestores
to contain old and/or new versions of rows affected by a statement.
References to these tuplestores are in the TriggerData structure.
C triggers can access the tuplestores directly, so they are usable,
but they cannot yet be referenced within a SQL statement.

It looks that we will now have ability to see rows that got changed, in statement-level triggers.

Let's test the idea:

$ create table test (id serial primary key, payload text);
CREATE TABLE
 
$ insert into test (payload) values
    ( 'boor' ),
    ( 'burble' ),
    ( 'debits' ),
    ( 'dwells' ),
    ( 'gloat' ),
    ( 'lurked' ),
    ( 'mambo' ),
    ( 'mush' ),
    ( 'noised' ),
    ( 'peddle' );
INSERT 0 10

Now, let us quickly write test function:

$ create function test_trigger() returns trigger as $$
declare
    temprec record;
begin
    for temprec in select * from v_old_table LOOP
        raise notice 'OLD: %', temprec;
    end loop;
    for temprec in select * from v_new_table LOOP
        raise notice 'NEW: %', temprec;
    end loop;
    return NEW;
end;
$$ language plpgsql;

This function, when used as trigger, will show us all records from “v_old_table" and “v_new_table". Of course, I don't have any such tables, as I only have test:

$ \d
            List of relations
 Schema |    Name     |   Type   | Owner  
--------+-------------+----------+--------
 public | test        | table    | depesz
 public | test_id_seq | sequence | depesz
(2 rows)

So, let's create the trigger:

$ create trigger test_it
    AFTER UPDATE ON test
    REFERENCING NEW TABLE AS v_new_table
        OLD TABLE AS v_old_table
    FOR EACH STATEMENT
    EXECUTE PROCEDURE test_trigger();
CREATE TRIGGER

And now, let's see how it works:

$ update test set payload = 'depesz' where id = 1;
NOTICE:  OLD: (1,boor)
NOTICE:  NEW: (1,depesz)
UPDATE 1

What about larger updates?

$ update test set payload = payload || 'xx' where id > 6 and payload <> 'noised';
NOTICE:  OLD: (7,mambo)
NOTICE:  OLD: (8,mush)
NOTICE:  OLD: (10,peddle)
NOTICE:  NEW: (7,mamboxx)
NOTICE:  NEW: (8,mushxx)
NOTICE:  NEW: (10,peddlexx)
UPDATE 3

I'm not testing for insert or delete triggers, as these seem obvious.

This is (was?) huge. Whole classes of setups will now become easier and faster because you will be able to run summaries or materialization triggers once per statement, and not once per row. In my own history it would be a big win many, many times. Thanks a lot to all involved, and sorry for missing it.

Also, thanks to albertus1 on irc for reminding me about it.

  1. 2 comments

  2. # Corey J Huinker
    Jun 2, 2017

    This is the most underrated feature of PG 10.

  3. Jun 22, 2017

    Completely missed this one… And thanks for elaborating the use case. Whoever’s ever been troubled by performance issues using row triggers, would inherently see it’s value.

Leave a comment