/*------------------------------------------------------------------------- * * trigger.c * PostgreSQL TRIGGERs support code. * *------------------------------------------------------------------------- */ #include #include "postgres.h" #include "access/genam.h" #include "access/heapam.h" #include "access/valid.h" #include "access/xact.h" #include "catalog/catalog.h" #include "catalog/catname.h" #include "catalog/indexing.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "commands/trigger.h" #include "fmgr.h" #include "nodes/memnodes.h" #include "nodes/parsenodes.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" #include "executor/executor.h" #ifndef NO_SECURITY #include "miscadmin.h" #include "utils/acl.h" #endif DLLIMPORT TriggerData *CurrentTriggerData = NULL; void RelationBuildTriggers(Relation relation); void FreeTriggerDesc(Relation relation); static void DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger); static HeapTuple GetTupleForTrigger(EState *estate, ItemPointer tid, TupleTableSlot **newSlot); 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 pgrel; HeapTuple tuple; Relation idescs[Num_pg_trigger_indices]; Relation ridescs[Num_pg_class_indices]; MemoryContext oldcxt; Oid fargtypes[8]; int found = 0; int i; if (!allowSystemTableMods && 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); LockRelation(rel, AccessExclusiveLock); 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); LockRelation(tgrel, AccessExclusiveLock); ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, F_OIDEQ, RelationGetRelid(rel)); tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key); while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0))) { 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), Int32GetDatum(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(RelationGetRelid(rel)); values[Anum_pg_trigger_tgname - 1] = NameGetDatum(namein(stmt->trigname)); values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(tuple->t_data->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); UnlockRelation(tgrel, AccessExclusiveLock); heap_close(tgrel); pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1])); pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1])); /* update pg_class */ tuple = SearchSysCacheTupleCopy(RELNAME, PointerGetDatum(stmt->relname), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "CreateTrigger: relation %s not found in pg_class", stmt->relname); pgrel = heap_openr(RelationRelationName); ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1; RelationInvalidateHeapTuple(pgrel, tuple); heap_replace(pgrel, &tuple->t_self, tuple, NULL); CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple); CatalogCloseIndices(Num_pg_class_indices, ridescs); pfree(tuple); heap_close(pgrel); 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 pgrel; HeapTuple tuple; 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); LockRelation(rel, AccessExclusiveLock); tgrel = heap_openr(TriggerRelationName); LockRelation(tgrel, AccessExclusiveLock); ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, F_OIDEQ, RelationGetRelid(rel)); tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key); while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0))) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0) { heap_delete(tgrel, &tuple->t_self, NULL); 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); UnlockRelation(tgrel, AccessExclusiveLock); heap_close(tgrel); tuple = SearchSysCacheTupleCopy(RELNAME, PointerGetDatum(stmt->relname), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "DropTrigger: relation %s not found in pg_class", stmt->relname); /* update pg_class */ pgrel = heap_openr(RelationRelationName); ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found; RelationInvalidateHeapTuple(pgrel, tuple); heap_replace(pgrel, &tuple->t_self, tuple, NULL); CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple); CatalogCloseIndices(Num_pg_class_indices, ridescs); pfree(tuple); heap_close(pgrel); 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); LockRelation(tgrel, AccessExclusiveLock); ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, F_OIDEQ, RelationGetRelid(rel)); tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key); while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0))) heap_delete(tgrel, &tup->t_self, NULL); heap_endscan(tgscan); UnlockRelation(tgrel, AccessExclusiveLock); 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; HeapTupleData tuple; IndexScanDesc sd; RetrieveIndexResult indexRes; Buffer buffer; struct varlena *val; bool isnull; int found; MemSet(trigdesc, 0, sizeof(TriggerDesc)); ScanKeyEntryInitialize(&skey, (bits16) 0x0, (AttrNumber) 1, (RegProcedure) F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); tgrel = heap_openr(TriggerRelationName); irel = index_openr(TriggerRelidIndex); sd = index_beginscan(irel, false, 1, &skey); for (found = 0;;) { indexRes = index_getnext(sd, ForwardScanDirection); if (!indexRes) break; tuple.t_self = indexRes->heap_iptr; heap_fetch(tgrel, SnapshotNow, &tuple, &buffer); pfree(indexRes); if (!tuple.t_data) 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.fn_addr = 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); 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.fn_addr == NULL) fmgr_info(trigger->tgfoid, &trigger->tgfunc); if (trigger->tgfunc.fn_plhandler != NULL) { return (HeapTuple) (*(trigger->tgfunc.fn_plhandler)) (&trigger->tgfunc); } return (HeapTuple) ((*fmgr_faddr(&trigger->tgfunc)) ()); } 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(EState *estate, ItemPointer tupleid) { Relation rel = estate->es_result_relation_info->ri_RelationDesc; 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; TupleTableSlot *newSlot; int i; trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot); 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; if (newtuple != trigtuple) pfree(newtuple); } CurrentTriggerData = NULL; pfree(SaveTriggerData); pfree(trigtuple); return (newtuple == NULL) ? false : true; } void ExecARDeleteTriggers(EState *estate, ItemPointer tupleid) { Relation rel = estate->es_result_relation_info->ri_RelationDesc; 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(estate, tupleid, NULL); 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(EState *estate, ItemPointer tupleid, HeapTuple newtuple) { Relation rel = estate->es_result_relation_info->ri_RelationDesc; 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; TupleTableSlot *newSlot; int i; trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot); if (trigtuple == NULL) return NULL; /* * In READ COMMITTED isolevel it's possible that newtuple * was changed due to concurrent update. */ if (newSlot != NULL) intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot); 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(EState *estate, ItemPointer tupleid, HeapTuple newtuple) { Relation rel = estate->es_result_relation_info->ri_RelationDesc; 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(estate, tupleid, NULL); 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; } extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid); static HeapTuple GetTupleForTrigger(EState *estate, ItemPointer tid, TupleTableSlot **newSlot) { Relation relation = estate->es_result_relation_info->ri_RelationDesc; HeapTupleData tuple; HeapTuple result; Buffer buffer; if (newSlot != NULL) { int test; /* * mark tuple for update */ *newSlot = NULL; tuple.t_self = *tid; ltrmark:; test = heap_mark4update(relation, &tuple, &buffer); switch (test) { case HeapTupleSelfUpdated: ReleaseBuffer(buffer); return(NULL); case HeapTupleMayBeUpdated: break; case HeapTupleUpdated: ReleaseBuffer(buffer); if (XactIsoLevel == XACT_SERIALIZABLE) elog(ERROR, "Can't serialize access due to concurrent update"); else if (!(ItemPointerEquals(&(tuple.t_self), tid))) { TupleTableSlot *epqslot = EvalPlanQual(estate, estate->es_result_relation_info->ri_RangeTableIndex, &(tuple.t_self)); if (!(TupIsNull(epqslot))) { *tid = tuple.t_self; *newSlot = epqslot; goto ltrmark; } } /* * if tuple was deleted or PlanQual failed * for updated tuple - we have not process * this tuple! */ return(NULL); default: ReleaseBuffer(buffer); elog(ERROR, "Unknown status %u from heap_mark4update", test); return(NULL); } } else { PageHeader dp; ItemId lp; buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); if (!BufferIsValid(buffer)) elog(ERROR, "GetTupleForTrigger: failed ReadBuffer"); dp = (PageHeader) BufferGetPage(buffer); lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); Assert(ItemIdIsUsed(lp)); tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); tuple.t_len = ItemIdGetLength(lp); tuple.t_self = *tid; } result = heap_copytuple(&tuple); ReleaseBuffer(buffer); return result; }