OSDN Git Service

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