Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7fc0f06

Browse files
committed
Add a WHEN clause to CREATE TRIGGER, allowing a boolean expression to be
checked to determine whether the trigger should be fired.For BEFORE triggers this is mostly a matter of spec compliance; but for AFTERtriggers it can provide a noticeable performance improvement, since queuing ofa deferred trigger event and re-fetching of the row(s) at end of statement canbe short-circuited if the trigger does not need to be fired.Takahiro Itagaki, reviewed by KaiGai Kohei.
1 parent201a45c commit7fc0f06

File tree

27 files changed

+783
-100
lines changed

27 files changed

+783
-100
lines changed

‎doc/src/sgml/catalogs.sgml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.210 2009/10/14 22:14:21 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.211 2009/11/20 20:38:09 tgl Exp $ -->
22
<!--
33
Documentation of the system catalogs, directed toward PostgreSQL developers
44
-->
@@ -4756,6 +4756,15 @@
47564756
<entry></entry>
47574757
<entry>Argument strings to pass to trigger, each NULL-terminated</entry>
47584758
</row>
4759+
4760+
<row>
4761+
<entry><structfield>tgqual</structfield></entry>
4762+
<entry><type>text</type></entry>
4763+
<entry></entry>
4764+
<entry>Expression tree (in <function>nodeToString()</function>
4765+
representation) for the trigger's <literal>WHEN</> condition, or NULL
4766+
if none</entry>
4767+
</row>
47594768
</tbody>
47604769
</tgroup>
47614770
</table>

‎doc/src/sgml/ref/create_constraint.sgml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/create_constraint.sgml,v 1.20 2009/09/19 10:23:26 petere Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/create_constraint.sgml,v 1.21 2009/11/20 20:38:09 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -27,6 +27,7 @@ CREATE CONSTRAINT TRIGGER <replaceable class="parameter">name</replaceable>
2727
[ FROM <replaceable class="parameter">referenced_table_name</replaceable> ]
2828
{ NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } }
2929
FOR EACH ROW
30+
[ WHEN ( <replaceable class="parameter">condition</replaceable> ) ]
3031
EXECUTE PROCEDURE <replaceable class="parameter">function_name</replaceable> ( <replaceable class="parameter">arguments</replaceable> )
3132
</synopsis>
3233
</refsynopsisdiv>
@@ -109,6 +110,22 @@ CREATE CONSTRAINT TRIGGER <replaceable class="parameter">name</replaceable>
109110
</listitem>
110111
</varlistentry>
111112

113+
<varlistentry>
114+
<term><replaceable class="parameter">condition</replaceable></term>
115+
<listitem>
116+
<para>
117+
A Boolean expression that determines whether the trigger function
118+
will actually be executed. This acts the same as in <xref
119+
linkend="SQL-CREATETRIGGER" endterm="SQL-CREATETRIGGER-TITLE">.
120+
Note in particular that evaluation of the <literal>WHEN</>
121+
condition is not deferred, but occurs immediately after the row
122+
update operation is performed. If the condition does not evaluate
123+
to <literal>true</> then the trigger is not queued for deferred
124+
execution.
125+
</para>
126+
</listitem>
127+
</varlistentry>
128+
112129
<varlistentry>
113130
<term><replaceable class="PARAMETER">function_name</replaceable></term>
114131
<listitem>

