]> granicus.if.org Git - postgresql/blob - src/backend/commands/trigger.c
Mop-up some infelicities in new relation lookup handling.
[postgresql] / src / backend / commands / trigger.c
1 /*-------------------------------------------------------------------------
2  *
3  * trigger.c
4  *        PostgreSQL TRIGGERs support code.
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.109 2002/03/29 22:10:33 tgl Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "access/genam.h"
17 #include "access/heapam.h"
18 #include "catalog/catalog.h"
19 #include "catalog/catname.h"
20 #include "catalog/indexing.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_language.h"
23 #include "catalog/pg_proc.h"
24 #include "catalog/pg_trigger.h"
25 #include "commands/comment.h"
26 #include "commands/trigger.h"
27 #include "executor/executor.h"
28 #include "miscadmin.h"
29 #include "utils/acl.h"
30 #include "utils/builtins.h"
31 #include "utils/fmgroids.h"
32 #include "utils/inval.h"
33 #include "utils/lsyscache.h"
34 #include "utils/syscache.h"
35
36
37 static void InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx);
38 static HeapTuple GetTupleForTrigger(EState *estate,
39                                    ResultRelInfo *relinfo,
40                                    ItemPointer tid,
41                                    TupleTableSlot **newSlot);
42 static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
43                                         FmgrInfo *finfo,
44                                         MemoryContext per_tuple_context);
45 static void DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
46                                                  HeapTuple oldtup, HeapTuple newtup);
47 static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
48                                            Relation rel, FmgrInfo *finfo,
49                                            MemoryContext per_tuple_context);
50
51
52 void
53 CreateTrigger(CreateTrigStmt *stmt)
54 {
55         int16           tgtype;
56         int16           tgattr[FUNC_MAX_ARGS];
57         Datum           values[Natts_pg_trigger];
58         char            nulls[Natts_pg_trigger];
59         Relation        rel;
60         Relation        tgrel;
61         SysScanDesc     tgscan;
62         ScanKeyData key;
63         Relation        pgrel;
64         HeapTuple       tuple;
65         Relation        idescs[Num_pg_trigger_indices];
66         Relation        ridescs[Num_pg_class_indices];
67         Oid                     fargtypes[FUNC_MAX_ARGS];
68         Oid                     funcoid;
69         Oid                     funclang;
70         int                     found = 0;
71         int                     i;
72         char            constrtrigname[NAMEDATALEN];
73         char       *constrname = "";
74         Oid                     constrrelid = InvalidOid;
75
76         rel = heap_openrv(stmt->relation, AccessExclusiveLock);
77
78         if (rel->rd_rel->relkind != RELKIND_RELATION)
79                 elog(ERROR, "CreateTrigger: relation \"%s\" is not a table",
80                          stmt->relation->relname);
81
82         if (!allowSystemTableMods && IsSystemRelationName(stmt->relation->relname))
83                 elog(ERROR, "CreateTrigger: can't create trigger for system relation %s",
84                         stmt->relation->relname);
85
86         if (pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
87                                                   stmt->isconstraint ? ACL_REFERENCES : ACL_TRIGGER)
88                 != ACLCHECK_OK)
89                 elog(ERROR, "permission denied");
90
91         /*
92          * If trigger is a constraint, user trigger name as constraint name
93          * and build a unique trigger name instead.
94          */
95         if (stmt->isconstraint)
96         {
97                 constrname = stmt->trigname;
98                 stmt->trigname = constrtrigname;
99                 sprintf(constrtrigname, "RI_ConstraintTrigger_%u", newoid());
100
101                 if (stmt->constrrel != NULL)
102                         constrrelid = RangeVarGetRelid(stmt->constrrel, false);
103                 else
104                         constrrelid = InvalidOid;
105         }
106
107         TRIGGER_CLEAR_TYPE(tgtype);
108         if (stmt->before)
109                 TRIGGER_SETT_BEFORE(tgtype);
110         if (stmt->row)
111                 TRIGGER_SETT_ROW(tgtype);
112         else
113                 elog(ERROR, "CreateTrigger: STATEMENT triggers are unimplemented, yet");
114
115         for (i = 0; i < 3 && stmt->actions[i]; i++)
116         {
117                 switch (stmt->actions[i])
118                 {
119                         case 'i':
120                                 if (TRIGGER_FOR_INSERT(tgtype))
121                                         elog(ERROR, "CreateTrigger: double INSERT event specified");
122                                 TRIGGER_SETT_INSERT(tgtype);
123                                 break;
124                         case 'd':
125                                 if (TRIGGER_FOR_DELETE(tgtype))
126                                         elog(ERROR, "CreateTrigger: double DELETE event specified");
127                                 TRIGGER_SETT_DELETE(tgtype);
128                                 break;
129                         case 'u':
130                                 if (TRIGGER_FOR_UPDATE(tgtype))
131                                         elog(ERROR, "CreateTrigger: double UPDATE event specified");
132                                 TRIGGER_SETT_UPDATE(tgtype);
133                                 break;
134                         default:
135                                 elog(ERROR, "CreateTrigger: unknown event specified");
136                                 break;
137                 }
138         }
139
140         /*
141          * Scan pg_trigger for existing triggers on relation.  NOTE that this
142          * is cool only because we have AccessExclusiveLock on the relation,
143          * so the trigger set won't be changing underneath us.
144          */
145         tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
146         ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
147                                                    F_OIDEQ,
148                                                    ObjectIdGetDatum(RelationGetRelid(rel)));
149         tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true,
150                                                                 SnapshotNow, 1, &key);
151         while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
152         {
153                 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
154
155                 if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
156                         elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s",
157                                  stmt->trigname, stmt->relation->relname);
158                 found++;
159         }
160         systable_endscan(tgscan);
161
162         /*
163          * Find and validate the trigger function.
164          */
165         MemSet(fargtypes, 0, FUNC_MAX_ARGS * sizeof(Oid));
166         tuple = SearchSysCache(PROCNAME,
167                                                    PointerGetDatum(stmt->funcname),
168                                                    Int32GetDatum(0),
169                                                    PointerGetDatum(fargtypes),
170                                                    0);
171         if (!HeapTupleIsValid(tuple))
172                 elog(ERROR, "CreateTrigger: function %s() does not exist",
173                          stmt->funcname);
174         if (((Form_pg_proc) GETSTRUCT(tuple))->prorettype != 0)
175                 elog(ERROR, "CreateTrigger: function %s() must return OPAQUE",
176                          stmt->funcname);
177         funcoid = tuple->t_data->t_oid;
178         funclang = ((Form_pg_proc) GETSTRUCT(tuple))->prolang;
179         ReleaseSysCache(tuple);
180
181         if (funclang != ClanguageId && funclang != INTERNALlanguageId)
182         {
183                 HeapTuple       langTup;
184
185                 langTup = SearchSysCache(LANGOID,
186                                                                  ObjectIdGetDatum(funclang),
187                                                                  0, 0, 0);
188                 if (!HeapTupleIsValid(langTup))
189                         elog(ERROR, "CreateTrigger: cache lookup for language %u failed",
190                                  funclang);
191                 if (((Form_pg_language) GETSTRUCT(langTup))->lanispl == false)
192                         elog(ERROR, "CreateTrigger: only internal, C and PL functions are supported");
193                 ReleaseSysCache(langTup);
194         }
195
196         /*
197          * Build the new pg_trigger tuple.
198          */
199         MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char));
200
201         values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
202         values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
203                                                                                 CStringGetDatum(stmt->trigname));
204         values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
205         values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
206         values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true);
207         values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint);
208         values[Anum_pg_trigger_tgconstrname - 1] = PointerGetDatum(constrname);
209         values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
210         values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
211         values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
212
213         if (stmt->args)
214         {
215                 List       *le;
216                 char       *args;
217                 int16           nargs = length(stmt->args);
218                 int                     len = 0;
219
220                 foreach(le, stmt->args)
221                 {
222                         char       *ar = ((Value *) lfirst(le))->val.str;
223
224                         len += strlen(ar) + 4;
225                         for (; *ar; ar++)
226                         {
227                                 if (*ar == '\\')
228                                         len++;
229                         }
230                 }
231                 args = (char *) palloc(len + 1);
232                 args[0] = '\0';
233                 foreach(le, stmt->args)
234                 {
235                         char       *s = ((Value *) lfirst(le))->val.str;
236                         char       *d = args + strlen(args);
237
238                         while (*s)
239                         {
240                                 if (*s == '\\')
241                                         *d++ = '\\';
242                                 *d++ = *s++;
243                         }
244                         strcpy(d, "\\000");
245                 }
246                 values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
247                 values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
248                                                                                                   CStringGetDatum(args));
249         }
250         else
251         {
252                 values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
253                 values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
254                                                                                                         CStringGetDatum(""));
255         }
256         MemSet(tgattr, 0, FUNC_MAX_ARGS * sizeof(int16));
257         values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
258
259         tuple = heap_formtuple(tgrel->rd_att, values, nulls);
260
261         /*
262          * Insert tuple into pg_trigger.
263          */
264         heap_insert(tgrel, tuple);
265         CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
266         CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
267         CatalogCloseIndices(Num_pg_trigger_indices, idescs);
268         heap_freetuple(tuple);
269         heap_close(tgrel, RowExclusiveLock);
270
271         pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
272         pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
273
274         /*
275          * Update relation's pg_class entry.  Crucial side-effect: other
276          * backends (and this one too!) are sent SI message to make them
277          * rebuild relcache entries.
278          */
279         pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
280         tuple = SearchSysCacheCopy(RELOID,
281                                                            ObjectIdGetDatum(RelationGetRelid(rel)),
282                                                            0, 0, 0);
283         if (!HeapTupleIsValid(tuple))
284                 elog(ERROR, "CreateTrigger: relation %s not found in pg_class",
285                          stmt->relation->relname);
286
287         ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;
288         simple_heap_update(pgrel, &tuple->t_self, tuple);
289         CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
290         CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
291         CatalogCloseIndices(Num_pg_class_indices, ridescs);
292         heap_freetuple(tuple);
293         heap_close(pgrel, RowExclusiveLock);
294
295         /*
296          * We used to try to update the rel's relcache entry here, but that's
297          * fairly pointless since it will happen as a byproduct of the
298          * upcoming CommandCounterIncrement...
299          */
300
301         /* Keep lock on target rel until end of xact */
302         heap_close(rel, NoLock);
303 }
304
305 /*
306  * DropTrigger - drop an individual trigger by name
307  */
308 void
309 DropTrigger(Oid relid, const char *trigname)
310 {
311         Relation        rel;
312         Relation        tgrel;
313         SysScanDesc     tgscan;
314         ScanKeyData key;
315         Relation        pgrel;
316         HeapTuple       tuple;
317         Relation        ridescs[Num_pg_class_indices];
318         int                     remaining = 0;
319         int                     found = 0;
320
321         rel = heap_open(relid, AccessExclusiveLock);
322
323         if (rel->rd_rel->relkind != RELKIND_RELATION)
324                 elog(ERROR, "DropTrigger: relation \"%s\" is not a table",
325                          RelationGetRelationName(rel));
326
327         if (!allowSystemTableMods && IsSystemRelationName(RelationGetRelationName(rel)))
328                 elog(ERROR, "DropTrigger: can't drop trigger for system relation %s",
329                          RelationGetRelationName(rel));
330
331         if (!pg_class_ownercheck(relid, GetUserId()))
332                 elog(ERROR, "%s: %s", RelationGetRelationName(rel),
333                          aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
334
335         /*
336          * Search pg_trigger, delete target trigger, count remaining triggers
337          * for relation.  Note this is OK only because we have
338          * AccessExclusiveLock on the rel, so no one else is creating/deleting
339          * triggers on this rel at the same time.
340          */
341         tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
342         ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
343                                                    F_OIDEQ,
344                                                    ObjectIdGetDatum(relid));
345         tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true,
346                                                                 SnapshotNow, 1, &key);
347         while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
348         {
349                 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
350
351                 if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
352                 {
353                         /* Delete any comments associated with this trigger */
354                         DeleteComments(tuple->t_data->t_oid, RelationGetRelid(tgrel));
355
356                         simple_heap_delete(tgrel, &tuple->t_self);
357                         found++;
358                 }
359                 else
360                         remaining++;
361         }
362         systable_endscan(tgscan);
363         heap_close(tgrel, RowExclusiveLock);
364
365         if (found == 0)
366                 elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
367                          trigname, RelationGetRelationName(rel));
368         if (found > 1)                          /* shouldn't happen */
369                 elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s",
370                          found, trigname, RelationGetRelationName(rel));
371
372         /*
373          * Update relation's pg_class entry.  Crucial side-effect: other
374          * backends (and this one too!) are sent SI message to make them
375          * rebuild relcache entries.
376          */
377         pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
378         tuple = SearchSysCacheCopy(RELOID,
379                                                            ObjectIdGetDatum(relid),
380                                                            0, 0, 0);
381         if (!HeapTupleIsValid(tuple))
382                 elog(ERROR, "DropTrigger: relation %s not found in pg_class",
383                          RelationGetRelationName(rel));
384
385         ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = remaining;
386         simple_heap_update(pgrel, &tuple->t_self, tuple);
387         CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
388         CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
389         CatalogCloseIndices(Num_pg_class_indices, ridescs);
390         heap_freetuple(tuple);
391         heap_close(pgrel, RowExclusiveLock);
392
393         /* Keep lock on target rel until end of xact */
394         heap_close(rel, NoLock);
395 }
396
397 /*
398  * Remove all triggers for a relation that's being deleted.
399  */
400 void
401 RelationRemoveTriggers(Relation rel)
402 {
403         Relation        tgrel;
404         SysScanDesc     tgscan;
405         ScanKeyData key;
406         HeapTuple       tup;
407         bool            found = false;
408
409         tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
410         ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
411                                                    F_OIDEQ,
412                                                    ObjectIdGetDatum(RelationGetRelid(rel)));
413         tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true,
414                                                                 SnapshotNow, 1, &key);
415
416         while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
417         {
418                 /* Delete any comments associated with this trigger */
419                 DeleteComments(tup->t_data->t_oid, RelationGetRelid(tgrel));
420
421                 simple_heap_delete(tgrel, &tup->t_self);
422
423                 found = true;
424         }
425
426         systable_endscan(tgscan);
427
428         /*
429          * If we deleted any triggers, must update pg_class entry and advance
430          * command counter to make the updated entry visible. This is fairly
431          * annoying, since we'e just going to drop the durn thing later, but
432          * it's necessary to have a consistent state in case we do
433          * CommandCounterIncrement() below --- if RelationBuildTriggers()
434          * runs, it will complain otherwise. Perhaps RelationBuildTriggers()
435          * shouldn't be so picky...
436          */
437         if (found)
438         {
439                 Relation        pgrel;
440                 Relation        ridescs[Num_pg_class_indices];
441
442                 pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
443                 tup = SearchSysCacheCopy(RELOID,
444                                                                  ObjectIdGetDatum(RelationGetRelid(rel)),
445                                                                  0, 0, 0);
446                 if (!HeapTupleIsValid(tup))
447                         elog(ERROR, "RelationRemoveTriggers: relation %u not found in pg_class",
448                                  RelationGetRelid(rel));
449
450                 ((Form_pg_class) GETSTRUCT(tup))->reltriggers = 0;
451                 simple_heap_update(pgrel, &tup->t_self, tup);
452                 CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
453                 CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tup);
454                 CatalogCloseIndices(Num_pg_class_indices, ridescs);
455                 heap_freetuple(tup);
456                 heap_close(pgrel, RowExclusiveLock);
457                 CommandCounterIncrement();
458         }
459
460         /*
461          * Also drop all constraint triggers referencing this relation
462          */
463         ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgconstrrelid,
464                                                    F_OIDEQ,
465                                                    ObjectIdGetDatum(RelationGetRelid(rel)));
466         tgscan = systable_beginscan(tgrel, TriggerConstrRelidIndex, true,
467                                                                 SnapshotNow, 1, &key);
468
469         while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
470         {
471                 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tup);
472
473                 elog(NOTICE, "DROP TABLE implicitly drops referential integrity trigger from table \"%s\"",
474                          get_temp_rel_by_physicalname(get_rel_name(pg_trigger->tgrelid)));
475
476                 DropTrigger(pg_trigger->tgrelid, NameStr(pg_trigger->tgname));
477
478                 /*
479                  * Need to do a command counter increment here to show up new
480                  * pg_class.reltriggers in the next loop iteration (in case there
481                  * are multiple referential integrity action triggers for the same
482                  * FK table defined on the PK table).
483                  */
484                 CommandCounterIncrement();
485         }
486         systable_endscan(tgscan);
487
488         heap_close(tgrel, RowExclusiveLock);
489 }
490
491 /*
492  * Build trigger data to attach to the given relcache entry.
493  *
494  * Note that trigger data must be allocated in CacheMemoryContext
495  * to ensure it survives as long as the relcache entry.  But we
496  * are probably running in a less long-lived working context.
497  */
498 void
499 RelationBuildTriggers(Relation relation)
500 {
501         TriggerDesc *trigdesc;
502         int                     ntrigs = relation->rd_rel->reltriggers;
503         Trigger    *triggers = NULL;
504         int                     found = 0;
505         Relation        tgrel;
506         ScanKeyData skey;
507         SysScanDesc     tgscan;
508         HeapTuple       htup;
509         struct varlena *val;
510         bool            isnull;
511
512         ScanKeyEntryInitialize(&skey,
513                                                    (bits16) 0x0,
514                                                    (AttrNumber) Anum_pg_trigger_tgrelid,
515                                                    (RegProcedure) F_OIDEQ,
516                                                    ObjectIdGetDatum(RelationGetRelid(relation)));
517
518         tgrel = heap_openr(TriggerRelationName, AccessShareLock);
519         tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true,
520                                                                 SnapshotNow, 1, &skey);
521
522         while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
523         {
524                 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
525                 Trigger    *build;
526
527                 if (found == ntrigs)
528                         elog(ERROR, "RelationBuildTriggers: unexpected record found for rel %s",
529                                  RelationGetRelationName(relation));
530
531                 if (triggers == NULL)
532                         triggers = (Trigger *) MemoryContextAlloc(CacheMemoryContext,
533                                                                                                           sizeof(Trigger));
534                 else
535                         triggers = (Trigger *) repalloc(triggers,
536                                                                                   (found + 1) * sizeof(Trigger));
537                 build = &(triggers[found]);
538
539                 build->tgoid = htup->t_data->t_oid;
540                 build->tgname = MemoryContextStrdup(CacheMemoryContext,
541                                                          DatumGetCString(DirectFunctionCall1(nameout,
542                                                                         NameGetDatum(&pg_trigger->tgname))));
543                 build->tgfoid = pg_trigger->tgfoid;
544                 build->tgtype = pg_trigger->tgtype;
545                 build->tgenabled = pg_trigger->tgenabled;
546                 build->tgisconstraint = pg_trigger->tgisconstraint;
547                 build->tgdeferrable = pg_trigger->tgdeferrable;
548                 build->tginitdeferred = pg_trigger->tginitdeferred;
549                 build->tgnargs = pg_trigger->tgnargs;
550                 memcpy(build->tgattr, &(pg_trigger->tgattr),
551                            FUNC_MAX_ARGS * sizeof(int16));
552                 if (build->tgnargs > 0)
553                 {
554                         char       *p;
555                         int                     i;
556
557                         val = (struct varlena *) fastgetattr(htup,
558                                                                                                  Anum_pg_trigger_tgargs,
559                                                                                                  tgrel->rd_att, &isnull);
560                         if (isnull)
561                                 elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %s",
562                                          RelationGetRelationName(relation));
563                         p = (char *) VARDATA(val);
564                         build->tgargs = (char **)
565                                 MemoryContextAlloc(CacheMemoryContext,
566                                                                    build->tgnargs * sizeof(char *));
567                         for (i = 0; i < build->tgnargs; i++)
568                         {
569                                 build->tgargs[i] = MemoryContextStrdup(CacheMemoryContext,
570                                                                                                            p);
571                                 p += strlen(p) + 1;
572                         }
573                 }
574                 else
575                         build->tgargs = NULL;
576
577                 found++;
578         }
579
580         systable_endscan(tgscan);
581         heap_close(tgrel, AccessShareLock);
582
583         if (found != ntrigs)
584                 elog(ERROR, "RelationBuildTriggers: %d record(s) not found for rel %s",
585                          ntrigs - found,
586                          RelationGetRelationName(relation));
587
588         /* Build trigdesc */
589         trigdesc = (TriggerDesc *) MemoryContextAlloc(CacheMemoryContext,
590                                                                                                   sizeof(TriggerDesc));
591         MemSet(trigdesc, 0, sizeof(TriggerDesc));
592         trigdesc->triggers = triggers;
593         trigdesc->numtriggers = ntrigs;
594         for (found = 0; found < ntrigs; found++)
595                 InsertTrigger(trigdesc, &(triggers[found]), found);
596
597         relation->trigdesc = trigdesc;
598 }
599
600 /* Insert the given trigger into the appropriate index list(s) for it */
601 static void
602 InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx)
603 {
604         uint16     *n;
605         int               **t,
606                           **tp;
607
608         if (TRIGGER_FOR_ROW(trigger->tgtype))
609         {
610                 /* ROW trigger */
611                 if (TRIGGER_FOR_BEFORE(trigger->tgtype))
612                 {
613                         n = trigdesc->n_before_row;
614                         t = trigdesc->tg_before_row;
615                 }
616                 else
617                 {
618                         n = trigdesc->n_after_row;
619                         t = trigdesc->tg_after_row;
620                 }
621         }
622         else
623         {
624                 /* STATEMENT trigger */
625                 if (TRIGGER_FOR_BEFORE(trigger->tgtype))
626                 {
627                         n = trigdesc->n_before_statement;
628                         t = trigdesc->tg_before_statement;
629                 }
630                 else
631                 {
632                         n = trigdesc->n_after_statement;
633                         t = trigdesc->tg_after_statement;
634                 }
635         }
636
637         if (TRIGGER_FOR_INSERT(trigger->tgtype))
638         {
639                 tp = &(t[TRIGGER_EVENT_INSERT]);
640                 if (*tp == NULL)
641                         *tp = (int *) MemoryContextAlloc(CacheMemoryContext,
642                                                                                          sizeof(int));
643                 else
644                         *tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) *
645                                                                    sizeof(int));
646                 (*tp)[n[TRIGGER_EVENT_INSERT]] = indx;
647                 (n[TRIGGER_EVENT_INSERT])++;
648         }
649
650         if (TRIGGER_FOR_DELETE(trigger->tgtype))
651         {
652                 tp = &(t[TRIGGER_EVENT_DELETE]);
653                 if (*tp == NULL)
654                         *tp = (int *) MemoryContextAlloc(CacheMemoryContext,
655                                                                                          sizeof(int));
656                 else
657                         *tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) *
658                                                                    sizeof(int));
659                 (*tp)[n[TRIGGER_EVENT_DELETE]] = indx;
660                 (n[TRIGGER_EVENT_DELETE])++;
661         }
662
663         if (TRIGGER_FOR_UPDATE(trigger->tgtype))
664         {
665                 tp = &(t[TRIGGER_EVENT_UPDATE]);
666                 if (*tp == NULL)
667                         *tp = (int *) MemoryContextAlloc(CacheMemoryContext,
668                                                                                          sizeof(int));
669                 else
670                         *tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) *
671                                                                    sizeof(int));
672                 (*tp)[n[TRIGGER_EVENT_UPDATE]] = indx;
673                 (n[TRIGGER_EVENT_UPDATE])++;
674         }
675 }
676
677 void
678 FreeTriggerDesc(TriggerDesc *trigdesc)
679 {
680         int               **t;
681         Trigger    *trigger;
682         int                     i;
683
684         if (trigdesc == NULL)
685                 return;
686
687         t = trigdesc->tg_before_statement;
688         for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
689                 if (t[i] != NULL)
690                         pfree(t[i]);
691         t = trigdesc->tg_before_row;
692         for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
693                 if (t[i] != NULL)
694                         pfree(t[i]);
695         t = trigdesc->tg_after_row;
696         for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
697                 if (t[i] != NULL)
698                         pfree(t[i]);
699         t = trigdesc->tg_after_statement;
700         for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
701                 if (t[i] != NULL)
702                         pfree(t[i]);
703
704         trigger = trigdesc->triggers;
705         for (i = 0; i < trigdesc->numtriggers; i++)
706         {
707                 pfree(trigger->tgname);
708                 if (trigger->tgnargs > 0)
709                 {
710                         while (--(trigger->tgnargs) >= 0)
711                                 pfree(trigger->tgargs[trigger->tgnargs]);
712                         pfree(trigger->tgargs);
713                 }
714                 trigger++;
715         }
716         pfree(trigdesc->triggers);
717         pfree(trigdesc);
718 }
719
720 bool
721 equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
722 {
723         int                     i,
724                                 j;
725
726         /*
727          * We need not examine the "index" data, just the trigger array
728          * itself; if we have the same triggers with the same types, the
729          * derived index data should match.
730          */
731         if (trigdesc1 != NULL)
732         {
733                 if (trigdesc2 == NULL)
734                         return false;
735                 if (trigdesc1->numtriggers != trigdesc2->numtriggers)
736                         return false;
737                 for (i = 0; i < trigdesc1->numtriggers; i++)
738                 {
739                         Trigger    *trig1 = trigdesc1->triggers + i;
740                         Trigger    *trig2 = NULL;
741
742                         /*
743                          * We can't assume that the triggers are always read from
744                          * pg_trigger in the same order; so use the trigger OIDs to
745                          * identify the triggers to compare.  (We assume here that the
746                          * same OID won't appear twice in either trigger set.)
747                          */
748                         for (j = 0; j < trigdesc2->numtriggers; j++)
749                         {
750                                 trig2 = trigdesc2->triggers + j;
751                                 if (trig1->tgoid == trig2->tgoid)
752                                         break;
753                         }
754                         if (j >= trigdesc2->numtriggers)
755                                 return false;
756                         if (strcmp(trig1->tgname, trig2->tgname) != 0)
757                                 return false;
758                         if (trig1->tgfoid != trig2->tgfoid)
759                                 return false;
760                         if (trig1->tgtype != trig2->tgtype)
761                                 return false;
762                         if (trig1->tgenabled != trig2->tgenabled)
763                                 return false;
764                         if (trig1->tgisconstraint != trig2->tgisconstraint)
765                                 return false;
766                         if (trig1->tgdeferrable != trig2->tgdeferrable)
767                                 return false;
768                         if (trig1->tginitdeferred != trig2->tginitdeferred)
769                                 return false;
770                         if (trig1->tgnargs != trig2->tgnargs)
771                                 return false;
772                         if (memcmp(trig1->tgattr, trig2->tgattr,
773                                            sizeof(trig1->tgattr)) != 0)
774                                 return false;
775                         for (j = 0; j < trig1->tgnargs; j++)
776                                 if (strcmp(trig1->tgargs[j], trig2->tgargs[j]) != 0)
777                                         return false;
778                 }
779         }
780         else if (trigdesc2 != NULL)
781                 return false;
782         return true;
783 }
784
785 /*
786  * Call a trigger function.
787  *
788  *              trigdata: trigger descriptor.
789  *              finfo: possibly-cached call info for the function.
790  *              per_tuple_context: memory context to execute the function in.
791  *
792  * Returns the tuple (or NULL) as returned by the function.
793  */
794 static HeapTuple
795 ExecCallTriggerFunc(TriggerData *trigdata,
796                                         FmgrInfo *finfo,
797                                         MemoryContext per_tuple_context)
798 {
799         FunctionCallInfoData fcinfo;
800         Datum           result;
801         MemoryContext oldContext;
802
803         /*
804          * We cache fmgr lookup info, to avoid making the lookup again on each
805          * call.
806          */
807         if (finfo->fn_oid == InvalidOid)
808                 fmgr_info(trigdata->tg_trigger->tgfoid, finfo);
809
810         Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid);
811
812         /*
813          * Do the function evaluation in the per-tuple memory context, so that
814          * leaked memory will be reclaimed once per tuple. Note in particular
815          * that any new tuple created by the trigger function will live till
816          * the end of the tuple cycle.
817          */
818         oldContext = MemoryContextSwitchTo(per_tuple_context);
819
820         /*
821          * Call the function, passing no arguments but setting a context.
822          */
823         MemSet(&fcinfo, 0, sizeof(fcinfo));
824
825         fcinfo.flinfo = finfo;
826         fcinfo.context = (Node *) trigdata;
827
828         result = FunctionCallInvoke(&fcinfo);
829
830         MemoryContextSwitchTo(oldContext);
831
832         /*
833          * Trigger protocol allows function to return a null pointer, but NOT
834          * to set the isnull result flag.
835          */
836         if (fcinfo.isnull)
837                 elog(ERROR, "ExecCallTriggerFunc: function %u returned NULL",
838                          fcinfo.flinfo->fn_oid);
839
840         return (HeapTuple) DatumGetPointer(result);
841 }
842
843 HeapTuple
844 ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
845                                          HeapTuple trigtuple)
846 {
847         TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
848         int                     ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_INSERT];
849         int                *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_INSERT];
850         HeapTuple       newtuple = trigtuple;
851         HeapTuple       oldtuple;
852         TriggerData LocTriggerData;
853         int                     i;
854
855         /* Allocate cache space for fmgr lookup info, if not done yet */
856         if (relinfo->ri_TrigFunctions == NULL)
857         {
858                 relinfo->ri_TrigFunctions = (FmgrInfo *)
859                         palloc(trigdesc->numtriggers * sizeof(FmgrInfo));
860                 MemSet(relinfo->ri_TrigFunctions, 0,
861                            trigdesc->numtriggers * sizeof(FmgrInfo));
862         }
863
864         LocTriggerData.type = T_TriggerData;
865         LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
866         LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
867         LocTriggerData.tg_newtuple = NULL;
868         for (i = 0; i < ntrigs; i++)
869         {
870                 Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
871
872                 if (!trigger->tgenabled)
873                         continue;
874                 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
875                 LocTriggerData.tg_trigger = trigger;
876                 newtuple = ExecCallTriggerFunc(&LocTriggerData,
877                                                                    relinfo->ri_TrigFunctions + tgindx[i],
878                                                                            GetPerTupleMemoryContext(estate));
879                 if (oldtuple != newtuple && oldtuple != trigtuple)
880                         heap_freetuple(oldtuple);
881                 if (newtuple == NULL)
882                         break;
883         }
884         return newtuple;
885 }
886
887 void
888 ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
889                                          HeapTuple trigtuple)
890 {
891         TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
892
893         if (trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
894                 DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
895                                                                  NULL, trigtuple);
896 }
897
898 bool
899 ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
900                                          ItemPointer tupleid)
901 {
902         TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
903         int                     ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_DELETE];
904         int                *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_DELETE];
905         TriggerData LocTriggerData;
906         HeapTuple       trigtuple;
907         HeapTuple       newtuple = NULL;
908         TupleTableSlot *newSlot;
909         int                     i;
910
911         trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
912         if (trigtuple == NULL)
913                 return false;
914
915         /* Allocate cache space for fmgr lookup info, if not done yet */
916         if (relinfo->ri_TrigFunctions == NULL)
917         {
918                 relinfo->ri_TrigFunctions = (FmgrInfo *)
919                         palloc(trigdesc->numtriggers * sizeof(FmgrInfo));
920                 MemSet(relinfo->ri_TrigFunctions, 0,
921                            trigdesc->numtriggers * sizeof(FmgrInfo));
922         }
923
924         LocTriggerData.type = T_TriggerData;
925         LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
926         LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
927         LocTriggerData.tg_newtuple = NULL;
928         for (i = 0; i < ntrigs; i++)
929         {
930                 Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
931
932                 if (!trigger->tgenabled)
933                         continue;
934                 LocTriggerData.tg_trigtuple = trigtuple;
935                 LocTriggerData.tg_trigger = trigger;
936                 newtuple = ExecCallTriggerFunc(&LocTriggerData,
937                                                                    relinfo->ri_TrigFunctions + tgindx[i],
938                                                                            GetPerTupleMemoryContext(estate));
939                 if (newtuple == NULL)
940                         break;
941                 if (newtuple != trigtuple)
942                         heap_freetuple(newtuple);
943         }
944         heap_freetuple(trigtuple);
945
946         return (newtuple == NULL) ? false : true;
947 }
948
949 void
950 ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
951                                          ItemPointer tupleid)
952 {
953         TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
954
955         if (trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
956         {
957                 HeapTuple       trigtuple = GetTupleForTrigger(estate, relinfo,
958                                                                                                    tupleid, NULL);
959
960                 DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
961                                                                  trigtuple, NULL);
962                 heap_freetuple(trigtuple);
963         }
964 }
965
966 HeapTuple
967 ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
968                                          ItemPointer tupleid, HeapTuple newtuple)
969 {
970         TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
971         int                     ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_UPDATE];
972         int                *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_UPDATE];
973         TriggerData LocTriggerData;
974         HeapTuple       trigtuple;
975         HeapTuple       oldtuple;
976         HeapTuple       intuple = newtuple;
977         TupleTableSlot *newSlot;
978         int                     i;
979
980         trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
981         if (trigtuple == NULL)
982                 return NULL;
983
984         /*
985          * In READ COMMITTED isolevel it's possible that newtuple was changed
986          * due to concurrent update.
987          */
988         if (newSlot != NULL)
989                 intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot);
990
991         /* Allocate cache space for fmgr lookup info, if not done yet */
992         if (relinfo->ri_TrigFunctions == NULL)
993         {
994                 relinfo->ri_TrigFunctions = (FmgrInfo *)
995                         palloc(trigdesc->numtriggers * sizeof(FmgrInfo));
996                 MemSet(relinfo->ri_TrigFunctions, 0,
997                            trigdesc->numtriggers * sizeof(FmgrInfo));
998         }
999
1000         LocTriggerData.type = T_TriggerData;
1001         LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
1002         LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
1003         for (i = 0; i < ntrigs; i++)
1004         {
1005                 Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
1006
1007                 if (!trigger->tgenabled)
1008                         continue;
1009                 LocTriggerData.tg_trigtuple = trigtuple;
1010                 LocTriggerData.tg_newtuple = oldtuple = newtuple;
1011                 LocTriggerData.tg_trigger = trigger;
1012                 newtuple = ExecCallTriggerFunc(&LocTriggerData,
1013                                                                    relinfo->ri_TrigFunctions + tgindx[i],
1014                                                                            GetPerTupleMemoryContext(estate));
1015                 if (oldtuple != newtuple && oldtuple != intuple)
1016                         heap_freetuple(oldtuple);
1017                 if (newtuple == NULL)
1018                         break;
1019         }
1020         heap_freetuple(trigtuple);
1021         return newtuple;
1022 }
1023
1024 void
1025 ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
1026                                          ItemPointer tupleid, HeapTuple newtuple)
1027 {
1028         TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
1029
1030         if (trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0)
1031         {
1032                 HeapTuple       trigtuple = GetTupleForTrigger(estate, relinfo,
1033                                                                                                    tupleid, NULL);
1034
1035                 DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
1036                                                                  trigtuple, newtuple);
1037                 heap_freetuple(trigtuple);
1038         }
1039 }
1040
1041
1042 static HeapTuple
1043 GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
1044                                    ItemPointer tid, TupleTableSlot **newSlot)
1045 {
1046         Relation        relation = relinfo->ri_RelationDesc;
1047         HeapTupleData tuple;
1048         HeapTuple       result;
1049         Buffer          buffer;
1050
1051         if (newSlot != NULL)
1052         {
1053                 int                     test;
1054
1055                 /*
1056                  * mark tuple for update
1057                  */
1058                 *newSlot = NULL;
1059                 tuple.t_self = *tid;
1060 ltrmark:;
1061                 test = heap_mark4update(relation, &tuple, &buffer);
1062                 switch (test)
1063                 {
1064                         case HeapTupleSelfUpdated:
1065                                 ReleaseBuffer(buffer);
1066                                 return (NULL);
1067
1068                         case HeapTupleMayBeUpdated:
1069                                 break;
1070
1071                         case HeapTupleUpdated:
1072                                 ReleaseBuffer(buffer);
1073                                 if (XactIsoLevel == XACT_SERIALIZABLE)
1074                                         elog(ERROR, "Can't serialize access due to concurrent update");
1075                                 else if (!(ItemPointerEquals(&(tuple.t_self), tid)))
1076                                 {
1077                                         TupleTableSlot *epqslot = EvalPlanQual(estate,
1078                                                                                          relinfo->ri_RangeTableIndex,
1079                                                                                                                 &(tuple.t_self));
1080
1081                                         if (!(TupIsNull(epqslot)))
1082                                         {
1083                                                 *tid = tuple.t_self;
1084                                                 *newSlot = epqslot;
1085                                                 goto ltrmark;
1086                                         }
1087                                 }
1088
1089                                 /*
1090                                  * if tuple was deleted or PlanQual failed for updated
1091                                  * tuple - we have not process this tuple!
1092                                  */
1093                                 return (NULL);
1094
1095                         default:
1096                                 ReleaseBuffer(buffer);
1097                                 elog(ERROR, "Unknown status %u from heap_mark4update", test);
1098                                 return (NULL);
1099                 }
1100         }
1101         else
1102         {
1103                 PageHeader      dp;
1104                 ItemId          lp;
1105
1106                 buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
1107
1108                 if (!BufferIsValid(buffer))
1109                         elog(ERROR, "GetTupleForTrigger: failed ReadBuffer");
1110
1111                 dp = (PageHeader) BufferGetPage(buffer);
1112                 lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid));
1113
1114                 Assert(ItemIdIsUsed(lp));
1115
1116                 tuple.t_datamcxt = NULL;
1117                 tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
1118                 tuple.t_len = ItemIdGetLength(lp);
1119                 tuple.t_self = *tid;
1120         }
1121
1122         result = heap_copytuple(&tuple);
1123         ReleaseBuffer(buffer);
1124
1125         return result;
1126 }
1127
1128
1129 /* ----------
1130  * Deferred trigger stuff
1131  * ----------
1132  */
1133
1134
1135 /* ----------
1136  * Internal data to the deferred trigger mechanism is held
1137  * during entire session in a global context created at startup and
1138  * over statements/commands in a separate context which
1139  * is created at transaction start and destroyed at transaction end.
1140  * ----------
1141  */
1142 static MemoryContext deftrig_gcxt = NULL;
1143 static MemoryContext deftrig_cxt = NULL;
1144
1145 /* ----------
1146  * Global data that tells which triggers are actually in
1147  * state IMMEDIATE or DEFERRED.
1148  * ----------
1149  */
1150 static bool deftrig_dfl_all_isset = false;
1151 static bool deftrig_dfl_all_isdeferred = false;
1152 static List *deftrig_dfl_trigstates = NIL;
1153
1154 static bool deftrig_all_isset;
1155 static bool deftrig_all_isdeferred;
1156 static List *deftrig_trigstates;
1157
1158 /* ----------
1159  * The list of pending deferred trigger events during the current transaction.
1160  *
1161  * deftrig_events is the head, deftrig_event_tail is the last entry.
1162  * Because this can grow pretty large, we don't use separate List nodes,
1163  * but instead thread the list through the dte_next fields of the member
1164  * nodes.  Saves just a few bytes per entry, but that adds up.
1165  *
1166  * XXX Need to be able to shove this data out to a file if it grows too
1167  *         large...
1168  * ----------
1169  */
1170 static DeferredTriggerEvent deftrig_events;
1171 static DeferredTriggerEvent deftrig_event_tail;
1172
1173
1174 /* ----------
1175  * deferredTriggerCheckState()
1176  *
1177  *      Returns true if the trigger identified by tgoid is actually
1178  *      in state DEFERRED.
1179  * ----------
1180  */
1181 static bool
1182 deferredTriggerCheckState(Oid tgoid, int32 itemstate)
1183 {
1184         MemoryContext oldcxt;
1185         List       *sl;
1186         DeferredTriggerStatus trigstate;
1187
1188         /*
1189          * Not deferrable triggers (i.e. normal AFTER ROW triggers and
1190          * constraints declared NOT DEFERRABLE, the state is allways false.
1191          */
1192         if ((itemstate & TRIGGER_DEFERRED_DEFERRABLE) == 0)
1193                 return false;
1194
1195         /*
1196          * Lookup if we know an individual state for this trigger
1197          */
1198         foreach(sl, deftrig_trigstates)
1199         {
1200                 trigstate = (DeferredTriggerStatus) lfirst(sl);
1201                 if (trigstate->dts_tgoid == tgoid)
1202                         return trigstate->dts_tgisdeferred;
1203         }
1204
1205         /*
1206          * No individual state known - so if the user issued a SET CONSTRAINT
1207          * ALL ..., we return that instead of the triggers default state.
1208          */
1209         if (deftrig_all_isset)
1210                 return deftrig_all_isdeferred;
1211
1212         /*
1213          * No ALL state known either, remember the default state as the
1214          * current and return that.
1215          */
1216         oldcxt = MemoryContextSwitchTo(deftrig_cxt);
1217
1218         trigstate = (DeferredTriggerStatus)
1219                 palloc(sizeof(DeferredTriggerStatusData));
1220         trigstate->dts_tgoid = tgoid;
1221         trigstate->dts_tgisdeferred =
1222                 ((itemstate & TRIGGER_DEFERRED_INITDEFERRED) != 0);
1223         deftrig_trigstates = lappend(deftrig_trigstates, trigstate);
1224
1225         MemoryContextSwitchTo(oldcxt);
1226
1227         return trigstate->dts_tgisdeferred;
1228 }
1229
1230
1231 /* ----------
1232  * deferredTriggerAddEvent()
1233  *
1234  *      Add a new trigger event to the queue.
1235  * ----------
1236  */
1237 static void
1238 deferredTriggerAddEvent(DeferredTriggerEvent event)
1239 {
1240         /*
1241          * Since the event list could grow quite long, we keep track of the
1242          * list tail and append there, rather than just doing a stupid
1243          * "lappend". This avoids O(N^2) behavior for large numbers of events.
1244          */
1245         event->dte_next = NULL;
1246         if (deftrig_event_tail == NULL)
1247         {
1248                 /* first list entry */
1249                 deftrig_events = event;
1250                 deftrig_event_tail = event;
1251         }
1252         else
1253         {
1254                 deftrig_event_tail->dte_next = event;
1255                 deftrig_event_tail = event;
1256         }
1257 }
1258
1259
1260 /* ----------
1261  * DeferredTriggerExecute()
1262  *
1263  *      Fetch the required tuples back from the heap and fire one
1264  *      single trigger function.
1265  *
1266  *      Frequently, this will be fired many times in a row for triggers of
1267  *      a single relation.      Therefore, we cache the open relation and provide
1268  *      fmgr lookup cache space at the caller level.
1269  *
1270  *      event: event currently being fired.
1271  *      itemno: item within event currently being fired.
1272  *      rel: open relation for event.
1273  *      finfo: array of fmgr lookup cache entries (one per trigger of relation).
1274  *      per_tuple_context: memory context to call trigger function in.
1275  * ----------
1276  */
1277 static void
1278 DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
1279                                            Relation rel, FmgrInfo *finfo,
1280                                            MemoryContext per_tuple_context)
1281 {
1282         Oid                     tgoid = event->dte_item[itemno].dti_tgoid;
1283         TriggerDesc *trigdesc = rel->trigdesc;
1284         TriggerData LocTriggerData;
1285         HeapTupleData oldtuple;
1286         HeapTupleData newtuple;
1287         HeapTuple       rettuple;
1288         Buffer          oldbuffer;
1289         Buffer          newbuffer;
1290         int                     tgindx;
1291
1292         /*
1293          * Fetch the required OLD and NEW tuples.
1294          */
1295         if (ItemPointerIsValid(&(event->dte_oldctid)))
1296         {
1297                 ItemPointerCopy(&(event->dte_oldctid), &(oldtuple.t_self));
1298                 heap_fetch(rel, SnapshotAny, &oldtuple, &oldbuffer, NULL);
1299                 if (!oldtuple.t_data)
1300                         elog(ERROR, "DeferredTriggerExecute: failed to fetch old tuple");
1301         }
1302
1303         if (ItemPointerIsValid(&(event->dte_newctid)))
1304         {
1305                 ItemPointerCopy(&(event->dte_newctid), &(newtuple.t_self));
1306                 heap_fetch(rel, SnapshotAny, &newtuple, &newbuffer, NULL);
1307                 if (!newtuple.t_data)
1308                         elog(ERROR, "DeferredTriggerExecute: failed to fetch new tuple");
1309         }
1310
1311         /*
1312          * Setup the trigger information
1313          */
1314         LocTriggerData.type = T_TriggerData;
1315         LocTriggerData.tg_event = (event->dte_event & TRIGGER_EVENT_OPMASK) |
1316                 TRIGGER_EVENT_ROW;
1317         LocTriggerData.tg_relation = rel;
1318
1319         LocTriggerData.tg_trigger = NULL;
1320         for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
1321         {
1322                 if (trigdesc->triggers[tgindx].tgoid == tgoid)
1323                 {
1324                         LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
1325                         break;
1326                 }
1327         }
1328         if (LocTriggerData.tg_trigger == NULL)
1329                 elog(ERROR, "DeferredTriggerExecute: can't find trigger %u", tgoid);
1330
1331         switch (event->dte_event & TRIGGER_EVENT_OPMASK)
1332         {
1333                 case TRIGGER_EVENT_INSERT:
1334                         LocTriggerData.tg_trigtuple = &newtuple;
1335                         LocTriggerData.tg_newtuple = NULL;
1336                         break;
1337
1338                 case TRIGGER_EVENT_UPDATE:
1339                         LocTriggerData.tg_trigtuple = &oldtuple;
1340                         LocTriggerData.tg_newtuple = &newtuple;
1341                         break;
1342
1343                 case TRIGGER_EVENT_DELETE:
1344                         LocTriggerData.tg_trigtuple = &oldtuple;
1345                         LocTriggerData.tg_newtuple = NULL;
1346                         break;
1347         }
1348
1349         /*
1350          * Call the trigger and throw away an eventually returned updated
1351          * tuple.
1352          */
1353         rettuple = ExecCallTriggerFunc(&LocTriggerData,
1354                                                                    finfo + tgindx,
1355                                                                    per_tuple_context);
1356         if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple)
1357                 heap_freetuple(rettuple);
1358
1359         /*
1360          * Might have been a referential integrity constraint trigger. Reset
1361          * the snapshot overriding flag.
1362          */
1363         ReferentialIntegritySnapshotOverride = false;
1364
1365         /*
1366          * Release buffers
1367          */
1368         if (ItemPointerIsValid(&(event->dte_oldctid)))
1369                 ReleaseBuffer(oldbuffer);
1370         if (ItemPointerIsValid(&(event->dte_newctid)))
1371                 ReleaseBuffer(newbuffer);
1372 }
1373
1374
1375 /* ----------
1376  * deferredTriggerInvokeEvents()
1377  *
1378  *      Scan the event queue for not yet invoked triggers. Check if they
1379  *      should be invoked now and do so.
1380  * ----------
1381  */
1382 static void
1383 deferredTriggerInvokeEvents(bool immediate_only)
1384 {
1385         DeferredTriggerEvent event,
1386                                 prev_event = NULL;
1387         MemoryContext per_tuple_context;
1388         Relation        rel = NULL;
1389         FmgrInfo   *finfo = NULL;
1390
1391         /*
1392          * If immediate_only is true, we remove fully-processed events from
1393          * the event queue to recycle space.  If immediate_only is false,
1394          * we are going to discard the whole event queue on return anyway,
1395          * so no need to bother with "retail" pfree's.
1396          *
1397          * In a scenario with many commands in a transaction and many
1398          * deferred-to-end-of-transaction triggers, it could get annoying
1399          * to rescan all the deferred triggers at each command end.
1400          * To speed this up, we could remember the actual end of the queue at
1401          * EndQuery and examine only events that are newer. On state changes
1402          * we simply reset the saved position to the beginning of the queue
1403          * and process all events once with the new states.
1404          */
1405
1406         /* Make a per-tuple memory context for trigger function calls */
1407         per_tuple_context =
1408                 AllocSetContextCreate(CurrentMemoryContext,
1409                                                           "DeferredTriggerTupleContext",
1410                                                           ALLOCSET_DEFAULT_MINSIZE,
1411                                                           ALLOCSET_DEFAULT_INITSIZE,
1412                                                           ALLOCSET_DEFAULT_MAXSIZE);
1413
1414         event = deftrig_events;
1415         while (event != NULL)
1416         {
1417                 bool            still_deferred_ones = false;
1418                 DeferredTriggerEvent next_event;
1419                 int                     i;
1420
1421                 /*
1422                  * Check if event is already completely done.
1423                  */
1424                 if (! (event->dte_event & (TRIGGER_DEFERRED_DONE |
1425                                                                    TRIGGER_DEFERRED_CANCELED)))
1426                 {
1427                         MemoryContextReset(per_tuple_context);
1428
1429                         /*
1430                          * Check each trigger item in the event.
1431                          */
1432                         for (i = 0; i < event->dte_n_items; i++)
1433                         {
1434                                 if (event->dte_item[i].dti_state & TRIGGER_DEFERRED_DONE)
1435                                         continue;
1436
1437                                 /*
1438                                  * This trigger item hasn't been called yet. Check if we
1439                                  * should call it now.
1440                                  */
1441                                 if (immediate_only &&
1442                                         deferredTriggerCheckState(event->dte_item[i].dti_tgoid,
1443                                                                                           event->dte_item[i].dti_state))
1444                                 {
1445                                         still_deferred_ones = true;
1446                                         continue;
1447                                 }
1448
1449                                 /*
1450                                  * So let's fire it... but first, open the correct relation
1451                                  * if this is not the same relation as before.
1452                                  */
1453                                 if (rel == NULL || rel->rd_id != event->dte_relid)
1454                                 {
1455                                         if (rel)
1456                                                 heap_close(rel, NoLock);
1457                                         if (finfo)
1458                                                 pfree(finfo);
1459
1460                                         /*
1461                                          * We assume that an appropriate lock is still held by the
1462                                          * executor, so grab no new lock here.
1463                                          */
1464                                         rel = heap_open(event->dte_relid, NoLock);
1465
1466                                         /*
1467                                          * Allocate space to cache fmgr lookup info for triggers
1468                                          * of this relation.
1469                                          */
1470                                         finfo = (FmgrInfo *)
1471                                                 palloc(rel->trigdesc->numtriggers * sizeof(FmgrInfo));
1472                                         MemSet(finfo, 0,
1473                                                    rel->trigdesc->numtriggers * sizeof(FmgrInfo));
1474                                 }
1475
1476                                 DeferredTriggerExecute(event, i, rel, finfo,
1477                                                                            per_tuple_context);
1478
1479                                 event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
1480                         } /* end loop over items within event */
1481                 }
1482
1483                 /*
1484                  * If it's now completely done, throw it away.
1485                  *
1486                  * NB: it's possible the trigger calls above added more events to the
1487                  * queue, or that calls we will do later will want to add more,
1488                  * so we have to be careful about maintaining list validity here.
1489                  */
1490                 next_event = event->dte_next;
1491
1492                 if (still_deferred_ones)
1493                 {
1494                         /* Not done, keep in list */
1495                         prev_event = event;
1496                 }
1497                 else
1498                 {
1499                         /* Done */
1500                         if (immediate_only)
1501                         {
1502                                 /* delink it from list and free it */
1503                                 if (prev_event)
1504                                         prev_event->dte_next = next_event;
1505                                 else
1506                                         deftrig_events = next_event;
1507                                 pfree(event);
1508                         }
1509                         else
1510                         {
1511                                 /*
1512                                  * We will clean up later, but just for paranoia's sake,
1513                                  * mark the event done.
1514                                  */
1515                                 event->dte_event |= TRIGGER_DEFERRED_DONE;
1516                         }
1517                 }
1518
1519                 event = next_event;
1520         }
1521
1522         /* Update list tail pointer in case we just deleted tail event */
1523         deftrig_event_tail = prev_event;
1524
1525         /* Release working resources */
1526         if (rel)
1527                 heap_close(rel, NoLock);
1528         if (finfo)
1529                 pfree(finfo);
1530         MemoryContextDelete(per_tuple_context);
1531 }
1532
1533
1534 /* ----------
1535  * DeferredTriggerInit()
1536  *
1537  *      Initialize the deferred trigger mechanism. This is called during
1538  *      backend startup and is guaranteed to be before the first of all
1539  *      transactions.
1540  * ----------
1541  */
1542 void
1543 DeferredTriggerInit(void)
1544 {
1545         /*
1546          * Since this context will never be reset, give it a minsize of 0.
1547          * This avoids using any memory if the session never stores anything.
1548          */
1549         deftrig_gcxt = AllocSetContextCreate(TopMemoryContext,
1550                                                                                  "DeferredTriggerSession",
1551                                                                                  0,
1552                                                                                  ALLOCSET_DEFAULT_INITSIZE,
1553                                                                                  ALLOCSET_DEFAULT_MAXSIZE);
1554 }
1555
1556
1557 /* ----------
1558  * DeferredTriggerBeginXact()
1559  *
1560  *      Called at transaction start (either BEGIN or implicit for single
1561  *      statement outside of transaction block).
1562  * ----------
1563  */
1564 void
1565 DeferredTriggerBeginXact(void)
1566 {
1567         MemoryContext oldcxt;
1568         List       *l;
1569         DeferredTriggerStatus dflstat;
1570         DeferredTriggerStatus stat;
1571
1572         if (deftrig_cxt != NULL)
1573                 elog(ERROR,
1574                    "DeferredTriggerBeginXact() called while inside transaction");
1575
1576         /*
1577          * Create the per transaction memory context and copy all states from
1578          * the per session context to here.  Set the minsize to 0 to avoid
1579          * wasting memory if there is no deferred trigger data.
1580          */
1581         deftrig_cxt = AllocSetContextCreate(TopTransactionContext,
1582                                                                                 "DeferredTriggerXact",
1583                                                                                 0,
1584                                                                                 ALLOCSET_DEFAULT_INITSIZE,
1585                                                                                 ALLOCSET_DEFAULT_MAXSIZE);
1586         oldcxt = MemoryContextSwitchTo(deftrig_cxt);
1587
1588         deftrig_all_isset = deftrig_dfl_all_isset;
1589         deftrig_all_isdeferred = deftrig_dfl_all_isdeferred;
1590
1591         deftrig_trigstates = NIL;
1592         foreach(l, deftrig_dfl_trigstates)
1593         {
1594                 dflstat = (DeferredTriggerStatus) lfirst(l);
1595                 stat = (DeferredTriggerStatus)
1596                         palloc(sizeof(DeferredTriggerStatusData));
1597
1598                 stat->dts_tgoid = dflstat->dts_tgoid;
1599                 stat->dts_tgisdeferred = dflstat->dts_tgisdeferred;
1600
1601                 deftrig_trigstates = lappend(deftrig_trigstates, stat);
1602         }
1603
1604         MemoryContextSwitchTo(oldcxt);
1605
1606         deftrig_events = NULL;
1607         deftrig_event_tail = NULL;
1608 }
1609
1610
1611 /* ----------
1612  * DeferredTriggerEndQuery()
1613  *
1614  *      Called after one query sent down by the user has completely been
1615  *      processed. At this time we invoke all outstanding IMMEDIATE triggers.
1616  * ----------
1617  */
1618 void
1619 DeferredTriggerEndQuery(void)
1620 {
1621         /*
1622          * Ignore call if we aren't in a transaction.
1623          */
1624         if (deftrig_cxt == NULL)
1625                 return;
1626
1627         deferredTriggerInvokeEvents(true);
1628 }
1629
1630
1631 /* ----------
1632  * DeferredTriggerEndXact()
1633  *
1634  *      Called just before the current transaction is committed. At this
1635  *      time we invoke all DEFERRED triggers and tidy up.
1636  * ----------
1637  */
1638 void
1639 DeferredTriggerEndXact(void)
1640 {
1641         /*
1642          * Ignore call if we aren't in a transaction.
1643          */
1644         if (deftrig_cxt == NULL)
1645                 return;
1646
1647         deferredTriggerInvokeEvents(false);
1648
1649         MemoryContextDelete(deftrig_cxt);
1650         deftrig_cxt = NULL;
1651 }
1652
1653
1654 /* ----------
1655  * DeferredTriggerAbortXact()
1656  *
1657  *      The current transaction has entered the abort state.
1658  *      All outstanding triggers are canceled so we simply throw
1659  *      away anything we know.
1660  * ----------
1661  */
1662 void
1663 DeferredTriggerAbortXact(void)
1664 {
1665         /*
1666          * Ignore call if we aren't in a transaction.
1667          */
1668         if (deftrig_cxt == NULL)
1669                 return;
1670
1671         MemoryContextDelete(deftrig_cxt);
1672         deftrig_cxt = NULL;
1673 }
1674
1675
1676 /* ----------
1677  * DeferredTriggerSetState()
1678  *
1679  *      Called for the users SET CONSTRAINTS ... utility command.
1680  * ----------
1681  */
1682 void
1683 DeferredTriggerSetState(ConstraintsSetStmt *stmt)
1684 {
1685         Relation        tgrel;
1686         List       *l;
1687         List       *ls;
1688         List       *loid = NIL;
1689         MemoryContext oldcxt;
1690         bool            found;
1691         DeferredTriggerStatus state;
1692
1693         /*
1694          * Handle SET CONSTRAINTS ALL ...
1695          */
1696         if (stmt->constraints == NIL)
1697         {
1698                 if (!IsTransactionBlock())
1699                 {
1700                         /*
1701                          * ... outside of a transaction block
1702                          *
1703                          * Drop all information about individual trigger states per
1704                          * session.
1705                          */
1706                         l = deftrig_dfl_trigstates;
1707                         while (l != NIL)
1708                         {
1709                                 List       *next = lnext(l);
1710
1711                                 pfree(lfirst(l));
1712                                 pfree(l);
1713                                 l = next;
1714                         }
1715                         deftrig_dfl_trigstates = NIL;
1716
1717                         /*
1718                          * Set the session ALL state to known.
1719                          */
1720                         deftrig_dfl_all_isset = true;
1721                         deftrig_dfl_all_isdeferred = stmt->deferred;
1722
1723                         return;
1724                 }
1725                 else
1726                 {
1727                         /*
1728                          * ... inside of a transaction block
1729                          *
1730                          * Drop all information about individual trigger states per
1731                          * transaction.
1732                          */
1733                         l = deftrig_trigstates;
1734                         while (l != NIL)
1735                         {
1736                                 List       *next = lnext(l);
1737
1738                                 pfree(lfirst(l));
1739                                 pfree(l);
1740                                 l = next;
1741                         }
1742                         deftrig_trigstates = NIL;
1743
1744                         /*
1745                          * Set the per transaction ALL state to known.
1746                          */
1747                         deftrig_all_isset = true;
1748                         deftrig_all_isdeferred = stmt->deferred;
1749
1750                         return;
1751                 }
1752         }
1753
1754         /* ----------
1755          * Handle SET CONSTRAINTS constraint-name [, ...]
1756          * First lookup all trigger Oid's for the constraint names.
1757          * ----------
1758          */
1759         tgrel = heap_openr(TriggerRelationName, AccessShareLock);
1760
1761         foreach(l, stmt->constraints)
1762         {
1763                 char       *cname = strVal(lfirst(l));
1764                 ScanKeyData skey;
1765                 SysScanDesc     tgscan;
1766                 HeapTuple       htup;
1767
1768                 /*
1769                  * Check that only named constraints are set explicitly
1770                  */
1771                 if (strlen(cname) == 0)
1772                         elog(ERROR, "unnamed constraints cannot be set explicitly");
1773
1774                 /*
1775                  * Setup to scan pg_trigger by tgconstrname ...
1776                  */
1777                 ScanKeyEntryInitialize(&skey,
1778                                                            (bits16) 0x0,
1779                                                            (AttrNumber) Anum_pg_trigger_tgconstrname,
1780                                                            (RegProcedure) F_NAMEEQ,
1781                                                            PointerGetDatum(cname));
1782
1783                 tgscan = systable_beginscan(tgrel, TriggerConstrNameIndex, true,
1784                                                                         SnapshotNow, 1, &skey);
1785
1786                 /*
1787                  * ... and search for the constraint trigger row
1788                  */
1789                 found = false;
1790
1791                 while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
1792                 {
1793                         Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
1794                         Oid                     constr_oid;
1795
1796                         /*
1797                          * If we found some, check that they fit the deferrability but
1798                          * skip ON <event> RESTRICT ones, since they are silently
1799                          * never deferrable.
1800                          */
1801                         if (stmt->deferred && !pg_trigger->tgdeferrable &&
1802                                 pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD &&
1803                                 pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL)
1804                                 elog(ERROR, "Constraint '%s' is not deferrable",
1805                                          cname);
1806
1807                         constr_oid = htup->t_data->t_oid;
1808                         loid = lappendi(loid, constr_oid);
1809                         found = true;
1810                 }
1811
1812                 systable_endscan(tgscan);
1813
1814                 /*
1815                  * Not found ?
1816                  */
1817                 if (!found)
1818                         elog(ERROR, "Constraint '%s' does not exist", cname);
1819         }
1820         heap_close(tgrel, AccessShareLock);
1821
1822         if (!IsTransactionBlock())
1823         {
1824                 /*
1825                  * Outside of a transaction block set the trigger states of
1826                  * individual triggers on session level.
1827                  */
1828                 oldcxt = MemoryContextSwitchTo(deftrig_gcxt);
1829
1830                 foreach(l, loid)
1831                 {
1832                         found = false;
1833                         foreach(ls, deftrig_dfl_trigstates)
1834                         {
1835                                 state = (DeferredTriggerStatus) lfirst(ls);
1836                                 if (state->dts_tgoid == (Oid) lfirsti(l))
1837                                 {
1838                                         state->dts_tgisdeferred = stmt->deferred;
1839                                         found = true;
1840                                         break;
1841                                 }
1842                         }
1843                         if (!found)
1844                         {
1845                                 state = (DeferredTriggerStatus)
1846                                         palloc(sizeof(DeferredTriggerStatusData));
1847                                 state->dts_tgoid = (Oid) lfirsti(l);
1848                                 state->dts_tgisdeferred = stmt->deferred;
1849
1850                                 deftrig_dfl_trigstates =
1851                                         lappend(deftrig_dfl_trigstates, state);
1852                         }
1853                 }
1854
1855                 MemoryContextSwitchTo(oldcxt);
1856
1857                 return;
1858         }
1859         else
1860         {
1861                 /*
1862                  * Inside of a transaction block set the trigger states of
1863                  * individual triggers on transaction level.
1864                  */
1865                 oldcxt = MemoryContextSwitchTo(deftrig_cxt);
1866
1867                 foreach(l, loid)
1868                 {
1869                         found = false;
1870                         foreach(ls, deftrig_trigstates)
1871                         {
1872                                 state = (DeferredTriggerStatus) lfirst(ls);
1873                                 if (state->dts_tgoid == (Oid) lfirsti(l))
1874                                 {
1875                                         state->dts_tgisdeferred = stmt->deferred;
1876                                         found = true;
1877                                         break;
1878                                 }
1879                         }
1880                         if (!found)
1881                         {
1882                                 state = (DeferredTriggerStatus)
1883                                         palloc(sizeof(DeferredTriggerStatusData));
1884                                 state->dts_tgoid = (Oid) lfirsti(l);
1885                                 state->dts_tgisdeferred = stmt->deferred;
1886
1887                                 deftrig_trigstates =
1888                                         lappend(deftrig_trigstates, state);
1889                         }
1890                 }
1891
1892                 MemoryContextSwitchTo(oldcxt);
1893
1894                 return;
1895         }
1896 }
1897
1898
1899 /* ----------
1900  * DeferredTriggerSaveEvent()
1901  *
1902  *      Called by ExecAR...Triggers() to add the event to the queue.
1903  *
1904  *      NOTE: should be called only if we've determined that an event must
1905  *      be added to the queue.
1906  * ----------
1907  */
1908 static void
1909 DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
1910                                                  HeapTuple oldtup, HeapTuple newtup)
1911 {
1912         Relation        rel = relinfo->ri_RelationDesc;
1913         TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
1914         MemoryContext oldcxt;
1915         DeferredTriggerEvent new_event;
1916         int                     new_size;
1917         int                     i;
1918         int                     ntriggers;
1919         int                *tgindx;
1920         ItemPointerData oldctid;
1921         ItemPointerData newctid;
1922         TriggerData LocTriggerData;
1923
1924         if (deftrig_cxt == NULL)
1925                 elog(ERROR,
1926                          "DeferredTriggerSaveEvent() called outside of transaction");
1927
1928         /*
1929          * Get the CTID's of OLD and NEW
1930          */
1931         if (oldtup != NULL)
1932                 ItemPointerCopy(&(oldtup->t_self), &(oldctid));
1933         else
1934                 ItemPointerSetInvalid(&(oldctid));
1935         if (newtup != NULL)
1936                 ItemPointerCopy(&(newtup->t_self), &(newctid));
1937         else
1938                 ItemPointerSetInvalid(&(newctid));
1939
1940         /*
1941          * Create a new event
1942          */
1943         oldcxt = MemoryContextSwitchTo(deftrig_cxt);
1944
1945         ntriggers = trigdesc->n_after_row[event];
1946         tgindx = trigdesc->tg_after_row[event];
1947         new_size = offsetof(DeferredTriggerEventData, dte_item[0]) +
1948                 ntriggers * sizeof(DeferredTriggerEventItem);
1949
1950         new_event = (DeferredTriggerEvent) palloc(new_size);
1951         new_event->dte_next = NULL;
1952         new_event->dte_event = event & TRIGGER_EVENT_OPMASK;
1953         new_event->dte_relid = rel->rd_id;
1954         ItemPointerCopy(&oldctid, &(new_event->dte_oldctid));
1955         ItemPointerCopy(&newctid, &(new_event->dte_newctid));
1956         new_event->dte_n_items = ntriggers;
1957         for (i = 0; i < ntriggers; i++)
1958         {
1959                 Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
1960
1961                 new_event->dte_item[i].dti_tgoid = trigger->tgoid;
1962                 new_event->dte_item[i].dti_state =
1963                         ((trigger->tgdeferrable) ?
1964                          TRIGGER_DEFERRED_DEFERRABLE : 0) |
1965                         ((trigger->tginitdeferred) ?
1966                          TRIGGER_DEFERRED_INITDEFERRED : 0) |
1967                         ((trigdesc->n_before_row[event] > 0) ?
1968                          TRIGGER_DEFERRED_HAS_BEFORE : 0);
1969         }
1970
1971         MemoryContextSwitchTo(oldcxt);
1972
1973         switch (event & TRIGGER_EVENT_OPMASK)
1974         {
1975                 case TRIGGER_EVENT_INSERT:
1976                         /* nothing to do */
1977                         break;
1978
1979                 case TRIGGER_EVENT_UPDATE:
1980                         /*
1981                          * Check if one of the referenced keys is changed.
1982                          */
1983                         for (i = 0; i < ntriggers; i++)
1984                         {
1985                                 Trigger    *trigger = &trigdesc->triggers[tgindx[i]];
1986                                 bool            is_ri_trigger;
1987                                 bool            key_unchanged;
1988
1989                                 /*
1990                                  * We are interested in RI_FKEY triggers only.
1991                                  */
1992                                 switch (trigger->tgfoid)
1993                                 {
1994                                         case F_RI_FKEY_NOACTION_UPD:
1995                                         case F_RI_FKEY_CASCADE_UPD:
1996                                         case F_RI_FKEY_RESTRICT_UPD:
1997                                         case F_RI_FKEY_SETNULL_UPD:
1998                                         case F_RI_FKEY_SETDEFAULT_UPD:
1999                                                 is_ri_trigger = true;
2000                                                 break;
2001
2002                                         default:
2003                                                 is_ri_trigger = false;
2004                                                 break;
2005                                 }
2006                                 if (!is_ri_trigger)
2007                                         continue;
2008
2009                                 LocTriggerData.type = T_TriggerData;
2010                                 LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE;
2011                                 LocTriggerData.tg_relation = rel;
2012                                 LocTriggerData.tg_trigtuple = oldtup;
2013                                 LocTriggerData.tg_newtuple = newtup;
2014                                 LocTriggerData.tg_trigger = trigger;
2015
2016                                 key_unchanged = RI_FKey_keyequal_upd(&LocTriggerData);
2017
2018                                 if (key_unchanged)
2019                                 {
2020                                         /*
2021                                          * The key hasn't changed, so no need later to invoke
2022                                          * the trigger at all.
2023                                          */
2024                                         new_event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
2025                                 }
2026                         }
2027
2028                         break;
2029
2030                 case TRIGGER_EVENT_DELETE:
2031                         /* nothing to do */
2032                         break;
2033         }
2034
2035         /*
2036          * Add the new event to the queue.
2037          */
2038         deferredTriggerAddEvent(new_event);
2039 }