Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 92bf7e2

Browse files
committed
Provide the OR REPLACE option for CREATE TRIGGER.
This is mostly straightforward. However, we disallow replacing constraint triggers or changing the is-constraint property; perhaps that can be added later, but the complexity versus benefit tradeoff doesn't look very good. Also, no special thought is taken here for whether replacing an existing trigger should result in changes to queued-but-not-fired trigger actions. We just document that if you're surprised by the results, too bad, don't do that. (Note that any such pending trigger activity would have to be within the current session.) Takamichi Osumi, reviewed at various times by Surafel Temesgen, Peter Smith, and myself Discussion: https://postgr.es/m/0DDF369B45A1B44B8A687ED43F06557C010BC362@G01JPEXMBYT03
1 parent dbca945 commit 92bf7e2

File tree

10 files changed

+408
-114
lines changed

10 files changed

+408
-114
lines changed

doc/src/sgml/ref/create_trigger.sgml

+53-23
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ PostgreSQL documentation
2626

2727
<refsynopsisdiv>
2828
<synopsis>
29-
CREATE [ CONSTRAINT ] TRIGGER <replaceable class="parameter">name</replaceable> { BEFORE | AFTER | INSTEAD OF } { <replaceable class="parameter">event</replaceable> [ OR ... ] }
29+
CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER <replaceable class="parameter">name</replaceable> { BEFORE | AFTER | INSTEAD OF } { <replaceable class="parameter">event</replaceable> [ OR ... ] }
3030
ON <replaceable class="parameter">table_name</replaceable>
3131
[ FROM <replaceable class="parameter">referenced_table_name</replaceable> ]
3232
[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
@@ -48,13 +48,21 @@ CREATE [ CONSTRAINT ] TRIGGER <replaceable class="parameter">name</replaceable>
4848
<title>Description</title>
4949

5050
<para>
51-
<command>CREATE TRIGGER</command> creates a new trigger. The
51+
<command>CREATE TRIGGER</command> creates a new trigger.
52+
<command>CREATE OR REPLACE TRIGGER</command> will either create a
53+
new trigger, or replace an existing trigger. The
5254
trigger will be associated with the specified table, view, or foreign table
5355
and will execute the specified
5456
function <replaceable class="parameter">function_name</replaceable> when
5557
certain operations are performed on that table.
5658
</para>
5759

60+
<para>
61+
To replace the current definition of an existing trigger, use
62+
<command>CREATE OR REPLACE TRIGGER</command>, specifying the existing
63+
trigger's name and parent table. All other properties are replaced.
64+
</para>
65+
5866
<para>
5967
The trigger can be specified to fire before the
6068
operation is attempted on a row (before constraints are checked and
@@ -436,7 +444,7 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
436444
<title>Notes</title>
437445

438446
<para>
439-
To create a trigger on a table, the user must have the
447+
To create or replace a trigger on a table, the user must have the
440448
<literal>TRIGGER</literal> privilege on the table. The user must
441449
also have <literal>EXECUTE</literal> privilege on the trigger function.
442450
</para>
@@ -445,6 +453,17 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
445453
Use <link linkend="sql-droptrigger"><command>DROP TRIGGER</command></link> to remove a trigger.
446454
</para>
447455

456+
<para>
457+
Creating a row-level trigger on a partitioned table will cause an
458+
identical <quote>clone</quote> trigger to be created on each of its
459+
existing partitions; and any partitions created or attached later will have
460+
an identical trigger, too. If there is a conflictingly-named trigger on a
461+
child partition already, an error occurs unless <command>CREATE OR REPLACE
462+
TRIGGER</command> is used, in which case that trigger is replaced with a
463+
clone trigger. When a partition is detached from its parent, its clone
464+
triggers are removed.
465+
</para>
466+
448467
<para>
449468
A column-specific trigger (one defined using the <literal>UPDATE OF
450469
<replaceable>column_name</replaceable></literal> syntax) will fire when any
@@ -457,12 +476,6 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
457476
value did not change.
458477
</para>
459478

460-
<para>
461-
There are a few built-in trigger functions that can be used to
462-
solve common problems without having to write your own trigger code;
463-
see <xref linkend="functions-trigger"/>.
464-
</para>
465-
466479
<para>
467480
In a <literal>BEFORE</literal> trigger, the <literal>WHEN</literal> condition is
468481
evaluated just before the function is or would be executed, so using
@@ -528,14 +541,6 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
528541
the ones that are fired.
529542
</para>
530543

531-
<para>
532-
Creating a row-level trigger on a partitioned table will cause identical
533-
triggers to be created in all its existing partitions; and any partitions
534-
created or attached later will contain an identical trigger, too.
535-
If the partition is detached from its parent, the trigger is removed.
536-
Triggers on partitioned tables may not be <literal>INSTEAD OF</literal>.
537-
</para>
538-
539544
<para>
540545
Modifying a partitioned table or a table with inheritance children fires
541546
statement-level triggers attached to the explicitly named table, but not
@@ -546,9 +551,32 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
546551
named by a <literal>REFERENCING</literal> clause, then before and after
547552
images of rows are visible from all affected partitions or child tables.
548553
In the case of inheritance children, the row images include only columns
549-
that are present in the table that the trigger is attached to. Currently,
550-
row-level triggers with transition relations cannot be defined on
551-
partitions or inheritance child tables.
554+
that are present in the table that the trigger is attached to.
555+
</para>
556+
557+
<para>
558+
Currently, row-level triggers with transition relations cannot be defined
559+
on partitions or inheritance child tables. Also, triggers on partitioned
560+
tables may not be <literal>INSTEAD OF</literal>.
561+
</para>
562+
563+
<para>
564+
Currently, the <literal>OR REPLACE</literal> option is not supported for
565+
constraint triggers.
566+
</para>
567+
568+
<para>
569+
Replacing an existing trigger within a transaction that has already
570+
performed updating actions on the trigger's table is not recommended.
571+
Trigger firing decisions, or portions of firing decisions, that have
572+
already been made will not be reconsidered, so the effects could be
573+
surprising.
574+
</para>
575+
576+
<para>
577+
There are a few built-in trigger functions that can be used to
578+
solve common problems without having to write your own trigger code;
579+
see <xref linkend="functions-trigger"/>.
552580
</para>
553581
</refsect1>
554582

@@ -566,11 +594,12 @@ CREATE TRIGGER check_update
566594
EXECUTE FUNCTION check_account_update();
567595
</programlisting>
568596

569-
The same, but only execute the function if column <literal>balance</literal>
570-
is specified as a target in the <command>UPDATE</command> command:
597+
Modify that trigger definition to only execute the function if
598+
column <literal>balance</literal> is specified as a target in
599+
the <command>UPDATE</command> command:
571600

572601
<programlisting>
573-
CREATE TRIGGER check_update
602+
CREATE OR REPLACE TRIGGER check_update
574603
BEFORE UPDATE OF balance ON accounts
575604
FOR EACH ROW
576605
EXECUTE FUNCTION check_account_update();
@@ -728,6 +757,7 @@ CREATE TRIGGER paired_items_update
728757
<command>CREATE CONSTRAINT TRIGGER</command> is a
729758
<productname>PostgreSQL</productname> extension of the <acronym>SQL</acronym>
730759
standard.
760+
So is the <literal>OR REPLACE</literal> option.
731761
</para>
732762

733763
</refsect1>

src/backend/catalog/index.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -2093,9 +2093,10 @@ index_constraint_create(Relation heapRelation,
20932093
*/
20942094
if (deferrable)
20952095
{
2096-
CreateTrigStmt *trigger;
2096+
CreateTrigStmt *trigger = makeNode(CreateTrigStmt);
20972097

2098-
trigger = makeNode(CreateTrigStmt);
2098+
trigger->replace = false;
2099+
trigger->isconstraint = true;
20992100
trigger->trigname = (constraintType == CONSTRAINT_PRIMARY) ?
21002101
"PK_ConstraintTrigger" :
21012102
"Unique_ConstraintTrigger";
@@ -2107,7 +2108,7 @@ index_constraint_create(Relation heapRelation,
21072108
trigger->events = TRIGGER_TYPE_INSERT | TRIGGER_TYPE_UPDATE;
21082109
trigger->columns = NIL;
21092110
trigger->whenClause = NULL;
2110-
trigger->isconstraint = true;
2111+
trigger->transitionRels = NIL;
21112112
trigger->deferrable = true;
21122113
trigger->initdeferred = initdeferred;
21132114
trigger->constrrel = NULL;

src/backend/commands/tablecmds.c

+16-12
Original file line numberDiff line numberDiff line change
@@ -10550,10 +10550,10 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
1055010550
* and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
1055110551
*/
1055210552
fk_trigger = makeNode(CreateTrigStmt);
10553+
fk_trigger->replace = false;
10554+
fk_trigger->isconstraint = true;
1055310555
fk_trigger->trigname = "RI_ConstraintTrigger_c";
1055410556
fk_trigger->relation = NULL;
10555-
fk_trigger->row = true;
10556-
fk_trigger->timing = TRIGGER_TYPE_AFTER;
1055710557

1055810558
/* Either ON INSERT or ON UPDATE */
1055910559
if (on_insert)
@@ -10567,14 +10567,15 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
1056710567
fk_trigger->events = TRIGGER_TYPE_UPDATE;
1056810568
}
1056910569

10570+
fk_trigger->args = NIL;
10571+
fk_trigger->row = true;
10572+
fk_trigger->timing = TRIGGER_TYPE_AFTER;
1057010573
fk_trigger->columns = NIL;
10571-
fk_trigger->transitionRels = NIL;
1057210574
fk_trigger->whenClause = NULL;
10573-
fk_trigger->isconstraint = true;
10575+
fk_trigger->transitionRels = NIL;
1057410576
fk_trigger->deferrable = fkconstraint->deferrable;
1057510577
fk_trigger->initdeferred = fkconstraint->initdeferred;
1057610578
fk_trigger->constrrel = NULL;
10577-
fk_trigger->args = NIL;
1057810579

1057910580
(void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid,
1058010581
indexOid, InvalidOid, InvalidOid, NULL, true, false);
@@ -10599,15 +10600,17 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
1059910600
* DELETE action on the referenced table.
1060010601
*/
1060110602
fk_trigger = makeNode(CreateTrigStmt);
10603+
fk_trigger->replace = false;
10604+
fk_trigger->isconstraint = true;
1060210605
fk_trigger->trigname = "RI_ConstraintTrigger_a";
1060310606
fk_trigger->relation = NULL;
10607+
fk_trigger->args = NIL;
1060410608
fk_trigger->row = true;
1060510609
fk_trigger->timing = TRIGGER_TYPE_AFTER;
1060610610
fk_trigger->events = TRIGGER_TYPE_DELETE;
1060710611
fk_trigger->columns = NIL;
10608-
fk_trigger->transitionRels = NIL;
1060910612
fk_trigger->whenClause = NULL;
10610-
fk_trigger->isconstraint = true;
10613+
fk_trigger->transitionRels = NIL;
1061110614
fk_trigger->constrrel = NULL;
1061210615
switch (fkconstraint->fk_del_action)
1061310616
{
@@ -10641,7 +10644,6 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
1064110644
(int) fkconstraint->fk_del_action);
1064210645
break;
1064310646
}
10644-
fk_trigger->args = NIL;
1064510647

1064610648
(void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel),
1064710649
constraintOid,
@@ -10655,15 +10657,17 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
1065510657
* UPDATE action on the referenced table.
1065610658
*/
1065710659
fk_trigger = makeNode(CreateTrigStmt);
10660+
fk_trigger->replace = false;
10661+
fk_trigger->isconstraint = true;
1065810662
fk_trigger->trigname = "RI_ConstraintTrigger_a";
1065910663
fk_trigger->relation = NULL;
10664+
fk_trigger->args = NIL;
1066010665
fk_trigger->row = true;
1066110666
fk_trigger->timing = TRIGGER_TYPE_AFTER;
1066210667
fk_trigger->events = TRIGGER_TYPE_UPDATE;
1066310668
fk_trigger->columns = NIL;
10664-
fk_trigger->transitionRels = NIL;
1066510669
fk_trigger->whenClause = NULL;
10666-
fk_trigger->isconstraint = true;
10670+
fk_trigger->transitionRels = NIL;
1066710671
fk_trigger->constrrel = NULL;
1066810672
switch (fkconstraint->fk_upd_action)
1066910673
{
@@ -10697,7 +10701,6 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
1069710701
(int) fkconstraint->fk_upd_action);
1069810702
break;
1069910703
}
10700-
fk_trigger->args = NIL;
1070110704

1070210705
(void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel),
1070310706
constraintOid,
@@ -16898,6 +16901,8 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
1689816901
}
1689916902

1690016903
trigStmt = makeNode(CreateTrigStmt);
16904+
trigStmt->replace = false;
16905+
trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
1690116906
trigStmt->trigname = NameStr(trigForm->tgname);
1690216907
trigStmt->relation = NULL;
1690316908
trigStmt->funcname = NULL; /* passed separately */
@@ -16907,7 +16912,6 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
1690716912
trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
1690816913
trigStmt->columns = cols;
1690916914
trigStmt->whenClause = NULL; /* passed separately */
16910-
trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
1691116915
trigStmt->transitionRels = NIL; /* not supported at present */
1691216916
trigStmt->deferrable = trigForm->tgdeferrable;
1691316917
trigStmt->initdeferred = trigForm->tginitdeferred;

0 commit comments

Comments
 (0)