‎doc/src/sgml/ref/create_trigger.sgml

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/create_trigger.sgml,v 1.51 2009/10/14 22:14:21 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/create_trigger.sgml,v 1.52 2009/11/20 20:38:09 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -23,6 +23,7 @@ PostgreSQL documentation
2323
<synopsis>
2424
CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTER } { <replaceable class="PARAMETER">event</replaceable> [ OR ... ] }
2525
ON <replaceable class="PARAMETER">table</replaceable> [ FOR [ EACH ] { ROW | STATEMENT } ]
26+
[ WHEN ( <replaceable class="parameter">condition</replaceable> ) ]
2627
EXECUTE PROCEDURE <replaceable class="PARAMETER">function_name</replaceable> ( <replaceable class="PARAMETER">arguments</replaceable> )
2728
</synopsis>
2829
</refsynopsisdiv>
@@ -72,6 +73,16 @@ CREATE TRIGGER <replaceable class="PARAMETER">name</replaceable> { BEFORE | AFTE
7273
<literal>FOR EACH STATEMENT</literal>.
7374
</para>
7475

76+
<para>
77+
Also, a trigger definition can specify a boolean <literal>WHEN</>
78+
condition, which will be tested to see whether the trigger should
79+
be fired. In row-level triggers the <literal>WHEN</> condition can
80+
examine the old and/or new values of columns of the row. Statement-level
81+
triggers can also have <literal>WHEN</> conditions, although the feature
82+
is not so useful for them since the condition cannot refer to any values
83+
in the table.
84+
</para>
85+
7586
<para>
7687
If multiple triggers of the same kind are defined for the same event,
7788
they will be fired in alphabetical order by name.
@@ -159,6 +170,31 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
159170
</listitem>
160171
</varlistentry>
161172

173+
<varlistentry>
174+
<term><replaceable class="parameter">condition</replaceable></term>
175+
<listitem>
176+
<para>
177+
A Boolean expression that determines whether the trigger function
178+
will actually be executed. If <literal>WHEN</> is specified, the
179+
function will only be called if the <replaceable
180+
class="parameter">condition</replaceable> returns <literal>true</>.
181+
In <literal>FOR EACH ROW</literal> triggers, the <literal>WHEN</>
182+
condition can refer to columns of the old and/or new row values
183+
by writing <literal>OLD.<replaceable
184+
class="parameter">column_name</replaceable></literal> or
185+
<literal>NEW.<replaceable
186+
class="parameter">column_name</replaceable></literal> respectively.
187+
Of course, <literal>INSERT</> triggers cannot refer to <literal>OLD</>
188+
and <literal>DELETE</> triggers cannot refer to <literal>NEW</>.
189+
</para>
190+
191+
<para>
192+
Currently, <literal>WHEN</literal> expressions cannot contain
193+
subqueries.
194+
</para>
195+
</listitem>
196+
</varlistentry>
197+
162198
<varlistentry>
163199
<term><replaceable class="parameter">function_name</replaceable></term>
164200
<listitem>
@@ -213,6 +249,29 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
213249
value did not change.
214250
</para>
215251

252+
<para>
253+
In a <literal>BEFORE</> trigger, the <literal>WHEN</> condition is
254+
evaluated just before the function is or would be executed, so using
255+
<literal>WHEN</> is not materially different from testing the same
256+
condition at the beginning of the trigger function. Note in particular
257+
that the <literal>NEW</> row seen by the condition is the current value,
258+
as possibly modified by earlier triggers. Also, a <literal>BEFORE</>
259+
trigger's <literal>WHEN</> condition is not allowed to examine the
260+
system columns of the <literal>NEW</> row (such as <literal>oid</>),
261+
because those won't have been set yet.
262+
</para>
263+
264+
<para>
265+
In an <literal>AFTER</> trigger, the <literal>WHEN</> condition is
266+
evaluated just after the row update occurs, and it determines whether an
267+
event is queued to fire the trigger at the end of statement. So when an
268+
<literal>AFTER</> trigger's <literal>WHEN</> condition does not return
269+
true, it is not necessary to queue an event nor to re-fetch the row at end
270+
of statement. This can result in significant speedups in statements that
271+
modify many rows, if the trigger only needs to be fired for a few of the
272+
rows.
273+
</para>
274+
216275
<para>
217276
In <productname>PostgreSQL</productname> versions before 7.3, it was
218277
necessary to declare trigger functions as returning the placeholder
@@ -223,11 +282,56 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
223282
</para>
224283
</refsect1>
225284

226-
<refsect1 id="R1-SQL-CREATETRIGGER-2">
285+
<refsect1 id="SQL-CREATETRIGGER-examples">
227286
<title>Examples</title>
228287

229288
<para>
230-
<xref linkend="trigger-example"> contains a complete example.
289+
Execute the function <function>check_account_update</> whenever
290+
a row of the table <literal>accounts</> is about to be updated:
291+
292+
<programlisting>
293+
CREATE TRIGGER check_update
294+
BEFORE UPDATE ON accounts
295+
FOR EACH ROW
296+
EXECUTE PROCEDURE check_account_update();
297+
</programlisting>
298+
299+
The same, but only execute the function if column <literal>balance</>
300+
is specified as a target in the <command>UPDATE</> command:
301+
302+
<programlisting>
303+
CREATE TRIGGER check_update
304+
BEFORE UPDATE OF balance ON accounts
305+
FOR EACH ROW
306+
EXECUTE PROCEDURE check_account_update();
307+
</programlisting>
308+
309+
This form only executes the function if column <literal>balance</>
310+
has in fact changed value:
311+
312+
<programlisting>
313+
CREATE TRIGGER check_update
314+
BEFORE UPDATE ON accounts
315+
FOR EACH ROW
316+
WHEN (OLD.balance IS DISTINCT FROM NEW.balance)
317+
EXECUTE PROCEDURE check_account_update();
318+
</programlisting>
319+
320+
Call a function to log updates of <literal>accounts</>, but only if
321+
something changed:
322+
323+
<programlisting>
324+
CREATE TRIGGER log_update
325+
AFTER UPDATE ON accounts
326+
FOR EACH ROW
327+
WHEN (OLD.* IS DISTINCT FROM NEW.*)
328+
EXECUTE PROCEDURE log_account_update();
329+
</programlisting>
330+
</para>
331+
332+
<para>
333+
<xref linkend="trigger-example"> contains a complete example of a trigger
334+
function written in C.
231335
</para>
232336
</refsect1>
233337

@@ -258,7 +362,7 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
258362
<productname>PostgreSQL</productname> only allows the execution
259363
of a user-defined function for the triggered action. The standard
260364
allows the execution of a number of other SQL commands, such as
261-
<command>CREATE TABLE</command> as the triggered action. This
365+
<command>CREATE TABLE</command>, as the triggered action. This
262366
limitation is not hard to work around by creating a user-defined
263367
function that executes the desired commands.
264368
</para>

‎doc/src/sgml/trigger.sgml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.59 2009/10/14 22:14:21 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.60 2009/11/20 20:38:09 tgl Exp $ -->
22

33
<chapter id="triggers">
44
<title>Triggers</title>
@@ -140,6 +140,25 @@
140140
triggers are not fired.
141141
</para>
142142

143+
<para>
144+
A trigger definition can also specify a boolean <literal>WHEN</>
145+
condition, which will be tested to see whether the trigger should
146+
be fired. In row-level triggers the <literal>WHEN</> condition can
147+
examine the old and/or new values of columns of the row. (Statement-level
148+
triggers can also have <literal>WHEN</> conditions, although the feature
149+
is not so useful for them.) In a before trigger, the <literal>WHEN</>
150+
condition is evaluated just before the function is or would be executed,
151+
so using <literal>WHEN</> is not materially different from testing the
152+
same condition at the beginning of the trigger function. However, in
153+
an after trigger, the <literal>WHEN</> condition is evaluated just after
154+
the row update occurs, and it determines whether an event is queued to
155+
fire the trigger at the end of statement. So when an after trigger's
156+
<literal>WHEN</> condition does not return true, it is not necessary
157+
to queue an event nor to re-fetch the row at end of statement. This
158+
can result in significant speedups in statements that modify many
159+
rows, if the trigger only needs to be fired for a few of the rows.
160+
</para>
161+
143162
<para>
144163
Typically, row before triggers are used for checking or
145164
modifying the data that will be inserted or updated. For example,
@@ -497,6 +516,7 @@ typedef struct Trigger
497516
int16 tgnattr;
498517
int16 *tgattr;
499518
char **tgargs;
519+
char *tgqual;
500520
} Trigger;
501521
</programlisting>
502522

