/*------------------------------------------------------------------------- * * trigger.c-- * PostgreSQL TRIGGERs support code. * *------------------------------------------------------------------------- */ #include #include "postgres.h" #include "nodes/parsenodes.h" #include "nodes/memnodes.h" #include "commands/trigger.h" #include "catalog/catalog.h" #include "catalog/catname.h" #include "catalog/indexing.h" #include "catalog/pg_proc.h" #include "catalog/pg_language.h" #include "catalog/pg_trigger.h" #include "access/genam.h" #include "access/heapam.h" #include "access/valid.h" #include "access/xact.h" #include "storage/lmgr.h" #include "storage/bufmgr.h" #include "utils/mcxt.h" #include "utils/inval.h" #include "utils/builtins.h" #include "utils/syscache.h" #ifndef NO_SECURITY #include "miscadmin.h" #include "utils/acl.h" #endif TriggerData *CurrentTriggerData = NULL; void RelationBuildTriggers(Relation relation); void FreeTriggerDesc(Relation relation); static void DescribeTrigger(TriggerDesc * trigdesc, Trigger * trigger); static HeapTuple GetTupleForTrigger(Relation relation, ItemPointer tid, bool before); extern void fmgr_info(Oid procedureId, func_ptr * function, int *nargs); extern GlobalMemory CacheCxt; void CreateTrigger(CreateTrigStmt * stmt) { int16 tgtype; int16 tgattr[8] = {0}; Datum values[Natts_pg_trigger]; char nulls[Natts_pg_trigger]; Relation rel; Relation tgrel; HeapScanDesc tgscan; ScanKeyData key; Relation relrdesc; HeapTuple tuple; ItemPointerData oldTID; Relation idescs[Num_pg_trigger_indices]; Relation ridescs[Num_pg_class_indices]; MemoryContext oldcxt; Oid fargtypes[8]; int found = 0; int i; if (IsSystemRelationName(stmt->relname)) elog(ERROR, "CreateTrigger: can't create trigger for system relation %s", stmt->relname); #ifndef NO_SECURITY if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME)) elog(ERROR, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); #endif rel = heap_openr(stmt->relname); if (!RelationIsValid(rel)) elog(ERROR, "CreateTrigger: there is no relation %s", stmt->relname); RelationSetLockForWrite(rel); TRIGGER_CLEAR_TYPE(tgtype); if (stmt->before) TRIGGER_SETT_BEFORE(tgtype); if (stmt->row) TRIGGER_SETT_ROW(tgtype); else elog(ERROR, "CreateTrigger: STATEMENT triggers are unimplemented, yet"); for (i = 0; i < 3 && stmt->actions[i]; i++) { switch (stmt->actions[i]) { case 'i': if (TRIGGER_FOR_INSERT(tgtype)) elog(ERROR, "CreateTrigger: double INSERT event specified"); TRIGGER_SETT_INSERT(tgtype); break; case 'd': if (TRIGGER_FOR_DELETE(tgtype)) elog(ERROR, "CreateTrigger: double DELETE event specified"); TRIGGER_SETT_DELETE(tgtype); break; case 'u': if (TRIGGER_FOR_UPDATE(tgtype)) elog(ERROR, "CreateTrigger: double UPDATE event specified"); TRIGGER_SETT_UPDATE(tgtype); break; default: elog(ERROR, "CreateTrigger: unknown event specified"); break; } } /* Scan pg_trigger */ tgrel = heap_openr(TriggerRelationName); RelationSetLockForWrite(tgrel); ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, ObjectIdEqualRegProcedure, rel->rd_id); tgscan = heap_beginscan(tgrel, 0, false, 1, &key); while (tuple = heap_getnext(tgscan, 0, (Buffer *) NULL), PointerIsValid(tuple)) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0) elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s", stmt->trigname, stmt->relname); else found++; } heap_endscan(tgscan); MemSet(fargtypes, 0, 8 * sizeof(Oid)); tuple = SearchSysCacheTuple(PRONAME, PointerGetDatum(stmt->funcname), 0, PointerGetDatum(fargtypes), 0); if (!HeapTupleIsValid(tuple) || ((Form_pg_proc) GETSTRUCT(tuple))->prorettype != 0 || ((Form_pg_proc) GETSTRUCT(tuple))->pronargs != 0) elog(ERROR, "CreateTrigger: function %s () does not exist", stmt->funcname); if (((Form_pg_proc) GETSTRUCT(tuple))->prolang != ClanguageId) { HeapTuple langTup; langTup = SearchSysCacheTuple(LANOID, ObjectIdGetDatum(((Form_pg_proc) GETSTRUCT(tuple))->prolang), 0, 0, 0); if (!HeapTupleIsValid(langTup)) { elog(ERROR, "CreateTrigger: cache lookup for PL failed"); } if (((Form_pg_language) GETSTRUCT(langTup))->lanispl == false) { elog(ERROR, "CreateTrigger: only C and PL functions are supported"); } } MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char)); values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(rel->rd_id); values[Anum_pg_trigger_tgname - 1] = NameGetDatum(namein(stmt->trigname)); values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(tuple->t_oid); values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype); if (stmt->args) { List *le; char *args; int16 nargs = length(stmt->args); int len = 0; foreach(le, stmt->args) { char *ar = (char *) lfirst(le); len += strlen(ar) + VARHDRSZ; for (; *ar; ar++) { if (*ar == '\\') len++; } } args = (char *) palloc(len + 1); args[0] = 0; foreach(le, stmt->args) { char *s = (char *) lfirst(le); char *d = args + strlen(args); while (*s) { if (*s == '\\') *d++ = '\\'; *d++ = *s++; } *d = 0; strcat(args, "\\000"); } values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs); values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain(args)); } else { values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0); values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain("")); } values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr); tuple = heap_formtuple(tgrel->rd_att, values, nulls); heap_insert(tgrel, tuple); CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs); CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple); CatalogCloseIndices(Num_pg_trigger_indices, idescs); pfree(tuple); RelationUnsetLockForWrite(tgrel); heap_close(tgrel); pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1])); pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1])); /* update pg_class */ relrdesc = heap_openr(RelationRelationName); tuple = ClassNameIndexScan(relrdesc, stmt->relname); if (!PointerIsValid(tuple)) { heap_close(relrdesc); elog(ERROR, "CreateTrigger: relation %s not found in pg_class", stmt->relname); } ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1; RelationInvalidateHeapTuple(relrdesc, tuple); oldTID = tuple->t_ctid; heap_replace(relrdesc, &oldTID, tuple); CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, tuple); CatalogCloseIndices(Num_pg_class_indices, ridescs); pfree(tuple); heap_close(relrdesc); CommandCounterIncrement(); oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); FreeTriggerDesc(rel); rel->rd_rel->reltriggers = found + 1; RelationBuildTriggers(rel); MemoryContextSwitchTo(oldcxt); heap_close(rel); return; } void DropTrigger(DropTrigStmt * stmt) { Relation rel; Relation tgrel; HeapScanDesc tgscan; ScanKeyData key; Relation relrdesc; HeapTuple tuple; ItemPointerData oldTID; Relation ridescs[Num_pg_class_indices]; MemoryContext oldcxt; int found = 0; int tgfound = 0; #ifndef NO_SECURITY if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME)) elog(ERROR, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); #endif rel = heap_openr(stmt->relname); if (!RelationIsValid(rel)) elog(ERROR, "DropTrigger: there is no relation %s", stmt->relname); RelationSetLockForWrite(rel); tgrel = heap_openr(TriggerRelationName); RelationSetLockForWrite(tgrel); ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, ObjectIdEqualRegProcedure, rel->rd_id); tgscan = heap_beginscan(tgrel, 0, false, 1, &key); while (tuple = heap_getnext(tgscan, 0, (Buffer *) NULL), PointerIsValid(tuple)) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0) { heap_delete(tgrel, &tuple->t_ctid); tgfound++; } else found++; } if (tgfound == 0) elog(ERROR, "DropTrigger: there is no trigger %s on relation %s", stmt->trigname, stmt->relname); if (tgfound > 1) elog(NOTICE, "DropTrigger: found (and deleted) %d trigger %s on relation %s", tgfound, stmt->trigname, stmt->relname); heap_endscan(tgscan); RelationUnsetLockForWrite(tgrel); heap_close(tgrel); /* update pg_class */ relrdesc = heap_openr(RelationRelationName); tuple = ClassNameIndexScan(relrdesc, stmt->relname); if (!PointerIsValid(tuple)) { heap_close(relrdesc); elog(ERROR, "DropTrigger: relation %s not found in pg_class", stmt->relname); } ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found; RelationInvalidateHeapTuple(relrdesc, tuple); oldTID = tuple->t_ctid; heap_replace(relrdesc, &oldTID, tuple); CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, tuple); CatalogCloseIndices(Num_pg_class_indices, ridescs); pfree(tuple); heap_close(relrdesc); CommandCounterIncrement(); oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); FreeTriggerDesc(rel); rel->rd_rel->reltriggers = found; if (found > 0) RelationBuildTriggers(rel); MemoryContextSwitchTo(oldcxt); heap_close(rel); return; } void RelationRemoveTriggers(Relation rel) { Relation tgrel; HeapScanDesc tgscan; ScanKeyData key; HeapTuple tup; tgrel = heap_openr(TriggerRelationName); RelationSetLockForWrite(tgrel); ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, ObjectIdEqualRegProcedure, rel->rd_id); tgscan = heap_beginscan(tgrel, 0, false, 1, &key); while (tup = heap_getnext(tgscan, 0, (Buffer *) NULL), PointerIsValid(tup)) heap_delete(tgrel, &tup->t_ctid); heap_endscan(tgscan); RelationUnsetLockForWrite(tgrel); heap_close(tgrel); } void RelationBuildTriggers(Relation relation) { TriggerDesc *trigdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc)); int ntrigs = relation->rd_rel->reltriggers; Trigger *triggers = NULL; Trigger *build; Relation tgrel; Form_pg_trigger pg_trigger; Relation irel; ScanKeyData skey; HeapTuple tuple; IndexScanDesc sd; RetrieveIndexResult indexRes; Buffer buffer; ItemPointer iptr; struct varlena *val; bool isnull; int found; MemSet(trigdesc, 0, sizeof(TriggerDesc)); ScanKeyEntryInitialize(&skey, (bits16) 0x0, (AttrNumber) 1, (RegProcedure) ObjectIdEqualRegProcedure, ObjectIdGetDatum(relation->rd_id)); tgrel = heap_openr(TriggerRelationName); RelationSetLockForRead(tgrel); irel = index_openr(TriggerRelidIndex); sd = index_beginscan(irel, false, 1, &skey); for (found = 0;;) { indexRes = index_getnext(sd, ForwardScanDirection); if (!indexRes) break; iptr = &indexRes->heap_iptr; tuple = heap_fetch(tgrel, false, iptr, &buffer); pfree(indexRes); if (!HeapTupleIsValid(tuple)) continue; if (found == ntrigs) elog(ERROR, "RelationBuildTriggers: unexpected record found for rel %.*s", NAMEDATALEN, relation->rd_rel->relname.data); pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); if (triggers == NULL) triggers = (Trigger *) palloc(sizeof(Trigger)); else triggers = (Trigger *) repalloc(triggers, (found + 1) * sizeof(Trigger)); build = &(triggers[found]); build->tgname = nameout(&(pg_trigger->tgname)); build->tgfoid = pg_trigger->tgfoid; build->tgfunc = NULL; build->tgplfunc = NULL; build->tgtype = pg_trigger->tgtype; build->tgnargs = pg_trigger->tgnargs; memcpy(build->tgattr, &(pg_trigger->tgattr), 8 * sizeof(int16)); val = (struct varlena *) fastgetattr(tuple, Anum_pg_trigger_tgargs, tgrel->rd_att, &isnull); if (isnull) elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", NAMEDATALEN, relation->rd_rel->relname.data); if (build->tgnargs > 0) { char *p; int i; val = (struct varlena *) fastgetattr(tuple, Anum_pg_trigger_tgargs, tgrel->rd_att, &isnull); if (isnull) elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", NAMEDATALEN, relation->rd_rel->relname.data); p = (char *) VARDATA(val); build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *)); for (i = 0; i < build->tgnargs; i++) { build->tgargs[i] = (char *) palloc(strlen(p) + 1); strcpy(build->tgargs[i], p); p += strlen(p) + 1; } } else build->tgargs = NULL; found++; ReleaseBuffer(buffer); } if (found < ntrigs) elog(ERROR, "RelationBuildTriggers: %d record not found for rel %.*s", ntrigs - found, NAMEDATALEN, relation->rd_rel->relname.data); index_endscan(sd); pfree(sd); index_close(irel); RelationUnsetLockForRead(tgrel); heap_close(tgrel); /* Build trigdesc */ trigdesc->triggers = triggers; for (found = 0; found < ntrigs; found++) { build = &(triggers[found]); DescribeTrigger(trigdesc, build); } relation->trigdesc = trigdesc; } void FreeTriggerDesc(Relation relation) { TriggerDesc *trigdesc = relation->trigdesc; Trigger ***t; Trigger *trigger; int i; if (trigdesc == NULL) return; t = trigdesc->tg_before_statement; for (i = 0; i < 3; i++) if (t[i] != NULL) pfree(t[i]); t = trigdesc->tg_before_row; for (i = 0; i < 3; i++) if (t[i] != NULL) pfree(t[i]); t = trigdesc->tg_after_row; for (i = 0; i < 3; i++) if (t[i] != NULL) pfree(t[i]); t = trigdesc->tg_after_statement; for (i = 0; i < 3; i++) if (t[i] != NULL) pfree(t[i]); trigger = trigdesc->triggers; for (i = 0; i < relation->rd_rel->reltriggers; i++) { pfree(trigger->tgname); if (trigger->tgnargs > 0) { while (--(trigger->tgnargs) >= 0) pfree(trigger->tgargs[trigger->tgnargs]); pfree(trigger->tgargs); } trigger++; } pfree(trigdesc->triggers); pfree(trigdesc); relation->trigdesc = NULL; return; } static void DescribeTrigger(TriggerDesc * trigdesc, Trigger * trigger) { uint16 *n; Trigger ***t, ***tp; if (TRIGGER_FOR_ROW(trigger->tgtype)) /* Is ROW/STATEMENT * trigger */ { if (TRIGGER_FOR_BEFORE(trigger->tgtype)) { n = trigdesc->n_before_row; t = trigdesc->tg_before_row; } else { n = trigdesc->n_after_row; t = trigdesc->tg_after_row; } } else /* STATEMENT (NI) */ { if (TRIGGER_FOR_BEFORE(trigger->tgtype)) { n = trigdesc->n_before_statement; t = trigdesc->tg_before_statement; } else { n = trigdesc->n_after_statement; t = trigdesc->tg_after_statement; } } if (TRIGGER_FOR_INSERT(trigger->tgtype)) { tp = &(t[TRIGGER_EVENT_INSERT]); if (*tp == NULL) *tp = (Trigger **) palloc(sizeof(Trigger *)); else *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) * sizeof(Trigger *)); (*tp)[n[TRIGGER_EVENT_INSERT]] = trigger; (n[TRIGGER_EVENT_INSERT])++; } if (TRIGGER_FOR_DELETE(trigger->tgtype)) { tp = &(t[TRIGGER_EVENT_DELETE]); if (*tp == NULL) *tp = (Trigger **) palloc(sizeof(Trigger *)); else *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) * sizeof(Trigger *)); (*tp)[n[TRIGGER_EVENT_DELETE]] = trigger; (n[TRIGGER_EVENT_DELETE])++; } if (TRIGGER_FOR_UPDATE(trigger->tgtype)) { tp = &(t[TRIGGER_EVENT_UPDATE]); if (*tp == NULL) *tp = (Trigger **) palloc(sizeof(Trigger *)); else *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) * sizeof(Trigger *)); (*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger; (n[TRIGGER_EVENT_UPDATE])++; } } static HeapTuple ExecCallTriggerFunc(Trigger * trigger) { if (trigger->tgfunc != NULL) { return (HeapTuple) ((*(trigger->tgfunc)) ()); } if (trigger->tgplfunc == NULL) { HeapTuple procTuple; HeapTuple langTuple; Form_pg_proc procStruct; Form_pg_language langStruct; int nargs; procTuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(trigger->tgfoid), 0, 0, 0); if (!HeapTupleIsValid(procTuple)) { elog(ERROR, "ExecCallTriggerFunc(): Cache lookup for proc %ld failed", ObjectIdGetDatum(trigger->tgfoid)); } procStruct = (Form_pg_proc) GETSTRUCT(procTuple); langTuple = SearchSysCacheTuple(LANOID, ObjectIdGetDatum(procStruct->prolang), 0, 0, 0); if (!HeapTupleIsValid(langTuple)) { elog(ERROR, "ExecCallTriggerFunc(): Cache lookup for language %ld failed", ObjectIdGetDatum(procStruct->prolang)); } langStruct = (Form_pg_language) GETSTRUCT(langTuple); if (langStruct->lanispl == false) { fmgr_info(trigger->tgfoid, &(trigger->tgfunc), &nargs); return (HeapTuple) ((*(trigger->tgfunc)) ()); } fmgr_info(langStruct->lanplcallfoid, &(trigger->tgplfunc), &nargs); } return (HeapTuple) ((*(trigger->tgplfunc)) (trigger->tgfoid)); } HeapTuple ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple) { TriggerData *SaveTriggerData; int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT]; Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT]; HeapTuple newtuple = trigtuple; HeapTuple oldtuple; int i; SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData)); SaveTriggerData->tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; SaveTriggerData->tg_relation = rel; SaveTriggerData->tg_newtuple = NULL; for (i = 0; i < ntrigs; i++) { CurrentTriggerData = SaveTriggerData; CurrentTriggerData->tg_trigtuple = oldtuple = newtuple; CurrentTriggerData->tg_trigger = trigger[i]; newtuple = ExecCallTriggerFunc(trigger[i]); if (newtuple == NULL) break; else if (oldtuple != newtuple && oldtuple != trigtuple) pfree(oldtuple); } CurrentTriggerData = NULL; pfree(SaveTriggerData); return (newtuple); } void ExecARInsertTriggers(Relation rel, HeapTuple trigtuple) { TriggerData *SaveTriggerData; int ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT]; Trigger **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_INSERT]; int i; SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData)); SaveTriggerData->tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW; SaveTriggerData->tg_relation = rel; SaveTriggerData->tg_newtuple = NULL; for (i = 0; i < ntrigs; i++) { CurrentTriggerData = SaveTriggerData; CurrentTriggerData->tg_trigtuple = trigtuple; CurrentTriggerData->tg_trigger = trigger[i]; ExecCallTriggerFunc(trigger[i]); } CurrentTriggerData = NULL; pfree(SaveTriggerData); return; } bool ExecBRDeleteTriggers(Relation rel, ItemPointer tupleid) { TriggerData *SaveTriggerData; int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_DELETE]; Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_DELETE]; HeapTuple trigtuple; HeapTuple newtuple = NULL; int i; trigtuple = GetTupleForTrigger(rel, tupleid, true); if (trigtuple == NULL) return (false); SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData)); SaveTriggerData->tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; SaveTriggerData->tg_relation = rel; SaveTriggerData->tg_newtuple = NULL; for (i = 0; i < ntrigs; i++) { CurrentTriggerData = SaveTriggerData; CurrentTriggerData->tg_trigtuple = trigtuple; CurrentTriggerData->tg_trigger = trigger[i]; newtuple = ExecCallTriggerFunc(trigger[i]); if (newtuple == NULL) break; } CurrentTriggerData = NULL; pfree(SaveTriggerData); pfree(trigtuple); return ((newtuple == NULL) ? false : true); } void ExecARDeleteTriggers(Relation rel, ItemPointer tupleid) { TriggerData *SaveTriggerData; int ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE]; Trigger **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_DELETE]; HeapTuple trigtuple; int i; trigtuple = GetTupleForTrigger(rel, tupleid, false); Assert(trigtuple != NULL); SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData)); SaveTriggerData->tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW; SaveTriggerData->tg_relation = rel; SaveTriggerData->tg_newtuple = NULL; for (i = 0; i < ntrigs; i++) { CurrentTriggerData = SaveTriggerData; CurrentTriggerData->tg_trigtuple = trigtuple; CurrentTriggerData->tg_trigger = trigger[i]; ExecCallTriggerFunc(trigger[i]); } CurrentTriggerData = NULL; pfree(SaveTriggerData); pfree(trigtuple); return; } HeapTuple ExecBRUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple) { TriggerData *SaveTriggerData; int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE]; Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_UPDATE]; HeapTuple trigtuple; HeapTuple oldtuple; HeapTuple intuple = newtuple; int i; trigtuple = GetTupleForTrigger(rel, tupleid, true); if (trigtuple == NULL) return (NULL); SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData)); SaveTriggerData->tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; SaveTriggerData->tg_relation = rel; for (i = 0; i < ntrigs; i++) { CurrentTriggerData = SaveTriggerData; CurrentTriggerData->tg_trigtuple = trigtuple; CurrentTriggerData->tg_newtuple = oldtuple = newtuple; CurrentTriggerData->tg_trigger = trigger[i]; newtuple = ExecCallTriggerFunc(trigger[i]); if (newtuple == NULL) break; else if (oldtuple != newtuple && oldtuple != intuple) pfree(oldtuple); } CurrentTriggerData = NULL; pfree(SaveTriggerData); pfree(trigtuple); return (newtuple); } void ExecARUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple newtuple) { TriggerData *SaveTriggerData; int ntrigs = rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE]; Trigger **trigger = rel->trigdesc->tg_after_row[TRIGGER_EVENT_UPDATE]; HeapTuple trigtuple; int i; trigtuple = GetTupleForTrigger(rel, tupleid, false); Assert(trigtuple != NULL); SaveTriggerData = (TriggerData *) palloc(sizeof(TriggerData)); SaveTriggerData->tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW; SaveTriggerData->tg_relation = rel; for (i = 0; i < ntrigs; i++) { CurrentTriggerData = SaveTriggerData; CurrentTriggerData->tg_trigtuple = trigtuple; CurrentTriggerData->tg_newtuple = newtuple; CurrentTriggerData->tg_trigger = trigger[i]; ExecCallTriggerFunc(trigger[i]); } CurrentTriggerData = NULL; pfree(SaveTriggerData); pfree(trigtuple); return; } static HeapTuple GetTupleForTrigger(Relation relation, ItemPointer tid, bool before) { ItemId lp; HeapTuple tuple; PageHeader dp; Buffer b; b = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); if (!BufferIsValid(b)) elog(ERROR, "GetTupleForTrigger: failed ReadBuffer"); dp = (PageHeader) BufferGetPage(b); lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); Assert(ItemIdIsUsed(lp)); tuple = (HeapTuple) PageGetItem((Page) dp, lp); if (before) { if (TupleUpdatedByCurXactAndCmd(tuple)) { elog(NOTICE, "GetTupleForTrigger: Non-functional delete/update"); ReleaseBuffer(b); return (NULL); } HeapTupleSatisfies(lp, relation, b, dp, false, 0, (ScanKey) NULL, tuple); if (!tuple) { ReleaseBuffer(b); elog(ERROR, "GetTupleForTrigger: (am)invalid tid"); } } tuple = heap_copytuple(tuple); ReleaseBuffer(b); return (tuple); }