1 /*-------------------------------------------------------------------------
4 * PostgreSQL TRIGGERs support code.
6 *-------------------------------------------------------------------------
11 #include "access/genam.h"
12 #include "access/heapam.h"
13 #include "catalog/catalog.h"
14 #include "catalog/catname.h"
15 #include "catalog/indexing.h"
16 #include "catalog/pg_language.h"
17 #include "catalog/pg_proc.h"
18 #include "catalog/pg_trigger.h"
19 #include "commands/comment.h"
20 #include "commands/trigger.h"
21 #include "executor/executor.h"
22 #include "miscadmin.h"
23 #include "utils/acl.h"
24 #include "utils/builtins.h"
25 #include "utils/inval.h"
26 #include "utils/syscache.h"
28 DLLIMPORT TriggerData *CurrentTriggerData = NULL;
30 void RelationBuildTriggers(Relation relation);
31 void FreeTriggerDesc(Relation relation);
33 static void DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger);
34 static HeapTuple GetTupleForTrigger(EState *estate, ItemPointer tid,
35 TupleTableSlot **newSlot);
37 extern GlobalMemory CacheCxt;
40 CreateTrigger(CreateTrigStmt *stmt)
43 int16 tgattr[8] = {0};
44 Datum values[Natts_pg_trigger];
45 char nulls[Natts_pg_trigger];
52 Relation idescs[Num_pg_trigger_indices];
53 Relation ridescs[Num_pg_class_indices];
58 char constrtrigname[NAMEDATALEN];
59 char *constrname = "";
62 if (!allowSystemTableMods && IsSystemRelationName(stmt->relname))
63 elog(ERROR, "CreateTrigger: can't create trigger for system relation %s", stmt->relname);
66 if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME))
67 elog(ERROR, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
71 * If trigger is a constraint, user trigger name as constraint
72 * name and build a unique trigger name instead.
75 if (stmt->isconstraint)
77 constrname = stmt->trigname;
78 stmt->trigname = constrtrigname;
79 sprintf(constrtrigname, "RI_ConstraintTrigger_%d", newoid());
81 if (strcmp(stmt->constrrelname, "") == 0)
85 rel = heap_openr(stmt->constrrelname, NoLock);
87 elog(ERROR, "table \"%s\" does not exist",
89 constrrelid = rel->rd_id;
90 heap_close(rel, NoLock);
94 rel = heap_openr(stmt->relname, AccessExclusiveLock);
96 TRIGGER_CLEAR_TYPE(tgtype);
98 TRIGGER_SETT_BEFORE(tgtype);
100 TRIGGER_SETT_ROW(tgtype);
102 elog(ERROR, "CreateTrigger: STATEMENT triggers are unimplemented, yet");
104 for (i = 0; i < 3 && stmt->actions[i]; i++)
106 switch (stmt->actions[i])
109 if (TRIGGER_FOR_INSERT(tgtype))
110 elog(ERROR, "CreateTrigger: double INSERT event specified");
111 TRIGGER_SETT_INSERT(tgtype);
114 if (TRIGGER_FOR_DELETE(tgtype))
115 elog(ERROR, "CreateTrigger: double DELETE event specified");
116 TRIGGER_SETT_DELETE(tgtype);
119 if (TRIGGER_FOR_UPDATE(tgtype))
120 elog(ERROR, "CreateTrigger: double UPDATE event specified");
121 TRIGGER_SETT_UPDATE(tgtype);
124 elog(ERROR, "CreateTrigger: unknown event specified");
129 /* Scan pg_trigger */
130 tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
131 ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
132 F_OIDEQ, RelationGetRelid(rel));
133 tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
134 while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0)))
136 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
138 if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
139 elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s",
140 stmt->trigname, stmt->relname);
144 heap_endscan(tgscan);
146 MemSet(fargtypes, 0, 8 * sizeof(Oid));
147 tuple = SearchSysCacheTuple(PROCNAME,
148 PointerGetDatum(stmt->funcname),
150 PointerGetDatum(fargtypes),
152 if (!HeapTupleIsValid(tuple) ||
153 ((Form_pg_proc) GETSTRUCT(tuple))->pronargs != 0)
154 elog(ERROR, "CreateTrigger: function %s() does not exist",
156 if (((Form_pg_proc) GETSTRUCT(tuple))->prorettype != 0)
157 elog(ERROR, "CreateTrigger: function %s() must return OPAQUE",
159 if (((Form_pg_proc) GETSTRUCT(tuple))->prolang != ClanguageId &&
160 ((Form_pg_proc) GETSTRUCT(tuple))->prolang != INTERNALlanguageId)
164 langTup = SearchSysCacheTuple(LANGOID,
165 ObjectIdGetDatum(((Form_pg_proc) GETSTRUCT(tuple))->prolang),
167 if (!HeapTupleIsValid(langTup))
168 elog(ERROR, "CreateTrigger: cache lookup for PL failed");
170 if (((Form_pg_language) GETSTRUCT(langTup))->lanispl == false)
171 elog(ERROR, "CreateTrigger: only builtin, C and PL functions are supported");
174 MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char));
176 values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
177 values[Anum_pg_trigger_tgname - 1] = NameGetDatum(namein(stmt->trigname));
178 values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(tuple->t_data->t_oid);
179 values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
181 values[Anum_pg_trigger_tgenabled - 1] = true;
182 values[Anum_pg_trigger_tgisconstraint - 1] = stmt->isconstraint;
183 values[Anum_pg_trigger_tgconstrname - 1] = PointerGetDatum(constrname);;
184 values[Anum_pg_trigger_tgconstrrelid - 1] = constrrelid;
185 values[Anum_pg_trigger_tgdeferrable - 1] = stmt->deferrable;
186 values[Anum_pg_trigger_tginitdeferred - 1] = stmt->initdeferred;
192 int16 nargs = length(stmt->args);
195 foreach(le, stmt->args)
197 char *ar = (char *) lfirst(le);
199 len += strlen(ar) + VARHDRSZ;
206 args = (char *) palloc(len + 1);
208 foreach(le, stmt->args)
210 char *s = (char *) lfirst(le);
211 char *d = args + strlen(args);
220 strcat(args, "\\000");
222 values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
223 values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain(args));
227 values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
228 values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain(""));
230 values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
232 tuple = heap_formtuple(tgrel->rd_att, values, nulls);
233 heap_insert(tgrel, tuple);
234 CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
235 CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
236 CatalogCloseIndices(Num_pg_trigger_indices, idescs);
238 heap_close(tgrel, RowExclusiveLock);
240 pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
241 pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
243 /* update pg_class */
244 pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
245 tuple = SearchSysCacheTupleCopy(RELNAME,
246 PointerGetDatum(stmt->relname),
248 if (!HeapTupleIsValid(tuple))
249 elog(ERROR, "CreateTrigger: relation %s not found in pg_class", stmt->relname);
251 ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;
252 RelationInvalidateHeapTuple(pgrel, tuple);
253 heap_update(pgrel, &tuple->t_self, tuple, NULL);
254 CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
255 CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
256 CatalogCloseIndices(Num_pg_class_indices, ridescs);
258 heap_close(pgrel, RowExclusiveLock);
260 CommandCounterIncrement();
261 oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
262 FreeTriggerDesc(rel);
263 rel->rd_rel->reltriggers = found + 1;
264 RelationBuildTriggers(rel);
265 MemoryContextSwitchTo(oldcxt);
266 /* Keep lock on target rel until end of xact */
267 heap_close(rel, NoLock);
271 DropTrigger(DropTrigStmt *stmt)
279 Relation ridescs[Num_pg_class_indices];
280 MemoryContext oldcxt;
285 if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME))
286 elog(ERROR, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]);
289 rel = heap_openr(stmt->relname, AccessExclusiveLock);
291 tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
292 ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
293 F_OIDEQ, RelationGetRelid(rel));
294 tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
295 while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0)))
297 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
299 if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
302 /*** Delete any comments associated with this trigger ***/
304 DeleteComments(tuple->t_data->t_oid);
306 heap_delete(tgrel, &tuple->t_self, NULL);
314 elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
315 stmt->trigname, stmt->relname);
317 elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s",
318 tgfound, stmt->trigname, stmt->relname);
319 heap_endscan(tgscan);
320 heap_close(tgrel, RowExclusiveLock);
322 /* update pg_class */
323 pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
324 tuple = SearchSysCacheTupleCopy(RELNAME,
325 PointerGetDatum(stmt->relname),
327 if (!HeapTupleIsValid(tuple))
328 elog(ERROR, "DropTrigger: relation %s not found in pg_class", stmt->relname);
330 ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found;
331 RelationInvalidateHeapTuple(pgrel, tuple);
332 heap_update(pgrel, &tuple->t_self, tuple, NULL);
333 CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
334 CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
335 CatalogCloseIndices(Num_pg_class_indices, ridescs);
337 heap_close(pgrel, RowExclusiveLock);
339 CommandCounterIncrement();
340 oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
341 FreeTriggerDesc(rel);
342 rel->rd_rel->reltriggers = found;
344 RelationBuildTriggers(rel);
345 MemoryContextSwitchTo(oldcxt);
346 /* Keep lock on target rel until end of xact */
347 heap_close(rel, NoLock);
351 RelationRemoveTriggers(Relation rel)
357 Form_pg_trigger pg_trigger;
359 tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
360 ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
361 F_OIDEQ, RelationGetRelid(rel));
363 tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
365 while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0))) {
367 /*** Delete any comments associated with this trigger ***/
369 DeleteComments(tup->t_data->t_oid);
371 heap_delete(tgrel, &tup->t_self, NULL);
375 heap_endscan(tgscan);
379 * Also drop all constraint triggers referencing this relation
382 ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgconstrrelid,
383 F_OIDEQ, RelationGetRelid(rel));
385 tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
386 while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0)))
391 pg_trigger = (Form_pg_trigger) GETSTRUCT(tup);
392 refrel = heap_open(pg_trigger->tgrelid, NoLock);
394 stmt.relname = pstrdup(RelationGetRelationName(refrel));
395 stmt.trigname = nameout(&pg_trigger->tgname);
400 pfree(stmt.trigname);
402 heap_close(refrel, NoLock);
404 heap_endscan(tgscan);
406 heap_close(tgrel, RowExclusiveLock);
410 RelationBuildTriggers(Relation relation)
412 TriggerDesc *trigdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
413 int ntrigs = relation->rd_rel->reltriggers;
414 Trigger *triggers = NULL;
417 Form_pg_trigger pg_trigger;
422 RetrieveIndexResult indexRes;
428 MemSet(trigdesc, 0, sizeof(TriggerDesc));
430 ScanKeyEntryInitialize(&skey,
433 (RegProcedure) F_OIDEQ,
434 ObjectIdGetDatum(RelationGetRelid(relation)));
436 tgrel = heap_openr(TriggerRelationName, AccessShareLock);
437 irel = index_openr(TriggerRelidIndex);
438 sd = index_beginscan(irel, false, 1, &skey);
442 indexRes = index_getnext(sd, ForwardScanDirection);
446 tuple.t_self = indexRes->heap_iptr;
447 heap_fetch(tgrel, SnapshotNow, &tuple, &buffer);
452 elog(ERROR, "RelationBuildTriggers: unexpected record found for rel %.*s",
453 NAMEDATALEN, RelationGetRelationName(relation));
455 pg_trigger = (Form_pg_trigger) GETSTRUCT(&tuple);
457 if (triggers == NULL)
458 triggers = (Trigger *) palloc(sizeof(Trigger));
460 triggers = (Trigger *) repalloc(triggers, (found + 1) * sizeof(Trigger));
461 build = &(triggers[found]);
463 build->tgoid = tuple.t_data->t_oid;
464 build->tgname = nameout(&pg_trigger->tgname);
465 build->tgfoid = pg_trigger->tgfoid;
466 build->tgfunc.fn_addr = NULL;
467 build->tgtype = pg_trigger->tgtype;
468 build->tgenabled = pg_trigger->tgenabled;
469 build->tgisconstraint = pg_trigger->tgisconstraint;
470 build->tgdeferrable = pg_trigger->tgdeferrable;
471 build->tginitdeferred = pg_trigger->tginitdeferred;
472 build->tgnargs = pg_trigger->tgnargs;
473 memcpy(build->tgattr, &(pg_trigger->tgattr), 8 * sizeof(int16));
474 val = (struct varlena *) fastgetattr(&tuple,
475 Anum_pg_trigger_tgargs,
476 tgrel->rd_att, &isnull);
478 elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %.*s",
479 NAMEDATALEN, RelationGetRelationName(relation));
480 if (build->tgnargs > 0)
485 val = (struct varlena *) fastgetattr(&tuple,
486 Anum_pg_trigger_tgargs,
487 tgrel->rd_att, &isnull);
489 elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %.*s",
490 NAMEDATALEN, RelationGetRelationName(relation));
491 p = (char *) VARDATA(val);
492 build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
493 for (i = 0; i < build->tgnargs; i++)
495 build->tgargs[i] = (char *) palloc(strlen(p) + 1);
496 strcpy(build->tgargs[i], p);
501 build->tgargs = NULL;
504 ReleaseBuffer(buffer);
508 elog(ERROR, "RelationBuildTriggers: %d record not found for rel %.*s",
510 NAMEDATALEN, RelationGetRelationName(relation));
515 heap_close(tgrel, AccessShareLock);
518 trigdesc->triggers = triggers;
519 for (found = 0; found < ntrigs; found++)
521 build = &(triggers[found]);
522 DescribeTrigger(trigdesc, build);
525 relation->trigdesc = trigdesc;
530 FreeTriggerDesc(Relation relation)
532 TriggerDesc *trigdesc = relation->trigdesc;
537 if (trigdesc == NULL)
540 t = trigdesc->tg_before_statement;
541 for (i = 0; i < 3; i++)
544 t = trigdesc->tg_before_row;
545 for (i = 0; i < 3; i++)
548 t = trigdesc->tg_after_row;
549 for (i = 0; i < 3; i++)
552 t = trigdesc->tg_after_statement;
553 for (i = 0; i < 3; i++)
557 trigger = trigdesc->triggers;
558 for (i = 0; i < relation->rd_rel->reltriggers; i++)
560 pfree(trigger->tgname);
561 if (trigger->tgnargs > 0)
563 while (--(trigger->tgnargs) >= 0)
564 pfree(trigger->tgargs[trigger->tgnargs]);
565 pfree(trigger->tgargs);
569 pfree(trigdesc->triggers);
571 relation->trigdesc = NULL;
576 DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
582 if (TRIGGER_FOR_ROW(trigger->tgtype)) /* Is ROW/STATEMENT
585 if (TRIGGER_FOR_BEFORE(trigger->tgtype))
587 n = trigdesc->n_before_row;
588 t = trigdesc->tg_before_row;
592 n = trigdesc->n_after_row;
593 t = trigdesc->tg_after_row;
599 if (TRIGGER_FOR_BEFORE(trigger->tgtype))
601 n = trigdesc->n_before_statement;
602 t = trigdesc->tg_before_statement;
606 n = trigdesc->n_after_statement;
607 t = trigdesc->tg_after_statement;
611 if (TRIGGER_FOR_INSERT(trigger->tgtype))
613 tp = &(t[TRIGGER_EVENT_INSERT]);
615 *tp = (Trigger **) palloc(sizeof(Trigger *));
617 *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) *
619 (*tp)[n[TRIGGER_EVENT_INSERT]] = trigger;
620 (n[TRIGGER_EVENT_INSERT])++;
623 if (TRIGGER_FOR_DELETE(trigger->tgtype))
625 tp = &(t[TRIGGER_EVENT_DELETE]);
627 *tp = (Trigger **) palloc(sizeof(Trigger *));
629 *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) *
631 (*tp)[n[TRIGGER_EVENT_DELETE]] = trigger;
632 (n[TRIGGER_EVENT_DELETE])++;
635 if (TRIGGER_FOR_UPDATE(trigger->tgtype))
637 tp = &(t[TRIGGER_EVENT_UPDATE]);
639 *tp = (Trigger **) palloc(sizeof(Trigger *));
641 *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) *
643 (*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger;
644 (n[TRIGGER_EVENT_UPDATE])++;
650 ExecCallTriggerFunc(Trigger *trigger)
653 if (trigger->tgfunc.fn_addr == NULL)
654 fmgr_info(trigger->tgfoid, &trigger->tgfunc);
656 if (trigger->tgfunc.fn_plhandler != NULL)
658 return (HeapTuple) (*(trigger->tgfunc.fn_plhandler))
662 return (HeapTuple) ((*fmgr_faddr(&trigger->tgfunc)) ());
666 ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple)
668 TriggerData *SaveTriggerData;
669 int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT];
670 Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT];
671 HeapTuple newtuple = trigtuple;
675 SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
676 SaveTriggerData->tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
677 SaveTriggerData->tg_relation = rel;
678 SaveTriggerData->tg_newtuple = NULL;
679 for (i = 0; i < ntrigs; i++)
681 if (!trigger[i]->tgenabled)
683 CurrentTriggerData = SaveTriggerData;
684 CurrentTriggerData->tg_trigtuple = oldtuple = newtuple;
685 CurrentTriggerData->tg_trigger = trigger[i];
686 newtuple = ExecCallTriggerFunc(trigger[i]);
687 if (newtuple == NULL)
689 else if (oldtuple != newtuple && oldtuple != trigtuple)
692 CurrentTriggerData = NULL;
693 pfree(SaveTriggerData);
698 ExecARInsertTriggers(Relation rel, HeapTuple trigtuple)
700 DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_INSERT, NULL, trigtuple);
705 ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid)
707 Relation rel = estate->es_result_relation_info->ri_RelationDesc;
708 TriggerData *SaveTriggerData;
709 int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_DELETE];
710 Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_DELETE];
712 HeapTuple newtuple = NULL;
713 TupleTableSlot *newSlot;
716 trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot);
717 if (trigtuple == NULL)
720 SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
721 SaveTriggerData->tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
722 SaveTriggerData->tg_relation = rel;
723 SaveTriggerData->tg_newtuple = NULL;
724 for (i = 0; i < ntrigs; i++)
726 if (!trigger[i]->tgenabled)
728 CurrentTriggerData = SaveTriggerData;
729 CurrentTriggerData->tg_trigtuple = trigtuple;
730 CurrentTriggerData->tg_trigger = trigger[i];
731 newtuple = ExecCallTriggerFunc(trigger[i]);
732 if (newtuple == NULL)
734 if (newtuple != trigtuple)
737 CurrentTriggerData = NULL;
738 pfree(SaveTriggerData);
741 return (newtuple == NULL) ? false : true;
745 ExecARDeleteTriggers(EState *estate, ItemPointer tupleid)
747 Relation rel = estate->es_result_relation_info->ri_RelationDesc;
748 HeapTuple trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
750 DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_DELETE, trigtuple, NULL);
755 ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
757 Relation rel = estate->es_result_relation_info->ri_RelationDesc;
758 TriggerData *SaveTriggerData;
759 int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE];
760 Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_UPDATE];
763 HeapTuple intuple = newtuple;
764 TupleTableSlot *newSlot;
767 trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot);
768 if (trigtuple == NULL)
772 * In READ COMMITTED isolevel it's possible that newtuple was changed
773 * due to concurrent update.
776 intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot);
778 SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData));
779 SaveTriggerData->tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
780 SaveTriggerData->tg_relation = rel;
781 for (i = 0; i < ntrigs; i++)
783 if (!trigger[i]->tgenabled)
785 CurrentTriggerData = SaveTriggerData;
786 CurrentTriggerData->tg_trigtuple = trigtuple;
787 CurrentTriggerData->tg_newtuple = oldtuple = newtuple;
788 CurrentTriggerData->tg_trigger = trigger[i];
789 newtuple = ExecCallTriggerFunc(trigger[i]);
790 if (newtuple == NULL)
792 else if (oldtuple != newtuple && oldtuple != intuple)
795 CurrentTriggerData = NULL;
796 pfree(SaveTriggerData);
802 ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
804 Relation rel = estate->es_result_relation_info->ri_RelationDesc;
805 HeapTuple trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
807 DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_UPDATE, trigtuple, newtuple);
811 extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid);
814 GetTupleForTrigger(EState *estate, ItemPointer tid, TupleTableSlot **newSlot)
816 Relation relation = estate->es_result_relation_info->ri_RelationDesc;
826 * mark tuple for update
831 test = heap_mark4update(relation, &tuple, &buffer);
834 case HeapTupleSelfUpdated:
835 ReleaseBuffer(buffer);
838 case HeapTupleMayBeUpdated:
841 case HeapTupleUpdated:
842 ReleaseBuffer(buffer);
843 if (XactIsoLevel == XACT_SERIALIZABLE)
844 elog(ERROR, "Can't serialize access due to concurrent update");
845 else if (!(ItemPointerEquals(&(tuple.t_self), tid)))
847 TupleTableSlot *epqslot = EvalPlanQual(estate,
848 estate->es_result_relation_info->ri_RangeTableIndex,
851 if (!(TupIsNull(epqslot)))
860 * if tuple was deleted or PlanQual failed for updated
861 * tuple - we have not process this tuple!
866 ReleaseBuffer(buffer);
867 elog(ERROR, "Unknown status %u from heap_mark4update", test);
876 buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
878 if (!BufferIsValid(buffer))
879 elog(ERROR, "GetTupleForTrigger: failed ReadBuffer");
881 dp = (PageHeader) BufferGetPage(buffer);
882 lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid));
884 Assert(ItemIdIsUsed(lp));
886 tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
887 tuple.t_len = ItemIdGetLength(lp);
891 result = heap_copytuple(&tuple);
892 ReleaseBuffer(buffer);
899 * Deferred trigger stuff
905 * Internal data to the deferred trigger mechanism is held
906 * during entire session in a global memor created at startup and
907 * over statements/commands in a separate global memory which
908 * is created at transaction start and destroyed at transaction
912 static GlobalMemory deftrig_gcxt = NULL;
913 static GlobalMemory deftrig_cxt = NULL;
916 * Global data that tells which triggers are actually in
917 * state IMMEDIATE or DEFERRED.
920 static bool deftrig_dfl_all_isset = false;
921 static bool deftrig_dfl_all_isdeferred = false;
922 static List *deftrig_dfl_trigstates = NIL;
924 static bool deftrig_all_isset;
925 static bool deftrig_all_isdeferred;
926 static List *deftrig_trigstates;
929 * The list of events during the entire transaction.
931 * XXX This must finally be held in a file because of the huge
932 * number of events that could occur in the real world.
935 static int deftrig_n_events;
936 static List *deftrig_events;
940 * deferredTriggerCheckState()
942 * Returns true if the trigger identified by tgoid is actually
947 deferredTriggerCheckState(Oid tgoid, int32 itemstate)
949 MemoryContext oldcxt;
951 DeferredTriggerStatus trigstate;
954 * Not deferrable triggers (i.e. normal AFTER ROW triggers
955 * and constraints declared NOT DEFERRABLE, the state is
959 if ((itemstate & TRIGGER_DEFERRED_DEFERRABLE) == 0)
963 * Lookup if we know an individual state for this trigger
966 foreach (sl, deftrig_trigstates)
968 trigstate = (DeferredTriggerStatus) lfirst(sl);
969 if (trigstate->dts_tgoid == tgoid)
970 return trigstate->dts_tgisdeferred;
974 * No individual state known - so if the user issued a
975 * SET CONSTRAINT ALL ..., we return that instead of the
976 * triggers default state.
979 if (deftrig_all_isset)
980 return deftrig_all_isdeferred;
983 * No ALL state known either, remember the default state
984 * as the current and return that.
987 oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
989 trigstate = (DeferredTriggerStatus)
990 palloc(sizeof(DeferredTriggerStatusData));
991 trigstate->dts_tgoid = tgoid;
992 trigstate->dts_tgisdeferred =
993 ((itemstate & TRIGGER_DEFERRED_INITDEFERRED) != 0);
994 deftrig_trigstates = lappend(deftrig_trigstates, trigstate);
996 MemoryContextSwitchTo(oldcxt);
998 return trigstate->dts_tgisdeferred;
1003 * deferredTriggerAddEvent()
1005 * Add a new trigger event to the queue.
1009 deferredTriggerAddEvent(DeferredTriggerEvent event)
1011 deftrig_events = lappend(deftrig_events, event);
1019 * deferredTriggerGetPreviousEvent()
1021 * Backward scan the eventlist to find the event a given OLD tuple
1022 * resulted from in the same transaction.
1025 static DeferredTriggerEvent
1026 deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid)
1028 DeferredTriggerEvent previous;
1031 for (n = deftrig_n_events - 1; n >= 0; n--)
1033 previous = (DeferredTriggerEvent) nth(n, deftrig_events);
1035 if (previous->dte_relid != relid)
1037 if (previous->dte_event & TRIGGER_DEFERRED_CANCELED)
1040 if (ItemPointerGetBlockNumber(ctid) ==
1041 ItemPointerGetBlockNumber(&(previous->dte_newctid)) &&
1042 ItemPointerGetOffsetNumber(ctid) ==
1043 ItemPointerGetOffsetNumber(&(previous->dte_newctid)))
1048 "deferredTriggerGetPreviousEvent(): event for tuple %s not found",
1055 * deferredTriggerCancelEvent()
1057 * Mark an event in the eventlist as cancelled because it isn't
1058 * required anymore (replaced by anoter event).
1062 deferredTriggerCancelEvent(DeferredTriggerEvent event)
1064 event->dte_event |= TRIGGER_DEFERRED_CANCELED;
1069 * deferredTriggerExecute()
1071 * Fetch the required tuples back from the heap and fire one
1072 * single trigger function.
1076 deferredTriggerExecute(DeferredTriggerEvent event, int itemno)
1079 TriggerData SaveTriggerData;
1080 HeapTupleData oldtuple;
1081 HeapTupleData newtuple;
1087 * Open the heap and fetch the required OLD and NEW tuples.
1090 rel = heap_open(event->dte_relid, NoLock);
1092 if (ItemPointerIsValid(&(event->dte_oldctid)))
1094 ItemPointerCopy(&(event->dte_oldctid), &(oldtuple.t_self));
1095 heap_fetch(rel, SnapshotAny, &oldtuple, &oldbuffer);
1096 if (!oldtuple.t_data)
1097 elog(ERROR, "deferredTriggerExecute(): failed to fetch old tuple");
1100 if (ItemPointerIsValid(&(event->dte_newctid)))
1102 ItemPointerCopy(&(event->dte_newctid), &(newtuple.t_self));
1103 heap_fetch(rel, SnapshotAny, &newtuple, &newbuffer);
1104 if (!newtuple.t_data)
1105 elog(ERROR, "deferredTriggerExecute(): failed to fetch new tuple");
1109 * Setup the trigger information
1112 SaveTriggerData.tg_event = event->dte_event | TRIGGER_EVENT_ROW;
1113 SaveTriggerData.tg_relation = rel;
1115 switch (event->dte_event)
1117 case TRIGGER_EVENT_INSERT:
1118 SaveTriggerData.tg_trigtuple = &newtuple;
1119 SaveTriggerData.tg_newtuple = NULL;
1120 SaveTriggerData.tg_trigger =
1121 rel->trigdesc->tg_after_row[TRIGGER_EVENT_INSERT][itemno];
1124 case TRIGGER_EVENT_UPDATE:
1125 SaveTriggerData.tg_trigtuple = &oldtuple;
1126 SaveTriggerData.tg_newtuple = &newtuple;
1127 SaveTriggerData.tg_trigger =
1128 rel->trigdesc->tg_after_row[TRIGGER_EVENT_UPDATE][itemno];
1131 case TRIGGER_EVENT_DELETE:
1132 SaveTriggerData.tg_trigtuple = &oldtuple;
1133 SaveTriggerData.tg_newtuple = NULL;
1134 SaveTriggerData.tg_trigger =
1135 rel->trigdesc->tg_after_row[TRIGGER_EVENT_DELETE][itemno];
1142 * Call the trigger and throw away an eventually returned
1146 CurrentTriggerData = &SaveTriggerData;
1147 rettuple = ExecCallTriggerFunc(SaveTriggerData.tg_trigger);
1148 CurrentTriggerData = NULL;
1149 if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple)
1153 * Release buffers and close the relation
1156 if (ItemPointerIsValid(&(event->dte_oldctid)))
1157 ReleaseBuffer(oldbuffer);
1158 if (ItemPointerIsValid(&(event->dte_newctid)))
1159 ReleaseBuffer(newbuffer);
1161 heap_close(rel, NoLock);
1168 * deferredTriggerInvokeEvents()
1170 * Scan the event queue for not yet invoked triggers. Check if they
1171 * should be invoked now and do so.
1175 deferredTriggerInvokeEvents(bool immediate_only)
1178 DeferredTriggerEvent event;
1179 int still_deferred_ones;
1184 * For now we process all events - to speedup transaction blocks
1185 * we need to remember the actual end of the queue at EndQuery
1186 * and process only events that are newer. On state changes we
1187 * simply reset the position to the beginning of the queue and
1188 * process all events once with the new states when the
1189 * SET CONSTRAINTS ... command finishes and calls EndQuery.
1192 foreach (el, deftrig_events)
1197 * Get the event and check if it is completely done.
1200 event = (DeferredTriggerEvent) lfirst(el);
1201 if (event->dte_event & (TRIGGER_DEFERRED_DONE |
1202 TRIGGER_DEFERRED_CANCELED))
1206 * Check each trigger item in the event.
1209 still_deferred_ones = false;
1210 for (i = 0; i < event->dte_n_items; i++)
1212 if (event->dte_item[i].dti_state & TRIGGER_DEFERRED_DONE)
1216 * This trigger item hasn't been called yet. Check if
1217 * we should call it now.
1220 if (immediate_only && deferredTriggerCheckState(
1221 event->dte_item[i].dti_tgoid,
1222 event->dte_item[i].dti_state))
1224 still_deferred_ones = true;
1229 * So let's fire it...
1232 deferredTriggerExecute(event, i);
1233 event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
1237 * Remember in the event itself if all trigger items are
1241 if (!still_deferred_ones)
1242 event->dte_event |= TRIGGER_DEFERRED_DONE;
1248 * DeferredTriggerInit()
1250 * Initialize the deferred trigger mechanism. This is called during
1251 * backend startup and is guaranteed to be before the first of all
1256 DeferredTriggerInit(void)
1258 deftrig_gcxt = CreateGlobalMemory("DeferredTriggerSession");
1264 * DeferredTriggerBeginXact()
1266 * Called at transaction start (either BEGIN or implicit for single
1267 * statement outside of transaction block).
1271 DeferredTriggerBeginXact(void)
1273 MemoryContext oldcxt;
1275 DeferredTriggerStatus dflstat;
1276 DeferredTriggerStatus stat;
1278 if (deftrig_cxt != NULL)
1280 "DeferredTriggerBeginXact() called while inside transaction");
1283 * Create the per transaction memory context and copy all states
1284 * from the per session context to here.
1287 deftrig_cxt = CreateGlobalMemory("DeferredTriggerXact");
1288 oldcxt = MemoryContextSwitchTo((MemoryContext)deftrig_cxt);
1290 deftrig_all_isset = deftrig_dfl_all_isset;
1291 deftrig_all_isdeferred = deftrig_dfl_all_isdeferred;
1293 deftrig_trigstates = NIL;
1294 foreach (l, deftrig_dfl_trigstates)
1296 dflstat = (DeferredTriggerStatus) lfirst(l);
1297 stat = (DeferredTriggerStatus)
1298 palloc(sizeof(DeferredTriggerStatusData));
1300 stat->dts_tgoid = dflstat->dts_tgoid;
1301 stat->dts_tgisdeferred = dflstat->dts_tgisdeferred;
1303 deftrig_trigstates = lappend(deftrig_trigstates, stat);
1306 MemoryContextSwitchTo(oldcxt);
1308 deftrig_n_events = 0;
1309 deftrig_events = NIL;
1314 * DeferredTriggerEndQuery()
1316 * Called after one query sent down by the user has completely been
1317 * processed. At this time we invoke all outstanding IMMEDIATE triggers.
1321 DeferredTriggerEndQuery(void)
1324 * Ignore call if we aren't in a transaction.
1327 if (deftrig_cxt == NULL)
1330 deferredTriggerInvokeEvents(true);
1335 * DeferredTriggerEndXact()
1337 * Called just before the current transaction is committed. At this
1338 * time we invoke all DEFERRED triggers and tidy up.
1342 DeferredTriggerEndXact(void)
1345 * Ignore call if we aren't in a transaction.
1348 if (deftrig_cxt == NULL)
1351 deferredTriggerInvokeEvents(false);
1353 GlobalMemoryDestroy(deftrig_cxt);
1359 * DeferredTriggerAbortXact()
1361 * The current transaction has entered the abort state.
1362 * All outstanding triggers are canceled so we simply throw
1363 * away anything we know.
1367 DeferredTriggerAbortXact(void)
1370 * Ignore call if we aren't in a transaction.
1373 if (deftrig_cxt == NULL)
1376 GlobalMemoryDestroy(deftrig_cxt);
1382 * DeferredTriggerSetState()
1384 * Called for the users SET CONSTRAINTS ... utility command.
1388 DeferredTriggerSetState(ConstraintsSetStmt *stmt)
1396 MemoryContext oldcxt;
1398 DeferredTriggerStatus state;
1401 * Handle SET CONSTRAINTS ALL ...
1404 if (stmt->constraints == NIL) {
1405 if (!IsTransactionBlock())
1408 * ... outside of a transaction block
1411 oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_gcxt);
1414 * Drop all information about individual trigger states per
1418 l = deftrig_dfl_trigstates;
1426 deftrig_dfl_trigstates = NIL;
1429 * Set the session ALL state to known.
1432 deftrig_dfl_all_isset = true;
1433 deftrig_dfl_all_isdeferred = stmt->deferred;
1435 MemoryContextSwitchTo(oldcxt);
1440 * ... inside of a transaction block
1443 oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
1446 * Drop all information about individual trigger states per
1450 l = deftrig_trigstates;
1458 deftrig_trigstates = NIL;
1461 * Set the per transaction ALL state to known.
1464 deftrig_all_isset = true;
1465 deftrig_all_isdeferred = stmt->deferred;
1467 MemoryContextSwitchTo(oldcxt);
1474 * Handle SET CONSTRAINTS constraint-name [, ...]
1475 * First lookup all trigger Oid's for the constraint names.
1478 tgrel = heap_openr(TriggerRelationName, AccessShareLock);
1479 irel = index_openr(TriggerConstrNameIndex);
1481 foreach (l, stmt->constraints)
1484 HeapTupleData tuple;
1486 RetrieveIndexResult indexRes;
1488 Form_pg_trigger pg_trigger;
1492 * Check that only named constraints are set explicitly
1495 if (strcmp((char *)lfirst(l), "") == 0)
1496 elog(ERROR, "unnamed constraints cannot be set explicitly");
1499 * Setup to scan pg_trigger by tgconstrname ...
1502 ScanKeyEntryInitialize(&skey,
1505 (RegProcedure) F_NAMEEQ,
1506 PointerGetDatum((char *)lfirst(l)));
1508 sd = index_beginscan(irel, false, 1, &skey);
1511 * ... and search for the constraint trigger row
1517 indexRes = index_getnext(sd, ForwardScanDirection);
1521 tuple.t_self = indexRes->heap_iptr;
1522 heap_fetch(tgrel, SnapshotNow, &tuple, &buffer);
1526 ReleaseBuffer(buffer);
1531 * If we found some, check that they fit the deferrability
1534 pg_trigger = (Form_pg_trigger) GETSTRUCT(&tuple);
1535 if (stmt->deferred & !pg_trigger->tgdeferrable)
1536 elog(ERROR, "Constraint '%s' is not deferrable",
1539 constr_oid = tuple.t_data->t_oid;
1540 loid = lappend(loid, (Node *)constr_oid);
1543 ReleaseBuffer(buffer);
1551 elog(ERROR, "Constraint '%s' does not exist", (char *)lfirst(l));
1557 heap_close(tgrel, AccessShareLock);
1560 if (!IsTransactionBlock())
1563 * Outside of a transaction block set the trigger
1564 * states of individual triggers on session level.
1567 oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_gcxt);
1572 foreach (ls, deftrig_dfl_trigstates)
1574 state = (DeferredTriggerStatus) lfirst(ls);
1575 if (state->dts_tgoid == (Oid) lfirst(l))
1577 state->dts_tgisdeferred = stmt->deferred;
1584 state = (DeferredTriggerStatus)
1585 palloc(sizeof(DeferredTriggerStatusData));
1586 state->dts_tgoid = (Oid) lfirst(l);
1587 state->dts_tgisdeferred = stmt->deferred;
1589 deftrig_dfl_trigstates =
1590 lappend(deftrig_dfl_trigstates, state);
1594 MemoryContextSwitchTo(oldcxt);
1599 * Inside of a transaction block set the trigger
1600 * states of individual triggers on transaction level.
1603 oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
1608 foreach (ls, deftrig_trigstates)
1610 state = (DeferredTriggerStatus) lfirst(ls);
1611 if (state->dts_tgoid == (Oid) lfirst(l))
1613 state->dts_tgisdeferred = stmt->deferred;
1620 state = (DeferredTriggerStatus)
1621 palloc(sizeof(DeferredTriggerStatusData));
1622 state->dts_tgoid = (Oid) lfirst(l);
1623 state->dts_tgisdeferred = stmt->deferred;
1625 deftrig_trigstates =
1626 lappend(deftrig_trigstates, state);
1630 MemoryContextSwitchTo(oldcxt);
1638 * DeferredTriggerSaveEvent()
1640 * Called by ExecAR...Triggers() to add the event to the queue.
1644 DeferredTriggerSaveEvent(Relation rel, int event,
1645 HeapTuple oldtup, HeapTuple newtup)
1647 MemoryContext oldcxt;
1648 DeferredTriggerEvent new_event;
1649 DeferredTriggerEvent prev_event;
1650 bool prev_done = false;
1655 ItemPointerData oldctid;
1656 ItemPointerData newctid;
1658 if (deftrig_cxt == NULL)
1660 "DeferredTriggerSaveEvent() called outside of transaction");
1663 * Check if we're interested in this row at all
1666 if (rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] == 0 &&
1667 rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] == 0 &&
1668 rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] == 0 &&
1669 rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] == 0 &&
1670 rel->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] == 0 &&
1671 rel->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] == 0)
1675 * Get the CTID's of OLD and NEW
1679 ItemPointerCopy(&(oldtup->t_self), &(oldctid));
1681 ItemPointerSetInvalid(&(oldctid));
1683 ItemPointerCopy(&(newtup->t_self), &(newctid));
1685 ItemPointerSetInvalid(&(newctid));
1688 * Eventually modify the event and do some general RI violation checks
1693 case TRIGGER_EVENT_INSERT:
1695 * Don't know how to (surely) check if another tuple with
1696 * this meaning (from all FK's point of view) got deleted
1697 * in the same transaction. Thus not handled yet.
1702 case TRIGGER_EVENT_UPDATE:
1704 * On UPDATE check if the tuple updated is a result
1705 * of the same transaction.
1708 if (oldtup->t_data->t_xmin != GetCurrentTransactionId())
1712 * Look at the previous event to the same tuple if
1713 * any of it's triggers has already been executed.
1714 * Such a situation would potentially violate RI
1715 * so we abort the transaction.
1718 prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid);
1719 if (prev_event->dte_event & TRIGGER_DEFERRED_HAS_BEFORE ||
1720 (prev_event->dte_n_items != 0 &&
1721 prev_event->dte_event & TRIGGER_DEFERRED_DONE))
1724 for (i = 0; i < prev_event->dte_n_items; i++)
1726 if (prev_event->dte_item[i].dti_state &
1727 TRIGGER_DEFERRED_DONE)
1736 elog(NOTICE, "UPDATE of row inserted/updated in same "
1737 "transaction violates");
1738 elog(NOTICE, "referential integrity semantics. Other "
1739 "triggers or IMMEDIATE ");
1740 elog(ERROR, " constraints have already been executed.");
1744 * Anything's fine so far - i.e. none of the previous
1745 * events triggers has been executed up to now. Let's
1746 * the REAL event that happened so far.
1749 switch (prev_event->dte_event & TRIGGER_EVENT_OPMASK)
1751 case TRIGGER_EVENT_INSERT:
1753 * The previous operation was an insert.
1754 * So the REAL new event is an INSERT of
1758 event = TRIGGER_EVENT_INSERT;
1759 ItemPointerSetInvalid(&oldctid);
1760 deferredTriggerCancelEvent(prev_event);
1763 case TRIGGER_EVENT_UPDATE:
1765 * The previous operation was an UPDATE.
1766 * So the REAL new event is still an UPDATE
1767 * but from the original tuple to this new one.
1770 event = TRIGGER_EVENT_UPDATE;
1771 ItemPointerCopy(&(prev_event->dte_oldctid), &oldctid);
1772 deferredTriggerCancelEvent(prev_event);
1778 case TRIGGER_EVENT_DELETE:
1780 * On DELETE check if the tuple updated is a result
1781 * of the same transaction.
1784 if (oldtup->t_data->t_xmin != GetCurrentTransactionId())
1788 * Look at the previous event to the same tuple if
1789 * any of it's triggers has already been executed.
1790 * Such a situation would potentially violate RI
1791 * so we abort the transaction.
1794 prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid);
1795 if (prev_event->dte_event & TRIGGER_DEFERRED_HAS_BEFORE ||
1796 (prev_event->dte_n_items != 0 &&
1797 prev_event->dte_event & TRIGGER_DEFERRED_DONE))
1800 for (i = 0; i < prev_event->dte_n_items; i++)
1802 if (prev_event->dte_item[i].dti_state &
1803 TRIGGER_DEFERRED_DONE)
1812 elog(NOTICE, "DELETE of row inserted/updated in same "
1813 "transaction violates");
1814 elog(NOTICE, "referential integrity semantics. Other "
1815 "triggers or IMMEDIATE ");
1816 elog(ERROR, " constraints have already been executed.");
1820 * Anything's fine so far - i.e. none of the previous
1821 * events triggers has been executed up to now. Let's
1822 * the REAL event that happened so far.
1825 switch (prev_event->dte_event & TRIGGER_EVENT_OPMASK)
1827 case TRIGGER_EVENT_INSERT:
1829 * The previous operation was an insert.
1830 * So the REAL new event is nothing.
1833 deferredTriggerCancelEvent(prev_event);
1836 case TRIGGER_EVENT_UPDATE:
1838 * The previous operation was an UPDATE.
1839 * So the REAL new event is a DELETE
1840 * but from the original tuple.
1843 event = TRIGGER_EVENT_DELETE;
1844 ItemPointerCopy(&(prev_event->dte_oldctid), &oldctid);
1845 deferredTriggerCancelEvent(prev_event);
1853 * Create a new event and save it.
1856 oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
1858 ntriggers = rel->trigdesc->n_after_row[event];
1859 triggers = rel->trigdesc->tg_after_row[event];
1861 new_size = sizeof(DeferredTriggerEventData) +
1862 ntriggers * sizeof(DeferredTriggerEventItem);
1864 new_event = (DeferredTriggerEvent) palloc(new_size);
1865 new_event->dte_event = event;
1866 new_event->dte_relid = rel->rd_id;
1867 ItemPointerCopy(&oldctid, &(new_event->dte_oldctid));
1868 ItemPointerCopy(&newctid, &(new_event->dte_newctid));
1869 new_event->dte_n_items = ntriggers;
1870 new_event->dte_item[ntriggers].dti_state = new_size;
1871 for (i = 0; i < ntriggers; i++)
1873 new_event->dte_item[i].dti_tgoid = triggers[i]->tgoid;
1874 new_event->dte_item[i].dti_state =
1875 ((triggers[i]->tgdeferrable) ?
1876 TRIGGER_DEFERRED_DEFERRABLE : 0) |
1877 ((triggers[i]->tginitdeferred) ?
1878 TRIGGER_DEFERRED_INITDEFERRED : 0) |
1879 ((rel->trigdesc->n_before_row[event] > 0) ?
1880 TRIGGER_DEFERRED_HAS_BEFORE : 0);
1883 deferredTriggerAddEvent(new_event);
1885 MemoryContextSwitchTo(oldcxt);