‎src/backend/catalog/index.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.323 2009/10/14 22:14:21 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.324 2009/11/20 20:38:09 tgl Exp $
1212
*
1313
*
1414
* INTERFACE ROUTINES
@@ -793,12 +793,13 @@ index_create(Oid heapRelationId,
793793
trigger->row= true;
794794
trigger->events=TRIGGER_TYPE_INSERT |TRIGGER_TYPE_UPDATE;
795795
trigger->columns=NIL;
796+
trigger->whenClause=NULL;
796797
trigger->isconstraint= true;
797798
trigger->deferrable= true;
798799
trigger->initdeferred=initdeferred;
799800
trigger->constrrel=NULL;
800801

801-
(void)CreateTrigger(trigger,conOid,indexRelationId,
802+
(void)CreateTrigger(trigger,NULL,conOid,indexRelationId,
802803
isprimary ?"PK_ConstraintTrigger" :
803804
"Unique_ConstraintTrigger",
804805
false);

‎src/backend/commands/copy.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.317 2009/09/21 20:10:21 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.318 2009/11/20 20:38:10 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1799,8 +1799,12 @@ CopyFrom(CopyState cstate)
17991799
resultRelInfo->ri_RelationDesc=cstate->rel;
18001800
resultRelInfo->ri_TrigDesc=CopyTriggerDesc(cstate->rel->trigdesc);
18011801
if (resultRelInfo->ri_TrigDesc)
1802+
{
18021803
resultRelInfo->ri_TrigFunctions= (FmgrInfo*)
18031804
palloc0(resultRelInfo->ri_TrigDesc->numtriggers*sizeof(FmgrInfo));
1805+
resultRelInfo->ri_TrigWhenExprs= (List**)
1806+
palloc0(resultRelInfo->ri_TrigDesc->numtriggers*sizeof(List*));
1807+
}
18041808
resultRelInfo->ri_TrigInstrument=NULL;
18051809

18061810
ExecOpenIndices(resultRelInfo);
@@ -1810,7 +1814,8 @@ CopyFrom(CopyState cstate)
18101814
estate->es_result_relation_info=resultRelInfo;
18111815

18121816
/* Set up a tuple slot too */
1813-
slot=MakeSingleTupleTableSlot(tupDesc);
1817+
slot=ExecInitExtraTupleSlot(estate);
1818+
ExecSetSlotDescriptor(slot,tupDesc);
18141819

18151820
econtext=GetPerTupleExprContext(estate);
18161821

@@ -2198,7 +2203,7 @@ CopyFrom(CopyState cstate)
21982203
pfree(defmap);
21992204
pfree(defexprs);
22002205

2201-
ExecDropSingleTupleTableSlot(slot);
2206+
ExecResetTupleTable(estate->es_tupleTable, false);
22022207

22032208
ExecCloseIndices(resultRelInfo);
22042209

‎src/backend/commands/tablecmds.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.305 2009/11/04 12:24:23 heikki Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.306 2009/11/20 20:38:10 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -5403,7 +5403,7 @@ validateForeignKeyConstraint(Constraint *fkconstraint,
54035403
trig.tgconstraint=constraintOid;
54045404
trig.tgdeferrable= FALSE;
54055405
trig.tginitdeferred= FALSE;
5406-
/* we needn't fill in tgargs */
5406+
/* we needn't fill in tgargsor tgqual*/
54075407

54085408
/*
54095409
* See if we can do it with a single LEFT JOIN query. A FALSE result
@@ -5476,13 +5476,14 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint,
54765476
}
54775477

54785478
fk_trigger->columns=NIL;
5479+
fk_trigger->whenClause=NULL;
54795480
fk_trigger->isconstraint= true;
54805481
fk_trigger->deferrable=fkconstraint->deferrable;
54815482
fk_trigger->initdeferred=fkconstraint->initdeferred;
54825483
fk_trigger->constrrel=fkconstraint->pktable;
54835484
fk_trigger->args=NIL;
54845485

5485-
(void)CreateTrigger(fk_trigger,constraintOid,indexOid,
5486+
(void)CreateTrigger(fk_trigger,NULL,constraintOid,indexOid,
54865487
"RI_ConstraintTrigger", false);
54875488

54885489
/* Make changes-so-far visible */
@@ -5527,6 +5528,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
55275528
fk_trigger->row= true;
55285529
fk_trigger->events=TRIGGER_TYPE_DELETE;
55295530
fk_trigger->columns=NIL;
5531+
fk_trigger->whenClause=NULL;
55305532
fk_trigger->isconstraint= true;
55315533
fk_trigger->constrrel=myRel;
55325534
switch (fkconstraint->fk_del_action)
@@ -5563,7 +5565,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
55635565
}
55645566
fk_trigger->args=NIL;
55655567

5566-
(void)CreateTrigger(fk_trigger,constraintOid,indexOid,
5568+
(void)CreateTrigger(fk_trigger,NULL,constraintOid,indexOid,
55675569
"RI_ConstraintTrigger", false);
55685570

55695571
/* Make changes-so-far visible */
@@ -5580,6 +5582,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
55805582
fk_trigger->row= true;
55815583
fk_trigger->events=TRIGGER_TYPE_UPDATE;
55825584
fk_trigger->columns=NIL;
5585+
fk_trigger->whenClause=NULL;
55835586
fk_trigger->isconstraint= true;
55845587
fk_trigger->constrrel=myRel;
55855588
switch (fkconstraint->fk_upd_action)
@@ -5616,7 +5619,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
56165619
}
56175620
fk_trigger->args=NIL;
56185621

5619-
(void)CreateTrigger(fk_trigger,constraintOid,indexOid,
5622+
(void)CreateTrigger(fk_trigger,NULL,constraintOid,indexOid,
56205623
"RI_ConstraintTrigger", false);
56215624
}
56225625

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp