Skip to content

Commit 8c48375

Browse files
committed
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.
1 parent 69d590f commit 8c48375

File tree

16 files changed

+580
-43
lines changed

16 files changed

+580
-43
lines changed

doc/src/sgml/catalogs.sgml

+16
Original file line numberDiff line numberDiff line change
@@ -6231,6 +6231,22 @@
62316231
representation) for the trigger's <literal>WHEN</> condition, or null
62326232
if none</entry>
62336233
</row>
6234+
6235+
<row>
6236+
<entry><structfield>tgoldtable</structfield></entry>
6237+
<entry><type>name</type></entry>
6238+
<entry></entry>
6239+
<entry><literal>REFERENCING</> clause name for <literal>OLD TABLE</>,
6240+
or null if none</entry>
6241+
</row>
6242+
6243+
<row>
6244+
<entry><structfield>tgnewtable</structfield></entry>
6245+
<entry><type>name</type></entry>
6246+
<entry></entry>
6247+
<entry><literal>REFERENCING</> clause name for <literal>NEW TABLE</>,
6248+
or null if none</entry>
6249+
</row>
62346250
</tbody>
62356251
</tgroup>
62366252
</table>

doc/src/sgml/ref/create_trigger.sgml

+76-18
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="PARAMETER">name</replaceable>
2525
ON <replaceable class="PARAMETER">table_name</replaceable>
2626
[ FROM <replaceable class="parameter">referenced_table_name</replaceable> ]
2727
[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
28+
[ REFERENCING { { OLD | NEW } TABLE [ AS ] <replaceable class="PARAMETER">transition_relation_name</replaceable> } [ ... ] ]
2829
[ FOR [ EACH ] { ROW | STATEMENT } ]
2930
[ WHEN ( <replaceable class="parameter">condition</replaceable> ) ]
3031
EXECUTE PROCEDURE <replaceable class="PARAMETER">function_name</replaceable> ( <replaceable class="PARAMETER">arguments</replaceable> )
@@ -177,6 +178,15 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="PARAMETER">name</replaceable>
177178
when the constraints they implement are violated.
178179
</para>
179180

181+
<para>
182+
The <literal>REFERENCING</> option is only allowed for an <literal>AFTER</>
183+
trigger which is not a constraint trigger. <literal>OLD TABLE</> may only
184+
be specified once, and only on a trigger which can fire on
185+
<literal>UPDATE</> or <literal>DELETE</>. <literal>NEW TABLE</> may only
186+
be specified once, and only on a trigger which can fire on
187+
<literal>UPDATE</> or <literal>INSERT</>.
188+
</para>
189+
180190
<para>
181191
<command>SELECT</command> does not modify any rows so you cannot
182192
create <command>SELECT</command> triggers. Rules and views are more
@@ -281,6 +291,40 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
281291
</listitem>
282292
</varlistentry>
283293

294+
<varlistentry>
295+
<term><literal>REFERENCING</literal></term>
296+
<listitem>
297+
<para>
298+
This immediately preceeds the declaration of one or two relations which
299+
can be used to read the before and/or after images of all rows directly
300+
affected by the triggering statement. An <literal>AFTER EACH ROW</>
301+
trigger is allowed to use both these transition relation names and the
302+
row names (<literal>OLD</> and <literal>NEW</>) which reference each
303+
individual row for which the trigger fires.
304+
</para>
305+
</listitem>
306+
</varlistentry>
307+
308+
<varlistentry>
309+
<term><literal>OLD TABLE</literal></term>
310+
<term><literal>NEW TABLE</literal></term>
311+
<listitem>
312+
<para>
313+
This specifies whether the named relation contains the before or after
314+
images for rows affected by the statement which fired the trigger.
315+
</para>
316+
</listitem>
317+
</varlistentry>
318+
319+
<varlistentry>
320+
<term><replaceable class="PARAMETER">transition_relation_name</replaceable></term>
321+
<listitem>
322+
<para>
323+
The (unqualified) name to be used within the trigger for this relation.
324+
</para>
325+
</listitem>
326+
</varlistentry>
327+
284328
<varlistentry>
285329
<term><literal>FOR EACH ROW</literal></term>
286330
<term><literal>FOR EACH STATEMENT</literal></term>
@@ -474,6 +518,30 @@ CREATE TRIGGER view_insert
474518
FOR EACH ROW
475519
EXECUTE PROCEDURE view_insert_row();
476520
</programlisting>
521+
522+
Execute the function <function>check_transfer_balances_to_zero</> for each
523+
statement to confirm that the <literal>transfer</> rows offset to a net of
524+
zero:
525+
526+
<programlisting>
527+
CREATE TRIGGER transfer_insert
528+
AFTER INSERT ON transfer
529+
FOR EACH STATEMENT
530+
REFERENCING NEW TABLE AS inserted
531+
EXECUTE PROCEDURE check_transfer_balances_to_zero();
532+
</programlisting>
533+
534+
Execute the function <function>check_matching_pairs</> for each row to
535+
confirm that changes are made to matching pairs at the same time (by the
536+
same statement):
537+
538+
<programlisting>
539+
CREATE TRIGGER paired_items_update
540+
AFTER UPDATE ON paired_items
541+
FOR EACH ROW
542+
REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab
543+
EXECUTE PROCEDURE check_matching_pairs();
544+
</programlisting>
477545
</para>
478546

479547
<para>
@@ -502,24 +570,14 @@ CREATE TRIGGER view_insert
502570
<itemizedlist>
503571
<listitem>
504572
<para>
505-
SQL allows you to define aliases for the <quote>old</quote>
506-
and <quote>new</quote> rows or tables for use in the definition
507-
of the triggered action (e.g., <literal>CREATE TRIGGER ... ON
508-
tablename REFERENCING OLD ROW AS somename NEW ROW AS othername
509-
...</literal>). Since <productname>PostgreSQL</productname>
510-
allows trigger procedures to be written in any number of
511-
user-defined languages, access to the data is handled in a
512-
language-specific way.
513-
</para>
514-
</listitem>
515-
516-
<listitem>
517-
<para>
518-
<productname>PostgreSQL</productname> does not allow the old and new
519-
tables to be referenced in statement-level triggers, i.e., the tables
520-
that contain all the old and/or new rows, which are referred to by the
521-
<literal>OLD TABLE</literal> and <literal>NEW TABLE</literal> clauses in
522-
the <acronym>SQL</> standard.
573+
While transition tables for <literal>AFTER</> triggers are specified
574+
using the <literal>REFERENCING</> clause in the standard way, the row
575+
variables used in <literal>FOR EACH ROW</> triggers may not be
576+
specified in <literal>REFERENCING</> clause. They are available in a
577+
manner which is dependent on the language in which the trigger function
578+
is written. Some languages effectively behave as though there is a
579+
<literal>REFERENCING</> clause containing <literal>OLD ROW AS OLD NEW
580+
ROW AS NEW</>.
523581
</para>
524582
</listitem>
525583

src/backend/commands/tablecmds.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -7430,7 +7430,7 @@ validateForeignKeyConstraint(char *conname,
74307430
trig.tgconstraint = constraintOid;
74317431
trig.tgdeferrable = FALSE;
74327432
trig.tginitdeferred = FALSE;
7433-
/* we needn't fill in tgargs or tgqual */
7433+
/* we needn't fill in remaining fields */
74347434

74357435
/*
74367436
* See if we can do it with a single LEFT JOIN query. A FALSE result
@@ -7514,6 +7514,7 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
75147514
}
75157515

75167516
fk_trigger->columns = NIL;
7517+
fk_trigger->transitionRels = NIL;
75177518
fk_trigger->whenClause = NULL;
75187519
fk_trigger->isconstraint = true;
75197520
fk_trigger->deferrable = fkconstraint->deferrable;
@@ -7557,6 +7558,7 @@ createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
75577558
fk_trigger->timing = TRIGGER_TYPE_AFTER;
75587559
fk_trigger->events = TRIGGER_TYPE_DELETE;
75597560
fk_trigger->columns = NIL;
7561+
fk_trigger->transitionRels = NIL;
75607562
fk_trigger->whenClause = NULL;
75617563
fk_trigger->isconstraint = true;
75627564
fk_trigger->constrrel = NULL;
@@ -7611,6 +7613,7 @@ createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
76117613
fk_trigger->timing = TRIGGER_TYPE_AFTER;
76127614
fk_trigger->events = TRIGGER_TYPE_UPDATE;
76137615
fk_trigger->columns = NIL;
7616+
fk_trigger->transitionRels = NIL;
76147617
fk_trigger->whenClause = NULL;
76157618
fk_trigger->isconstraint = true;
76167619
fk_trigger->constrrel = NULL;

0 commit comments

Comments
 (0)