#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
Go to the source code of this file.
#define AFTER_TRIGGER_DEFERRABLE 0x00000020 |
Definition at line 64 of file trigger.h.
Referenced by afterTriggerCheckState(), and AfterTriggerSaveEvent().
#define AFTER_TRIGGER_INITDEFERRED 0x00000040 |
Definition at line 65 of file trigger.h.
Referenced by afterTriggerCheckState(), and AfterTriggerSaveEvent().
#define CALLED_AS_TRIGGER | ( | fcinfo | ) | ((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData)) |
Definition at line 24 of file trigger.h.
Referenced by autoinc(), check_foreign_key(), check_primary_key(), compute_function_hashkey(), do_compile(), funny_dup17(), insert_username(), lo_manage(), moddatetime(), plperl_call_handler(), plpgsql_call_handler(), plpython_call_handler(), pltcl_handler(), PLy_exec_trigger(), ri_CheckTrigger(), suppress_redundant_updates_trigger(), timetravel(), triggered_change_notification(), tsa_tsearch2(), tsvector_update_trigger(), ttdummy(), and unique_key_recheck().
#define RI_TRIGGER_FK 2 |
Definition at line 203 of file trigger.h.
Referenced by AfterTriggerSaveEvent().
#define RI_TRIGGER_NONE 0 |
Definition at line 204 of file trigger.h.
Referenced by AfterTriggerSaveEvent(), and CreateTrigger().
#define RI_TRIGGER_PK 1 |
Definition at line 202 of file trigger.h.
Referenced by AfterTriggerSaveEvent().
#define SESSION_REPLICATION_ROLE_REPLICA 1 |
Definition at line 98 of file trigger.h.
Referenced by filter_event_trigger(), matchLocks(), and TriggerEnabled().
#define TRIGGER_DISABLED 'D' |
Definition at line 109 of file trigger.h.
Referenced by ATExecCmd(), BuildEventTriggerCache(), and TriggerEnabled().
#define TRIGGER_EVENT_DELETE 0x00000001 |
Definition at line 50 of file trigger.h.
Referenced by AfterTriggerSaveEvent(), ExecARDeleteTriggers(), ExecASDeleteTriggers(), ExecBRDeleteTriggers(), ExecBSDeleteTriggers(), and ExecIRDeleteTriggers().
#define TRIGGER_EVENT_INSERT 0x00000000 |
Definition at line 49 of file trigger.h.
Referenced by AfterTriggerSaveEvent(), ExecARInsertTriggers(), ExecASInsertTriggers(), ExecBRInsertTriggers(), ExecBSInsertTriggers(), ExecIRInsertTriggers(), and validateForeignKeyConstraint().
#define TRIGGER_EVENT_OPMASK 0x00000003 |
Definition at line 53 of file trigger.h.
Referenced by AfterTriggerExecute().
#define TRIGGER_EVENT_ROW 0x00000004 |
Definition at line 55 of file trigger.h.
Referenced by AfterTriggerSaveEvent(), ExecBRDeleteTriggers(), ExecBRInsertTriggers(), ExecBRUpdateTriggers(), ExecIRDeleteTriggers(), ExecIRInsertTriggers(), and ExecIRUpdateTriggers().
#define TRIGGER_EVENT_TRUNCATE 0x00000003 |
Definition at line 52 of file trigger.h.
Referenced by AfterTriggerSaveEvent(), ExecASTruncateTriggers(), and ExecBSTruncateTriggers().
#define TRIGGER_EVENT_UPDATE 0x00000002 |
Definition at line 51 of file trigger.h.
Referenced by AfterTriggerSaveEvent(), ExecARUpdateTriggers(), ExecASUpdateTriggers(), ExecBRUpdateTriggers(), ExecBSUpdateTriggers(), and ExecIRUpdateTriggers().
#define TRIGGER_FIRED_AFTER | ( | event | ) | (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_AFTER) |
Definition at line 88 of file trigger.h.
Referenced by plperl_trigger_build_args(), plpgsql_exec_trigger(), pltcl_trigger_handler(), PLy_trigger_build_args(), ri_CheckTrigger(), triggered_change_notification(), and unique_key_recheck().
#define TRIGGER_FIRED_BEFORE | ( | event | ) | (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_BEFORE) |
Definition at line 85 of file trigger.h.
Referenced by autoinc(), funny_dup17(), insert_username(), moddatetime(), plperl_trigger_build_args(), plpgsql_exec_trigger(), pltcl_trigger_handler(), PLy_trigger_build_args(), suppress_redundant_updates_trigger(), timetravel(), tsvector_update_trigger(), and ttdummy().
#define TRIGGER_FIRED_BY_DELETE | ( | event | ) | (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_DELETE) |
Definition at line 70 of file trigger.h.
Referenced by check_primary_key(), lo_manage(), plperl_trigger_build_args(), plperl_trigger_handler(), plpgsql_exec_trigger(), pltcl_trigger_handler(), PLy_trigger_build_args(), ri_CheckTrigger(), and triggered_change_notification().
#define TRIGGER_FIRED_BY_INSERT | ( | event | ) | (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_INSERT) |
Definition at line 67 of file trigger.h.
Referenced by autoinc(), check_foreign_key(), check_primary_key(), insert_username(), moddatetime(), plperl_trigger_build_args(), plperl_trigger_handler(), plpgsql_exec_trigger(), pltcl_trigger_handler(), PLy_exec_trigger(), PLy_trigger_build_args(), ri_CheckTrigger(), timetravel(), triggered_change_notification(), tsvector_update_trigger(), ttdummy(), and unique_key_recheck().
#define TRIGGER_FIRED_BY_TRUNCATE | ( | event | ) | (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_TRUNCATE) |
Definition at line 76 of file trigger.h.
Referenced by plperl_trigger_build_args(), plperl_trigger_handler(), plpgsql_exec_trigger(), pltcl_trigger_handler(), and PLy_trigger_build_args().
#define TRIGGER_FIRED_BY_UPDATE | ( | event | ) | (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE) |
Definition at line 73 of file trigger.h.
Referenced by AfterTriggerSaveEvent(), autoinc(), check_foreign_key(), insert_username(), lo_manage(), moddatetime(), plperl_trigger_build_args(), plperl_trigger_handler(), plpgsql_exec_trigger(), pltcl_trigger_handler(), PLy_exec_trigger(), PLy_trigger_build_args(), ri_CheckTrigger(), RI_FKey_check(), suppress_redundant_updates_trigger(), timetravel(), triggered_change_notification(), TriggerEnabled(), tsvector_update_trigger(), ttdummy(), and unique_key_recheck().
#define TRIGGER_FIRED_FOR_ROW | ( | event | ) | ((event) & TRIGGER_EVENT_ROW) |
Definition at line 79 of file trigger.h.
Referenced by autoinc(), check_foreign_key(), check_primary_key(), insert_username(), moddatetime(), plperl_trigger_build_args(), plpgsql_exec_trigger(), pltcl_trigger_handler(), PLy_trigger_build_args(), ri_CheckTrigger(), suppress_redundant_updates_trigger(), timetravel(), triggered_change_notification(), tsvector_update_trigger(), ttdummy(), and unique_key_recheck().
#define TRIGGER_FIRED_FOR_STATEMENT | ( | event | ) | (!TRIGGER_FIRED_FOR_ROW(event)) |
Definition at line 82 of file trigger.h.
Referenced by plperl_trigger_build_args(), plpgsql_exec_trigger(), pltcl_trigger_handler(), and PLy_trigger_build_args().
#define TRIGGER_FIRED_INSTEAD | ( | event | ) | (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_INSTEAD) |
Definition at line 91 of file trigger.h.
Referenced by plperl_trigger_build_args(), plpgsql_exec_trigger(), pltcl_trigger_handler(), and PLy_trigger_build_args().
#define TRIGGER_FIRES_ALWAYS 'A' |
Definition at line 107 of file trigger.h.
Referenced by ATExecCmd().
#define TRIGGER_FIRES_ON_ORIGIN 'O' |
Definition at line 106 of file trigger.h.
Referenced by ATExecCmd(), CreateTrigger(), filter_event_trigger(), insert_event_trigger_tuple(), and TriggerEnabled().
#define TRIGGER_FIRES_ON_REPLICA 'R' |
Definition at line 108 of file trigger.h.
Referenced by ATExecCmd(), filter_event_trigger(), and TriggerEnabled().
typedef struct TriggerData TriggerData |
typedef uint32 TriggerEvent |
void AfterTriggerBeginQuery | ( | void | ) |
Definition at line 3716 of file trigger.c.
References Assert, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, NULL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, repalloc(), AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.
Referenced by CopyFrom(), ExecuteTruncate(), and standard_ExecutorStart().
{ AfterTriggerEventList *events; /* Must be inside a transaction */ Assert(afterTriggers != NULL); /* Increase the query stack depth */ afterTriggers->query_depth++; /* * Allocate more space in the query stack if needed. */ if (afterTriggers->query_depth >= afterTriggers->maxquerydepth) { /* repalloc will keep the stack in the same context */ int new_alloc = afterTriggers->maxquerydepth * 2; afterTriggers->query_stack = (AfterTriggerEventList *) repalloc(afterTriggers->query_stack, new_alloc * sizeof(AfterTriggerEventList)); afterTriggers->maxquerydepth = new_alloc; } /* Initialize this query's list to empty */ events = &afterTriggers->query_stack[afterTriggers->query_depth]; events->head = NULL; events->tail = NULL; events->tailfree = NULL; }
void AfterTriggerBeginSubXact | ( | void | ) |
Definition at line 3905 of file trigger.c.
References DEFTRIG_INITALLOC, AfterTriggersData::depth_stack, AfterTriggersData::events, AfterTriggersData::events_stack, AfterTriggersData::firing_counter, AfterTriggersData::firing_stack, GetCurrentTransactionNestLevel(), AfterTriggersData::maxtransdepth, MemoryContextSwitchTo(), NULL, palloc(), AfterTriggersData::query_depth, repalloc(), AfterTriggersData::state_stack, and TopTransactionContext.
Referenced by StartSubTransaction().
{ int my_level = GetCurrentTransactionNestLevel(); /* * Ignore call if the transaction is in aborted state. (Probably * shouldn't happen?) */ if (afterTriggers == NULL) return; /* * Allocate more space in the stacks if needed. (Note: because the * minimum nest level of a subtransaction is 2, we waste the first couple * entries of each array; not worth the notational effort to avoid it.) */ while (my_level >= afterTriggers->maxtransdepth) { if (afterTriggers->maxtransdepth == 0) { MemoryContext old_cxt; old_cxt = MemoryContextSwitchTo(TopTransactionContext); #define DEFTRIG_INITALLOC 8 afterTriggers->state_stack = (SetConstraintState *) palloc(DEFTRIG_INITALLOC * sizeof(SetConstraintState)); afterTriggers->events_stack = (AfterTriggerEventList *) palloc(DEFTRIG_INITALLOC * sizeof(AfterTriggerEventList)); afterTriggers->depth_stack = (int *) palloc(DEFTRIG_INITALLOC * sizeof(int)); afterTriggers->firing_stack = (CommandId *) palloc(DEFTRIG_INITALLOC * sizeof(CommandId)); afterTriggers->maxtransdepth = DEFTRIG_INITALLOC; MemoryContextSwitchTo(old_cxt); } else { /* repalloc will keep the stacks in the same context */ int new_alloc = afterTriggers->maxtransdepth * 2; afterTriggers->state_stack = (SetConstraintState *) repalloc(afterTriggers->state_stack, new_alloc * sizeof(SetConstraintState)); afterTriggers->events_stack = (AfterTriggerEventList *) repalloc(afterTriggers->events_stack, new_alloc * sizeof(AfterTriggerEventList)); afterTriggers->depth_stack = (int *) repalloc(afterTriggers->depth_stack, new_alloc * sizeof(int)); afterTriggers->firing_stack = (CommandId *) repalloc(afterTriggers->firing_stack, new_alloc * sizeof(CommandId)); afterTriggers->maxtransdepth = new_alloc; } } /* * Push the current information into the stack. The SET CONSTRAINTS state * is not saved until/unless changed. Likewise, we don't make a * per-subtransaction event context until needed. */ afterTriggers->state_stack[my_level] = NULL; afterTriggers->events_stack[my_level] = afterTriggers->events; afterTriggers->depth_stack[my_level] = afterTriggers->query_depth; afterTriggers->firing_stack[my_level] = afterTriggers->firing_counter; }
void AfterTriggerBeginXact | ( | void | ) |
Definition at line 3670 of file trigger.c.
References Assert, AfterTriggersData::depth_stack, AfterTriggersData::event_cxt, AfterTriggersData::events, AfterTriggersData::events_stack, AfterTriggersData::firing_counter, AfterTriggersData::firing_stack, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, MemoryContextAlloc(), NULL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, SetConstraintStateCreate(), AfterTriggersData::state, AfterTriggersData::state_stack, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TopTransactionContext.
Referenced by StartTransaction().
{ Assert(afterTriggers == NULL); /* * Build empty after-trigger state structure */ afterTriggers = (AfterTriggers) MemoryContextAlloc(TopTransactionContext, sizeof(AfterTriggersData)); afterTriggers->firing_counter = (CommandId) 1; /* mustn't be 0 */ afterTriggers->state = SetConstraintStateCreate(8); afterTriggers->events.head = NULL; afterTriggers->events.tail = NULL; afterTriggers->events.tailfree = NULL; afterTriggers->query_depth = -1; /* We initialize the query stack to a reasonable size */ afterTriggers->query_stack = (AfterTriggerEventList *) MemoryContextAlloc(TopTransactionContext, 8 * sizeof(AfterTriggerEventList)); afterTriggers->maxquerydepth = 8; /* Context for events is created only when needed */ afterTriggers->event_cxt = NULL; /* Subtransaction stack is empty until/unless needed */ afterTriggers->state_stack = NULL; afterTriggers->events_stack = NULL; afterTriggers->depth_stack = NULL; afterTriggers->firing_stack = NULL; afterTriggers->maxtransdepth = 0; }
void AfterTriggerEndQuery | ( | EState * | estate | ) |
Definition at line 3761 of file trigger.c.
References afterTriggerFreeEventList(), afterTriggerInvokeEvents(), afterTriggerMarkEvents(), Assert, AfterTriggersData::events, AfterTriggersData::firing_counter, NULL, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.
Referenced by CopyFrom(), ExecuteTruncate(), and standard_ExecutorFinish().
{ AfterTriggerEventList *events; /* Must be inside a transaction */ Assert(afterTriggers != NULL); /* Must be inside a query, too */ Assert(afterTriggers->query_depth >= 0); /* * Process all immediate-mode triggers queued by the query, and move the * deferred ones to the main list of deferred events. * * Notice that we decide which ones will be fired, and put the deferred * ones on the main list, before anything is actually fired. This ensures * reasonably sane behavior if a trigger function does SET CONSTRAINTS ... * IMMEDIATE: all events we have decided to defer will be available for it * to fire. * * We loop in case a trigger queues more events at the same query level * (is that even possible?). Be careful here: firing a trigger could * result in query_stack being repalloc'd, so we can't save its address * across afterTriggerInvokeEvents calls. * * If we find no firable events, we don't have to increment * firing_counter. */ for (;;) { events = &afterTriggers->query_stack[afterTriggers->query_depth]; if (afterTriggerMarkEvents(events, &afterTriggers->events, true)) { CommandId firing_id = afterTriggers->firing_counter++; /* OK to delete the immediate events after processing them */ if (afterTriggerInvokeEvents(events, firing_id, estate, true)) break; /* all fired */ } else break; } /* Release query-local storage for events */ afterTriggerFreeEventList(&afterTriggers->query_stack[afterTriggers->query_depth]); afterTriggers->query_depth--; }
void AfterTriggerEndSubXact | ( | bool | isCommit | ) |
Definition at line 3980 of file trigger.c.
References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, afterTriggerFreeEventList(), afterTriggerRestoreEventList(), Assert, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_firing_id, AfterTriggersData::depth_stack, AfterTriggersData::events, AfterTriggersData::events_stack, AfterTriggersData::firing_stack, for_each_event_chunk, GetCurrentTransactionNestLevel(), GetTriggerSharedData, AfterTriggersData::maxtransdepth, NULL, pfree(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, and AfterTriggersData::state_stack.
Referenced by AbortSubTransaction(), and CommitSubTransaction().
{ int my_level = GetCurrentTransactionNestLevel(); SetConstraintState state; AfterTriggerEvent event; AfterTriggerEventChunk *chunk; CommandId subxact_firing_id; /* * Ignore call if the transaction is in aborted state. (Probably * unneeded) */ if (afterTriggers == NULL) return; /* * Pop the prior state if needed. */ if (isCommit) { Assert(my_level < afterTriggers->maxtransdepth); /* If we saved a prior state, we don't need it anymore */ state = afterTriggers->state_stack[my_level]; if (state != NULL) pfree(state); /* this avoids double pfree if error later: */ afterTriggers->state_stack[my_level] = NULL; Assert(afterTriggers->query_depth == afterTriggers->depth_stack[my_level]); } else { /* * Aborting. It is possible subxact start failed before calling * AfterTriggerBeginSubXact, in which case we mustn't risk touching * stack levels that aren't there. */ if (my_level >= afterTriggers->maxtransdepth) return; /* * Release any event lists from queries being aborted, and restore * query_depth to its pre-subxact value. */ while (afterTriggers->query_depth > afterTriggers->depth_stack[my_level]) { afterTriggerFreeEventList(&afterTriggers->query_stack[afterTriggers->query_depth]); afterTriggers->query_depth--; } Assert(afterTriggers->query_depth == afterTriggers->depth_stack[my_level]); /* * Restore the global deferred-event list to its former length, * discarding any events queued by the subxact. */ afterTriggerRestoreEventList(&afterTriggers->events, &afterTriggers->events_stack[my_level]); /* * Restore the trigger state. If the saved state is NULL, then this * subxact didn't save it, so it doesn't need restoring. */ state = afterTriggers->state_stack[my_level]; if (state != NULL) { pfree(afterTriggers->state); afterTriggers->state = state; } /* this avoids double pfree if error later: */ afterTriggers->state_stack[my_level] = NULL; /* * Scan for any remaining deferred events that were marked DONE or IN * PROGRESS by this subxact or a child, and un-mark them. We can * recognize such events because they have a firing ID greater than or * equal to the firing_counter value we saved at subtransaction start. * (This essentially assumes that the current subxact includes all * subxacts started after it.) */ subxact_firing_id = afterTriggers->firing_stack[my_level]; for_each_event_chunk(event, chunk, afterTriggers->events) { AfterTriggerShared evtshared = GetTriggerSharedData(event); if (event->ate_flags & (AFTER_TRIGGER_DONE | AFTER_TRIGGER_IN_PROGRESS)) { if (evtshared->ats_firing_id >= subxact_firing_id) event->ate_flags &= ~(AFTER_TRIGGER_DONE | AFTER_TRIGGER_IN_PROGRESS); } } } }
void AfterTriggerEndXact | ( | bool | isCommit | ) |
Definition at line 3882 of file trigger.c.
References AfterTriggersData::event_cxt, and MemoryContextDelete().
Referenced by AbortTransaction(), CommitTransaction(), and PrepareTransaction().
{ /* * Forget everything we know about AFTER triggers. * * Since all the info is in TopTransactionContext or children thereof, we * don't really need to do anything to reclaim memory. However, the * pending-events list could be large, and so it's useful to discard it as * soon as possible --- especially if we are aborting because we ran out * of memory for the list! */ if (afterTriggers && afterTriggers->event_cxt) MemoryContextDelete(afterTriggers->event_cxt); afterTriggers = NULL; }
void AfterTriggerFireDeferred | ( | void | ) |
Definition at line 3823 of file trigger.c.
References afterTriggerInvokeEvents(), afterTriggerMarkEvents(), Assert, AfterTriggersData::events, AfterTriggersData::firing_counter, GetTransactionSnapshot(), AfterTriggerEventList::head, NULL, PopActiveSnapshot(), PushActiveSnapshot(), and AfterTriggersData::query_depth.
Referenced by CommitTransaction(), and PrepareTransaction().
{ AfterTriggerEventList *events; bool snap_pushed = false; /* Must be inside a transaction */ Assert(afterTriggers != NULL); /* ... but not inside a query */ Assert(afterTriggers->query_depth == -1); /* * If there are any triggers to fire, make sure we have set a snapshot for * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we * can't assume ActiveSnapshot is valid on entry.) */ events = &afterTriggers->events; if (events->head != NULL) { PushActiveSnapshot(GetTransactionSnapshot()); snap_pushed = true; } /* * Run all the remaining triggers. Loop until they are all gone, in case * some trigger queues more for us to do. */ while (afterTriggerMarkEvents(events, NULL, false)) { CommandId firing_id = afterTriggers->firing_counter++; if (afterTriggerInvokeEvents(events, firing_id, NULL, true)) break; /* all fired */ } /* * We don't bother freeing the event list, since it will go away anyway * (and more efficiently than via pfree) in AfterTriggerEndXact. */ if (snap_pushed) PopActiveSnapshot(); }
Definition at line 4451 of file trigger.c.
References AFTER_TRIGGER_DONE, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_relid, AfterTriggersData::events, for_each_event_chunk, GetTriggerSharedData, NULL, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.
Referenced by CheckTableNotInUse().
{ AfterTriggerEvent event; AfterTriggerEventChunk *chunk; int depth; /* No-op if we aren't in a transaction. (Shouldn't happen?) */ if (afterTriggers == NULL) return false; /* Scan queued events */ for_each_event_chunk(event, chunk, afterTriggers->events) { AfterTriggerShared evtshared = GetTriggerSharedData(event); /* * We can ignore completed events. (Even if a DONE flag is rolled * back by subxact abort, it's OK because the effects of the TRUNCATE * or whatever must get rolled back too.) */ if (event->ate_flags & AFTER_TRIGGER_DONE) continue; if (evtshared->ats_relid == relid) return true; } /* * Also scan events queued by incomplete queries. This could only matter * if TRUNCATE/etc is executed by a function or trigger within an updating * query on the same relation, which is pretty perverse, but let's check. */ for (depth = 0; depth <= afterTriggers->query_depth; depth++) { for_each_event_chunk(event, chunk, afterTriggers->query_stack[depth]) { AfterTriggerShared evtshared = GetTriggerSharedData(event); if (event->ate_flags & AFTER_TRIGGER_DONE) continue; if (evtshared->ats_relid == relid) return true; } } return false; }
void AfterTriggerSetState | ( | ConstraintsSetStmt * | stmt | ) |
Definition at line 4155 of file trigger.c.
References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, Anum_pg_constraint_conname, Anum_pg_constraint_connamespace, Anum_pg_trigger_tgconstraint, BTEqualStrategyNumber, RangeVar::catalogname, ConstraintNameNspIndexId, ConstraintRelationId, ConstraintsSetStmt::constraints, CStringGetDatum, ConstraintsSetStmt::deferred, elog, ereport, errcode(), errmsg(), ERROR, AfterTriggersData::events, fetch_search_path(), AfterTriggersData::firing_counter, get_database_name(), GetCurrentTransactionNestLevel(), GETSTRUCT, GetTransactionSnapshot(), heap_close, heap_open(), HeapTupleGetOid, HeapTupleIsValid, IsSubTransaction(), lappend_oid(), lfirst, lfirst_oid, list_free(), list_make1_oid, LookupExplicitNamespace(), MyDatabaseId, NIL, NULL, SetConstraintStateData::numstates, ObjectIdGetDatum, PopActiveSnapshot(), PushActiveSnapshot(), RangeVar::relname, ScanKeyInit(), RangeVar::schemaname, SetConstraintTriggerData::sct_tgisdeferred, SetConstraintTriggerData::sct_tgoid, SetConstraintStateAddItem(), SetConstraintStateCopy(), SnapshotNow, AfterTriggersData::state, AfterTriggersData::state_stack, systable_beginscan(), systable_endscan(), systable_getnext(), TriggerConstraintIndexId, TriggerRelationId, and SetConstraintStateData::trigstates.
Referenced by standard_ProcessUtility().
{ int my_level = GetCurrentTransactionNestLevel(); /* * Ignore call if we aren't in a transaction. (Shouldn't happen?) */ if (afterTriggers == NULL) return; /* * If in a subtransaction, and we didn't save the current state already, * save it so it can be restored if the subtransaction aborts. */ if (my_level > 1 && afterTriggers->state_stack[my_level] == NULL) { afterTriggers->state_stack[my_level] = SetConstraintStateCopy(afterTriggers->state); } /* * Handle SET CONSTRAINTS ALL ... */ if (stmt->constraints == NIL) { /* * Forget any previous SET CONSTRAINTS commands in this transaction. */ afterTriggers->state->numstates = 0; /* * Set the per-transaction ALL state to known. */ afterTriggers->state->all_isset = true; afterTriggers->state->all_isdeferred = stmt->deferred; } else { Relation conrel; Relation tgrel; List *conoidlist = NIL; List *tgoidlist = NIL; ListCell *lc; /* * Handle SET CONSTRAINTS constraint-name [, ...] * * First, identify all the named constraints and make a list of their * OIDs. Since, unlike the SQL spec, we allow multiple constraints of * the same name within a schema, the specifications are not * necessarily unique. Our strategy is to target all matching * constraints within the first search-path schema that has any * matches, but disregard matches in schemas beyond the first match. * (This is a bit odd but it's the historical behavior.) */ conrel = heap_open(ConstraintRelationId, AccessShareLock); foreach(lc, stmt->constraints) { RangeVar *constraint = lfirst(lc); bool found; List *namespacelist; ListCell *nslc; if (constraint->catalogname) { if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cross-database references are not implemented: \"%s.%s.%s\"", constraint->catalogname, constraint->schemaname, constraint->relname))); } /* * If we're given the schema name with the constraint, look only * in that schema. If given a bare constraint name, use the * search path to find the first matching constraint. */ if (constraint->schemaname) { Oid namespaceId = LookupExplicitNamespace(constraint->schemaname, false); namespacelist = list_make1_oid(namespaceId); } else { namespacelist = fetch_search_path(true); } found = false; foreach(nslc, namespacelist) { Oid namespaceId = lfirst_oid(nslc); SysScanDesc conscan; ScanKeyData skey[2]; HeapTuple tup; ScanKeyInit(&skey[0], Anum_pg_constraint_conname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(constraint->relname)); ScanKeyInit(&skey[1], Anum_pg_constraint_connamespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(namespaceId)); conscan = systable_beginscan(conrel, ConstraintNameNspIndexId, true, SnapshotNow, 2, skey); while (HeapTupleIsValid(tup = systable_getnext(conscan))) { Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup); if (con->condeferrable) conoidlist = lappend_oid(conoidlist, HeapTupleGetOid(tup)); else if (stmt->deferred) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("constraint \"%s\" is not deferrable", constraint->relname))); found = true; } systable_endscan(conscan); /* * Once we've found a matching constraint we do not search * later parts of the search path. */ if (found) break; } list_free(namespacelist); /* * Not found ? */ if (!found) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" does not exist", constraint->relname))); } heap_close(conrel, AccessShareLock); /* * Now, locate the trigger(s) implementing each of these constraints, * and make a list of their OIDs. */ tgrel = heap_open(TriggerRelationId, AccessShareLock); foreach(lc, conoidlist) { Oid conoid = lfirst_oid(lc); bool found; ScanKeyData skey; SysScanDesc tgscan; HeapTuple htup; found = false; ScanKeyInit(&skey, Anum_pg_trigger_tgconstraint, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(conoid)); tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true, SnapshotNow, 1, &skey); while (HeapTupleIsValid(htup = systable_getnext(tgscan))) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); /* * Silently skip triggers that are marked as non-deferrable in * pg_trigger. This is not an error condition, since a * deferrable RI constraint may have some non-deferrable * actions. */ if (pg_trigger->tgdeferrable) tgoidlist = lappend_oid(tgoidlist, HeapTupleGetOid(htup)); found = true; } systable_endscan(tgscan); /* Safety check: a deferrable constraint should have triggers */ if (!found) elog(ERROR, "no triggers found for constraint with OID %u", conoid); } heap_close(tgrel, AccessShareLock); /* * Now we can set the trigger states of individual triggers for this * xact. */ foreach(lc, tgoidlist) { Oid tgoid = lfirst_oid(lc); SetConstraintState state = afterTriggers->state; bool found = false; int i; for (i = 0; i < state->numstates; i++) { if (state->trigstates[i].sct_tgoid == tgoid) { state->trigstates[i].sct_tgisdeferred = stmt->deferred; found = true; break; } } if (!found) { afterTriggers->state = SetConstraintStateAddItem(state, tgoid, stmt->deferred); } } } /* * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred * checks against that constraint must be made when the SET CONSTRAINTS * command is executed -- i.e. the effects of the SET CONSTRAINTS command * apply retroactively. We've updated the constraints state, so scan the * list of previously deferred events to fire any that have now become * immediate. * * Obviously, if this was SET ... DEFERRED then it can't have converted * any unfired events to immediate, so we need do nothing in that case. */ if (!stmt->deferred) { AfterTriggerEventList *events = &afterTriggers->events; bool snapshot_set = false; while (afterTriggerMarkEvents(events, NULL, true)) { CommandId firing_id = afterTriggers->firing_counter++; /* * Make sure a snapshot has been established in case trigger * functions need one. Note that we avoid setting a snapshot if * we don't find at least one trigger that has to be fired now. * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are * at the start of a transaction it's not possible for any trigger * events to be queued yet.) */ if (!snapshot_set) { PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } /* * We can delete fired events if we are at top transaction level, * but we'd better not if inside a subtransaction, since the * subtransaction could later get rolled back. */ if (afterTriggerInvokeEvents(events, firing_id, NULL, !IsSubTransaction())) break; /* all fired */ } if (snapshot_set) PopActiveSnapshot(); } }
TriggerDesc* CopyTriggerDesc | ( | TriggerDesc * | trigdesc | ) |
Definition at line 1634 of file trigger.c.
References i, NULL, TriggerDesc::numtriggers, palloc(), pstrdup(), Trigger::tgargs, Trigger::tgattr, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgqual, and TriggerDesc::triggers.
Referenced by InitResultRelInfo(), and RelationBuildTriggers().
{ TriggerDesc *newdesc; Trigger *trigger; int i; if (trigdesc == NULL || trigdesc->numtriggers <= 0) return NULL; newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc)); memcpy(newdesc, trigdesc, sizeof(TriggerDesc)); trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger)); memcpy(trigger, trigdesc->triggers, trigdesc->numtriggers * sizeof(Trigger)); newdesc->triggers = trigger; for (i = 0; i < trigdesc->numtriggers; i++) { trigger->tgname = pstrdup(trigger->tgname); if (trigger->tgnattr > 0) { int16 *newattr; newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16)); memcpy(newattr, trigger->tgattr, trigger->tgnattr * sizeof(int16)); trigger->tgattr = newattr; } if (trigger->tgnargs > 0) { char **newargs; int16 j; newargs = (char **) palloc(trigger->tgnargs * sizeof(char *)); for (j = 0; j < trigger->tgnargs; j++) newargs[j] = pstrdup(trigger->tgargs[j]); trigger->tgargs = newargs; } if (trigger->tgqual) trigger->tgqual = pstrdup(trigger->tgqual); trigger++; } return newdesc; }
Oid CreateTrigger | ( | CreateTrigStmt * | stmt, | |
const char * | queryString, | |||
Oid | constraintOid, | |||
Oid | indexOid, | |||
bool | isInternal | |||
) |
Definition at line 120 of file trigger.c.
References AccessExclusiveLock, AccessShareLock, ACL_EXECUTE, ACL_KIND_CLASS, ACL_KIND_PROC, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addRangeTableEntryForRelation(), addRTEtoQuery(), allowSystemTableMods, Anum_pg_trigger_tgargs, Anum_pg_trigger_tgattr, Anum_pg_trigger_tgconstraint, Anum_pg_trigger_tgconstrindid, Anum_pg_trigger_tgconstrrelid, Anum_pg_trigger_tgdeferrable, Anum_pg_trigger_tgenabled, Anum_pg_trigger_tgfoid, Anum_pg_trigger_tginitdeferred, Anum_pg_trigger_tgisinternal, Anum_pg_trigger_tgname, Anum_pg_trigger_tgnargs, Anum_pg_trigger_tgqual, Anum_pg_trigger_tgrelid, Anum_pg_trigger_tgtype, CreateTrigStmt::args, Assert, assign_expr_collations(), attnameAttNum(), BoolGetDatum, BTEqualStrategyNumber, buildint2vector(), byteain(), CatalogUpdateIndexes(), CharGetDatum, ObjectAddress::classId, CreateTrigStmt::columns, CONSTRAINT_TRIGGER, CreateTrigStmt::constrrel, ConvertTriggerToFK(), copyObject(), CreateConstraintEntry(), CStringGetDatum, CStringGetTextDatum, DatumGetPointer, CreateTrigStmt::deferrable, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, DirectFunctionCall1, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, CreateTrigStmt::events, EXPR_KIND_TRIGGER_WHEN, free_parsestate(), CreateTrigStmt::funcname, get_func_rettype(), get_rel_name(), GetNewOid(), GETSTRUCT, GetUserId(), heap_close, heap_form_tuple(), heap_freetuple(), heap_open(), heap_openrv(), HeapTupleIsValid, HeapTupleSetOid, i, CreateTrigStmt::initdeferred, Int16GetDatum, InvalidAttrNumber, InvalidOid, InvokeObjectPostCreateHookArg, CreateTrigStmt::isconstraint, IsSystemRelation(), lfirst, list_length(), Var::location, LookupFuncName(), make_parsestate(), makeAlias(), name, namein(), NameListToString(), namestrcmp(), NIL, nodeToString(), NoLock, NULL, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, OidIsValid, OPAQUEOID, ParseState::p_rtable, ParseState::p_sourcetext, palloc(), parser_errposition(), pfree(), pg_class_aclcheck(), pg_proc_aclcheck(), PointerGetDatum, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pull_var_clause(), PVC_REJECT_AGGREGATES, PVC_REJECT_PLACEHOLDERS, RangeVarGetRelid, RelationData::rd_att, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnExpr(), CreateTrigStmt::relation, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RelationRelationId, RELKIND_RELATION, RELKIND_VIEW, RangeVar::relname, RELOID, RI_FKey_trigger_type(), RI_TRIGGER_NONE, CreateTrigStmt::row, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, SetFunctionReturnType(), simple_heap_insert(), simple_heap_update(), SnapshotNow, snprintf(), strVal, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, CreateTrigStmt::timing, transformWhereClause(), TRIGGER_CLEAR_TYPE, TRIGGER_FIRES_ON_ORIGIN, TRIGGER_FOR_BEFORE, TRIGGER_FOR_DELETE, TRIGGER_FOR_INSERT, TRIGGER_FOR_INSTEAD, TRIGGER_FOR_ROW, TRIGGER_FOR_TRUNCATE, TRIGGER_SETT_ROW, TRIGGER_TYPE_AFTER, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSTEAD, TRIGGEROID, TriggerRelationId, TriggerRelidNameIndexId, CreateTrigStmt::trigname, values, Var::varattno, Var::varno, WARNING, and CreateTrigStmt::whenClause.
Referenced by CreateFKCheckTrigger(), createForeignKeyTriggers(), index_constraint_create(), and ProcessUtilitySlow().
{ int16 tgtype; int ncolumns; int16 *columns; int2vector *tgattr; Node *whenClause; List *whenRtable; char *qual; Datum values[Natts_pg_trigger]; bool nulls[Natts_pg_trigger]; Relation rel; AclResult aclresult; Relation tgrel; SysScanDesc tgscan; ScanKeyData key; Relation pgrel; HeapTuple tuple; Oid fargtypes[1]; /* dummy */ Oid funcoid; Oid funcrettype; Oid trigoid; char internaltrigname[NAMEDATALEN]; char *trigname; Oid constrrelid = InvalidOid; ObjectAddress myself, referenced; rel = heap_openrv(stmt->relation, AccessExclusiveLock); /* * Triggers must be on tables or views, and there are additional * relation-type-specific restrictions. */ if (rel->rd_rel->relkind == RELKIND_RELATION) { /* Tables can't have INSTEAD OF triggers */ if (stmt->timing != TRIGGER_TYPE_BEFORE && stmt->timing != TRIGGER_TYPE_AFTER) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a table", RelationGetRelationName(rel)), errdetail("Tables cannot have INSTEAD OF triggers."))); } else if (rel->rd_rel->relkind == RELKIND_VIEW) { /* * Views can have INSTEAD OF triggers (which we check below are * row-level), or statement-level BEFORE/AFTER triggers. */ if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a view", RelationGetRelationName(rel)), errdetail("Views cannot have row-level BEFORE or AFTER triggers."))); /* Disallow TRUNCATE triggers on VIEWs */ if (TRIGGER_FOR_TRUNCATE(stmt->events)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a view", RelationGetRelationName(rel)), errdetail("Views cannot have TRUNCATE triggers."))); } else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table or view", RelationGetRelationName(rel)))); if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); if (stmt->isconstraint && stmt->constrrel != NULL) { /* * We must take a lock on the target relation to protect against * concurrent drop. It's not clear that AccessShareLock is strong * enough, but we certainly need at least that much... otherwise, we * might end up creating a pg_constraint entry referencing a * nonexistent table. */ constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock, false); } /* permission checks */ if (!isInternal) { aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_TRIGGER); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); if (OidIsValid(constrrelid)) { aclresult = pg_class_aclcheck(constrrelid, GetUserId(), ACL_TRIGGER); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, get_rel_name(constrrelid)); } } /* Compute tgtype */ TRIGGER_CLEAR_TYPE(tgtype); if (stmt->row) TRIGGER_SETT_ROW(tgtype); tgtype |= stmt->timing; tgtype |= stmt->events; /* Disallow ROW-level TRUNCATE triggers */ if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("TRUNCATE FOR EACH ROW triggers are not supported"))); /* INSTEAD triggers must be row-level, and can't have WHEN or columns */ if (TRIGGER_FOR_INSTEAD(tgtype)) { if (!TRIGGER_FOR_ROW(tgtype)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("INSTEAD OF triggers must be FOR EACH ROW"))); if (stmt->whenClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("INSTEAD OF triggers cannot have WHEN conditions"))); if (stmt->columns != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("INSTEAD OF triggers cannot have column lists"))); } /* * Parse the WHEN clause, if any */ if (stmt->whenClause) { ParseState *pstate; RangeTblEntry *rte; List *varList; ListCell *lc; /* Set up a pstate to parse with */ pstate = make_parsestate(NULL); pstate->p_sourcetext = queryString; /* * Set up RTEs for OLD and NEW references. * * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2. */ rte = addRangeTableEntryForRelation(pstate, rel, makeAlias("old", NIL), false, false); addRTEtoQuery(pstate, rte, false, true, true); rte = addRangeTableEntryForRelation(pstate, rel, makeAlias("new", NIL), false, false); addRTEtoQuery(pstate, rte, false, true, true); /* Transform expression. Copy to be sure we don't modify original */ whenClause = transformWhereClause(pstate, copyObject(stmt->whenClause), EXPR_KIND_TRIGGER_WHEN, "WHEN"); /* we have to fix its collations too */ assign_expr_collations(pstate, whenClause); /* * Check for disallowed references to OLD/NEW. * * NB: pull_var_clause is okay here only because we don't allow * subselects in WHEN clauses; it would fail to examine the contents * of subselects. */ varList = pull_var_clause(whenClause, PVC_REJECT_AGGREGATES, PVC_REJECT_PLACEHOLDERS); foreach(lc, varList) { Var *var = (Var *) lfirst(lc); switch (var->varno) { case PRS2_OLD_VARNO: if (!TRIGGER_FOR_ROW(tgtype)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("statement trigger's WHEN condition cannot reference column values"), parser_errposition(pstate, var->location))); if (TRIGGER_FOR_INSERT(tgtype)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("INSERT trigger's WHEN condition cannot reference OLD values"), parser_errposition(pstate, var->location))); /* system columns are okay here */ break; case PRS2_NEW_VARNO: if (!TRIGGER_FOR_ROW(tgtype)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("statement trigger's WHEN condition cannot reference column values"), parser_errposition(pstate, var->location))); if (TRIGGER_FOR_DELETE(tgtype)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("DELETE trigger's WHEN condition cannot reference NEW values"), parser_errposition(pstate, var->location))); if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"), parser_errposition(pstate, var->location))); break; default: /* can't happen without add_missing_from, so just elog */ elog(ERROR, "trigger WHEN condition cannot contain references to other relations"); break; } } /* we'll need the rtable for recordDependencyOnExpr */ whenRtable = pstate->p_rtable; qual = nodeToString(whenClause); free_parsestate(pstate); } else { whenClause = NULL; whenRtable = NIL; qual = NULL; } /* * Find and validate the trigger function. */ funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false); if (!isInternal) { aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(stmt->funcname)); } funcrettype = get_func_rettype(funcoid); if (funcrettype != TRIGGEROID) { /* * We allow OPAQUE just so we can load old dump files. When we see a * trigger function declared OPAQUE, change it to TRIGGER. */ if (funcrettype == OPAQUEOID) { ereport(WARNING, (errmsg("changing return type of function %s from \"opaque\" to \"trigger\"", NameListToString(stmt->funcname)))); SetFunctionReturnType(funcoid, TRIGGEROID); } else ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("function %s must return type \"trigger\"", NameListToString(stmt->funcname)))); } /* * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that * references one of the built-in RI_FKey trigger functions, assume it is * from a dump of a pre-7.3 foreign key constraint, and take steps to * convert this legacy representation into a regular foreign key * constraint. Ugly, but necessary for loading old dump files. */ if (stmt->isconstraint && !isInternal && list_length(stmt->args) >= 6 && (list_length(stmt->args) % 2) == 0 && RI_FKey_trigger_type(funcoid) != RI_TRIGGER_NONE) { /* Keep lock on target rel until end of xact */ heap_close(rel, NoLock); ConvertTriggerToFK(stmt, funcoid); return InvalidOid; } /* * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a * corresponding pg_constraint entry. */ if (stmt->isconstraint && !OidIsValid(constraintOid)) { /* Internal callers should have made their own constraints */ Assert(!isInternal); constraintOid = CreateConstraintEntry(stmt->trigname, RelationGetNamespace(rel), CONSTRAINT_TRIGGER, stmt->deferrable, stmt->initdeferred, true, RelationGetRelid(rel), NULL, /* no conkey */ 0, InvalidOid, /* no domain */ InvalidOid, /* no index */ InvalidOid, /* no foreign key */ NULL, NULL, NULL, NULL, 0, ' ', ' ', ' ', NULL, /* no exclusion */ NULL, /* no check constraint */ NULL, NULL, true, /* islocal */ 0, /* inhcount */ true, /* isnoinherit */ isInternal); /* is_internal */ } /* * Generate the trigger's OID now, so that we can use it in the name if * needed. */ tgrel = heap_open(TriggerRelationId, RowExclusiveLock); trigoid = GetNewOid(tgrel); /* * If trigger is internally generated, modify the provided trigger name to * ensure uniqueness by appending the trigger OID. (Callers will usually * supply a simple constant trigger name in these cases.) */ if (isInternal) { snprintf(internaltrigname, sizeof(internaltrigname), "%s_%u", stmt->trigname, trigoid); trigname = internaltrigname; } else { /* user-defined trigger; use the specified trigger name as-is */ trigname = stmt->trigname; } /* * Scan pg_trigger for existing triggers on relation. We do this only to * give a nice error message if there's already a trigger of the same * name. (The unique index on tgrelid/tgname would complain anyway.) We * can skip this for internally generated triggers, since the name * modification above should be sufficient. * * NOTE that this is cool only because we have AccessExclusiveLock on the * relation, so the trigger set won't be changing underneath us. */ if (!isInternal) { ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 1, &key); while (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); if (namestrcmp(&(pg_trigger->tgname), trigname) == 0) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("trigger \"%s\" for relation \"%s\" already exists", trigname, stmt->relation->relname))); } systable_endscan(tgscan); } /* * Build the new pg_trigger tuple. */ memset(nulls, false, sizeof(nulls)); values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel)); values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein, CStringGetDatum(trigname)); values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid); values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype); values[Anum_pg_trigger_tgenabled - 1] = CharGetDatum(TRIGGER_FIRES_ON_ORIGIN); values[Anum_pg_trigger_tgisinternal - 1] = BoolGetDatum(isInternal); values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid); values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid); values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid); values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable); values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred); if (stmt->args) { ListCell *le; char *args; int16 nargs = list_length(stmt->args); int len = 0; foreach(le, stmt->args) { char *ar = strVal(lfirst(le)); len += strlen(ar) + 4; for (; *ar; ar++) { if (*ar == '\\') len++; } } args = (char *) palloc(len + 1); args[0] = '\0'; foreach(le, stmt->args) { char *s = strVal(lfirst(le)); char *d = args + strlen(args); while (*s) { if (*s == '\\') *d++ = '\\'; *d++ = *s++; } strcpy(d, "\\000"); } values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs); values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain, CStringGetDatum(args)); } else { values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0); values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain, CStringGetDatum("")); } /* build column number array if it's a column-specific trigger */ ncolumns = list_length(stmt->columns); if (ncolumns == 0) columns = NULL; else { ListCell *cell; int i = 0; columns = (int16 *) palloc(ncolumns * sizeof(int16)); foreach(cell, stmt->columns) { char *name = strVal(lfirst(cell)); int16 attnum; int j; /* Lookup column name. System columns are not allowed */ attnum = attnameAttNum(rel, name, false); if (attnum == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", name, RelationGetRelationName(rel)))); /* Check for duplicates */ for (j = i - 1; j >= 0; j--) { if (columns[j] == attnum) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column \"%s\" specified more than once", name))); } columns[i++] = attnum; } } tgattr = buildint2vector(columns, ncolumns); values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr); /* set tgqual if trigger has WHEN clause */ if (qual) values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(qual); else nulls[Anum_pg_trigger_tgqual - 1] = true; tuple = heap_form_tuple(tgrel->rd_att, values, nulls); /* force tuple to have the desired OID */ HeapTupleSetOid(tuple, trigoid); /* * Insert tuple into pg_trigger. */ simple_heap_insert(tgrel, tuple); CatalogUpdateIndexes(tgrel, tuple); heap_freetuple(tuple); heap_close(tgrel, RowExclusiveLock); pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1])); pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1])); pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1])); /* * Update relation's pg_class entry. Crucial side-effect: other backends * (and this one too!) are sent SI message to make them rebuild relcache * entries. */ pgrel = heap_open(RelationRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(RelationGetRelid(rel))); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", RelationGetRelid(rel)); ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true; simple_heap_update(pgrel, &tuple->t_self, tuple); CatalogUpdateIndexes(pgrel, tuple); heap_freetuple(tuple); heap_close(pgrel, RowExclusiveLock); /* * We used to try to update the rel's relcache entry here, but that's * fairly pointless since it will happen as a byproduct of the upcoming * CommandCounterIncrement... */ /* * Record dependencies for trigger. Always place a normal dependency on * the function. */ myself.classId = TriggerRelationId; myself.objectId = trigoid; myself.objectSubId = 0; referenced.classId = ProcedureRelationId; referenced.objectId = funcoid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); if (isInternal && OidIsValid(constraintOid)) { /* * Internally-generated trigger for a constraint, so make it an * internal dependency of the constraint. We can skip depending on * the relation(s), as there'll be an indirect dependency via the * constraint. */ referenced.classId = ConstraintRelationId; referenced.objectId = constraintOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); } else { /* * User CREATE TRIGGER, so place dependencies. We make trigger be * auto-dropped if its relation is dropped or if the FK relation is * dropped. (Auto drop is compatible with our pre-7.3 behavior.) */ referenced.classId = RelationRelationId; referenced.objectId = RelationGetRelid(rel); referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); if (OidIsValid(constrrelid)) { referenced.classId = RelationRelationId; referenced.objectId = constrrelid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); } /* Not possible to have an index dependency in this case */ Assert(!OidIsValid(indexOid)); /* * If it's a user-specified constraint trigger, make the constraint * internally dependent on the trigger instead of vice versa. */ if (OidIsValid(constraintOid)) { referenced.classId = ConstraintRelationId; referenced.objectId = constraintOid; referenced.objectSubId = 0; recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } } /* If column-specific trigger, add normal dependencies on columns */ if (columns != NULL) { int i; referenced.classId = RelationRelationId; referenced.objectId = RelationGetRelid(rel); for (i = 0; i < ncolumns; i++) { referenced.objectSubId = columns[i]; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } /* * If it has a WHEN clause, add dependencies on objects mentioned in the * expression (eg, functions, as well as any columns used). */ if (whenClause != NULL) recordDependencyOnExpr(&myself, whenClause, whenRtable, DEPENDENCY_NORMAL); /* Post creation hook for new trigger */ InvokeObjectPostCreateHookArg(TriggerRelationId, trigoid, 0, isInternal); /* Keep lock on target rel until end of xact */ heap_close(rel, NoLock); return trigoid; }
Definition at line 1331 of file trigger.c.
References Anum_pg_trigger_tgname, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogUpdateIndexes(), CStringGetDatum, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_close, heap_copytuple(), heap_freetuple(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, InvokeObjectPostAlterHook, NameStr, ObjectIdGetDatum, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), simple_heap_update(), SnapshotNow, superuser(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, TriggerRelationId, and TriggerRelidNameIndexId.
Referenced by ATExecEnableDisableTrigger().
{ Relation tgrel; int nkeys; ScanKeyData keys[2]; SysScanDesc tgscan; HeapTuple tuple; bool found; bool changed; /* Scan the relevant entries in pg_triggers */ tgrel = heap_open(TriggerRelationId, RowExclusiveLock); ScanKeyInit(&keys[0], Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); if (tgname) { ScanKeyInit(&keys[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(tgname)); nkeys = 2; } else nkeys = 1; tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, nkeys, keys); found = changed = false; while (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple); if (oldtrig->tgisinternal) { /* system trigger ... ok to process? */ if (skip_system) continue; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system trigger", NameStr(oldtrig->tgname)))); } found = true; if (oldtrig->tgenabled != fires_when) { /* need to change this one ... make a copy to scribble on */ HeapTuple newtup = heap_copytuple(tuple); Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup); newtrig->tgenabled = fires_when; simple_heap_update(tgrel, &newtup->t_self, newtup); /* Keep catalog indexes current */ CatalogUpdateIndexes(tgrel, newtup); heap_freetuple(newtup); changed = true; } InvokeObjectPostAlterHook(TriggerRelationId, HeapTupleGetOid(tuple), 0); } systable_endscan(tgscan); heap_close(tgrel, RowExclusiveLock); if (tgname && !found) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("trigger \"%s\" for table \"%s\" does not exist", tgname, RelationGetRelationName(rel)))); /* * If we changed anything, broadcast a SI inval message to force each * backend (including our own!) to rebuild relation's relcache entry. * Otherwise they will fail to apply the change promptly. */ if (changed) CacheInvalidateRelcache(rel); }
void ExecARDeleteTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo, | |||
ItemPointer | tupleid | |||
) |
Definition at line 2205 of file trigger.c.
References AfterTriggerSaveEvent(), GetTupleForTrigger(), heap_freetuple(), LockTupleExclusive, NIL, NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_delete_after_row, and TRIGGER_EVENT_DELETE.
Referenced by ExecDelete().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_delete_after_row) { HeapTuple trigtuple = GetTupleForTrigger(estate, NULL, relinfo, tupleid, LockTupleExclusive, NULL); AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE, true, trigtuple, NULL, NIL, NULL); heap_freetuple(trigtuple); } }
void ExecARInsertTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo, | |||
HeapTuple | trigtuple, | |||
List * | recheckIndexes | |||
) |
Definition at line 2011 of file trigger.c.
References AfterTriggerSaveEvent(), NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_insert_after_row, and TRIGGER_EVENT_INSERT.
Referenced by CopyFrom(), CopyFromInsertBatch(), and ExecInsert().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_insert_after_row) AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT, true, NULL, trigtuple, recheckIndexes, NULL); }
void ExecARUpdateTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo, | |||
ItemPointer | tupleid, | |||
HeapTuple | newtuple, | |||
List * | recheckIndexes | |||
) |
Definition at line 2443 of file trigger.c.
References AfterTriggerSaveEvent(), GetModifiedColumns, GetTupleForTrigger(), heap_freetuple(), LockTupleExclusive, NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_update_after_row, and TRIGGER_EVENT_UPDATE.
Referenced by ExecUpdate().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_update_after_row) { HeapTuple trigtuple = GetTupleForTrigger(estate, NULL, relinfo, tupleid, LockTupleExclusive, NULL); AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE, true, trigtuple, newtuple, recheckIndexes, GetModifiedColumns(relinfo, estate)); heap_freetuple(trigtuple); } }
void ExecASDeleteTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo | |||
) |
Definition at line 2136 of file trigger.c.
References AfterTriggerSaveEvent(), NIL, NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_delete_after_statement, and TRIGGER_EVENT_DELETE.
Referenced by fireASTriggers().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_delete_after_statement) AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE, false, NULL, NULL, NIL, NULL); }
void ExecASInsertTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo | |||
) |
Definition at line 1937 of file trigger.c.
References AfterTriggerSaveEvent(), NIL, NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_insert_after_statement, and TRIGGER_EVENT_INSERT.
Referenced by CopyFrom(), and fireASTriggers().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_insert_after_statement) AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT, false, NULL, NULL, NIL, NULL); }
void ExecASTruncateTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo | |||
) |
Definition at line 2577 of file trigger.c.
References AfterTriggerSaveEvent(), NIL, NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_truncate_after_statement, and TRIGGER_EVENT_TRUNCATE.
Referenced by ExecuteTruncate().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_truncate_after_statement) AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_TRUNCATE, false, NULL, NULL, NIL, NULL); }
void ExecASUpdateTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo | |||
) |
Definition at line 2321 of file trigger.c.
References AfterTriggerSaveEvent(), GetModifiedColumns, NIL, NULL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_update_after_statement, and TRIGGER_EVENT_UPDATE.
Referenced by fireASTriggers().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_update_after_statement) AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE, false, NULL, NULL, NIL, GetModifiedColumns(relinfo, estate)); }
bool ExecBRDeleteTriggers | ( | EState * | estate, | |
EPQState * | epqstate, | |||
ResultRelInfo * | relinfo, | |||
ItemPointer | tupleid | |||
) |
Definition at line 2146 of file trigger.c.
References ExecCallTriggerFunc(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), LockTupleExclusive, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_ROW, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.
Referenced by ExecDelete().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; bool result = true; TriggerData LocTriggerData; HeapTuple trigtuple; HeapTuple newtuple; TupleTableSlot *newSlot; int i; trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid, LockTupleExclusive, &newSlot); if (trigtuple == NULL) return false; LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_ROW, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, NULL, trigtuple, NULL)) continue; LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (newtuple == NULL) { result = false; /* tell caller to suppress delete */ break; } if (newtuple != trigtuple) heap_freetuple(newtuple); } heap_freetuple(trigtuple); return result; }
TupleTableSlot* ExecBRInsertTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo, | |||
TupleTableSlot * | slot | |||
) |
Definition at line 1947 of file trigger.c.
References EState::es_trig_tuple_slot, ExecCallTriggerFunc(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ExecStoreTuple(), GetPerTupleMemoryContext, heap_freetuple(), InvalidBuffer, NULL, TriggerDesc::numtriggers, RelationGetDescr, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tupleDescriptor, and TriggerData::type.
Referenced by CopyFrom(), and ExecInsert().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; HeapTuple slottuple = ExecMaterializeSlot(slot); HeapTuple newtuple = slottuple; HeapTuple oldtuple; TriggerData LocTriggerData; int i; LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_ROW, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, NULL, NULL, newtuple)) continue; LocTriggerData.tg_trigtuple = oldtuple = newtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (oldtuple != newtuple && oldtuple != slottuple) heap_freetuple(oldtuple); if (newtuple == NULL) return NULL; /* "do nothing" */ } if (newtuple != slottuple) { /* * Return the modified tuple using the es_trig_tuple_slot. We assume * the tuple was allocated in per-tuple memory context, and therefore * will go away by itself. The tuple table slot should not try to * clear it. */ TupleTableSlot *newslot = estate->es_trig_tuple_slot; TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); if (newslot->tts_tupleDescriptor != tupdesc) ExecSetSlotDescriptor(newslot, tupdesc); ExecStoreTuple(newtuple, newslot, InvalidBuffer, false); slot = newslot; } return slot; }
TupleTableSlot* ExecBRUpdateTriggers | ( | EState * | estate, | |
EPQState * | epqstate, | |||
ResultRelInfo * | relinfo, | |||
ItemPointer | tupleid, | |||
TupleTableSlot * | slot | |||
) |
Definition at line 2332 of file trigger.c.
References bms_overlap(), EState::es_trig_tuple_slot, ExecCallTriggerFunc(), ExecFilterJunk(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ExecStoreTuple(), GetModifiedColumns, GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), InvalidBuffer, NULL, TriggerDesc::numtriggers, RelationGetDescr, RelationGetIndexAttrBitmap(), ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TRIGGER_TYPE_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tupleDescriptor, and TriggerData::type.
Referenced by ExecUpdate().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; HeapTuple slottuple = ExecMaterializeSlot(slot); HeapTuple newtuple = slottuple; TriggerData LocTriggerData; HeapTuple trigtuple; HeapTuple oldtuple; TupleTableSlot *newSlot; int i; Bitmapset *modifiedCols; Bitmapset *keyCols; LockTupleMode lockmode; /* * Compute lock mode to use. If columns that are part of the key have not * been modified, then we can use a weaker lock, allowing for better * concurrency. */ modifiedCols = GetModifiedColumns(relinfo, estate); keyCols = RelationGetIndexAttrBitmap(relinfo->ri_RelationDesc, true); if (bms_overlap(keyCols, modifiedCols)) lockmode = LockTupleExclusive; else lockmode = LockTupleNoKeyExclusive; /* get a copy of the on-disk tuple we are planning to update */ trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid, lockmode, &newSlot); if (trigtuple == NULL) return NULL; /* cancel the update action */ /* * In READ COMMITTED isolation level it's possible that target tuple was * changed due to concurrent update. In that case we have a raw subplan * output tuple in newSlot, and need to run it through the junk filter to * produce an insertable tuple. * * Caution: more than likely, the passed-in slot is the same as the * junkfilter's output slot, so we are clobbering the original value of * slottuple by doing the filtering. This is OK since neither we nor our * caller have any more interest in the prior contents of that slot. */ if (newSlot != NULL) { slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot); slottuple = ExecMaterializeSlot(slot); newtuple = slottuple; } LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_ROW, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_UPDATE)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, modifiedCols, trigtuple, newtuple)) continue; LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_newtuple = oldtuple = newtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_newtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (oldtuple != newtuple && oldtuple != slottuple) heap_freetuple(oldtuple); if (newtuple == NULL) { heap_freetuple(trigtuple); return NULL; /* "do nothing" */ } } heap_freetuple(trigtuple); if (newtuple != slottuple) { /* * Return the modified tuple using the es_trig_tuple_slot. We assume * the tuple was allocated in per-tuple memory context, and therefore * will go away by itself. The tuple table slot should not try to * clear it. */ TupleTableSlot *newslot = estate->es_trig_tuple_slot; TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); if (newslot->tts_tupleDescriptor != tupdesc) ExecSetSlotDescriptor(newslot, tupdesc); ExecStoreTuple(newtuple, newslot, InvalidBuffer, false); slot = newslot; } return slot; }
void ExecBSDeleteTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo | |||
) |
Definition at line 2086 of file trigger.c.
References ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), GetPerTupleMemoryContext, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TriggerDesc::trig_delete_before_statement, TRIGGER_EVENT_DELETE, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_STATEMENT, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.
Referenced by fireBSTriggers().
{ TriggerDesc *trigdesc; int i; TriggerData LocTriggerData; trigdesc = relinfo->ri_TrigDesc; if (trigdesc == NULL) return; if (!trigdesc->trig_delete_before_statement) return; LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_trigtuple = NULL; LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; HeapTuple newtuple; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, NULL, NULL, NULL)) continue; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (newtuple) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("BEFORE STATEMENT trigger cannot return a value"))); } }
void ExecBSInsertTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo | |||
) |
Definition at line 1887 of file trigger.c.
References ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), GetPerTupleMemoryContext, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TriggerDesc::trig_insert_before_statement, TRIGGER_EVENT_INSERT, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_STATEMENT, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.
Referenced by CopyFrom(), and fireBSTriggers().
{ TriggerDesc *trigdesc; int i; TriggerData LocTriggerData; trigdesc = relinfo->ri_TrigDesc; if (trigdesc == NULL) return; if (!trigdesc->trig_insert_before_statement) return; LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_trigtuple = NULL; LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; HeapTuple newtuple; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, NULL, NULL, NULL)) continue; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (newtuple) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("BEFORE STATEMENT trigger cannot return a value"))); } }
void ExecBSTruncateTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo | |||
) |
Definition at line 2527 of file trigger.c.
References ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), GetPerTupleMemoryContext, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TriggerDesc::trig_truncate_before_statement, TRIGGER_EVENT_TRUNCATE, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_TRUNCATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.
Referenced by ExecuteTruncate().
{ TriggerDesc *trigdesc; int i; TriggerData LocTriggerData; trigdesc = relinfo->ri_TrigDesc; if (trigdesc == NULL) return; if (!trigdesc->trig_truncate_before_statement) return; LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_TRUNCATE | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_trigtuple = NULL; LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; HeapTuple newtuple; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_TRUNCATE)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, NULL, NULL, NULL)) continue; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (newtuple) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("BEFORE STATEMENT trigger cannot return a value"))); } }
void ExecBSUpdateTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo | |||
) |
Definition at line 2268 of file trigger.c.
References ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), GetModifiedColumns, GetPerTupleMemoryContext, NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TriggerDesc::trig_update_before_statement, TRIGGER_EVENT_UPDATE, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.
Referenced by fireBSTriggers().
{ TriggerDesc *trigdesc; int i; TriggerData LocTriggerData; Bitmapset *modifiedCols; trigdesc = relinfo->ri_TrigDesc; if (trigdesc == NULL) return; if (!trigdesc->trig_update_before_statement) return; modifiedCols = GetModifiedColumns(relinfo, estate); LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_trigtuple = NULL; LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; HeapTuple newtuple; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_UPDATE)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, modifiedCols, NULL, NULL)) continue; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (newtuple) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), errmsg("BEFORE STATEMENT trigger cannot return a value"))); } }
bool ExecIRDeleteTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo, | |||
HeapTuple | trigtuple | |||
) |
Definition at line 2223 of file trigger.c.
References ExecCallTriggerFunc(), GetPerTupleMemoryContext, heap_freetuple(), NULL, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_ROW, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.
Referenced by ExecDelete().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; TriggerData LocTriggerData; HeapTuple rettuple; int i; LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_INSTEAD; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_ROW, TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_DELETE)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, NULL, trigtuple, NULL)) continue; LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; rettuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (rettuple == NULL) return false; /* Delete was suppressed */ if (rettuple != trigtuple) heap_freetuple(rettuple); } return true; }
TupleTableSlot* ExecIRInsertTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo, | |||
TupleTableSlot * | slot | |||
) |
Definition at line 2022 of file trigger.c.
References EState::es_trig_tuple_slot, ExecCallTriggerFunc(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ExecStoreTuple(), GetPerTupleMemoryContext, heap_freetuple(), InvalidBuffer, NULL, TriggerDesc::numtriggers, RelationGetDescr, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tupleDescriptor, and TriggerData::type.
Referenced by ExecInsert().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; HeapTuple slottuple = ExecMaterializeSlot(slot); HeapTuple newtuple = slottuple; HeapTuple oldtuple; TriggerData LocTriggerData; int i; LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_INSTEAD; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_ROW, TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_INSERT)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, NULL, NULL, newtuple)) continue; LocTriggerData.tg_trigtuple = oldtuple = newtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (oldtuple != newtuple && oldtuple != slottuple) heap_freetuple(oldtuple); if (newtuple == NULL) return NULL; /* "do nothing" */ } if (newtuple != slottuple) { /* * Return the modified tuple using the es_trig_tuple_slot. We assume * the tuple was allocated in per-tuple memory context, and therefore * will go away by itself. The tuple table slot should not try to * clear it. */ TupleTableSlot *newslot = estate->es_trig_tuple_slot; TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); if (newslot->tts_tupleDescriptor != tupdesc) ExecSetSlotDescriptor(newslot, tupdesc); ExecStoreTuple(newtuple, newslot, InvalidBuffer, false); slot = newslot; } return slot; }
TupleTableSlot* ExecIRUpdateTriggers | ( | EState * | estate, | |
ResultRelInfo * | relinfo, | |||
HeapTuple | trigtuple, | |||
TupleTableSlot * | slot | |||
) |
Definition at line 2463 of file trigger.c.
References EState::es_trig_tuple_slot, ExecCallTriggerFunc(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ExecStoreTuple(), GetPerTupleMemoryContext, heap_freetuple(), InvalidBuffer, NULL, TriggerDesc::numtriggers, RelationGetDescr, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgtype, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TRIGGER_TYPE_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tupleDescriptor, and TriggerData::type.
Referenced by ExecUpdate().
{ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; HeapTuple slottuple = ExecMaterializeSlot(slot); HeapTuple newtuple = slottuple; TriggerData LocTriggerData; HeapTuple oldtuple; int i; LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_INSTEAD; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; for (i = 0; i < trigdesc->numtriggers; i++) { Trigger *trigger = &trigdesc->triggers[i]; if (!TRIGGER_TYPE_MATCHES(trigger->tgtype, TRIGGER_TYPE_ROW, TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_UPDATE)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, NULL, trigtuple, newtuple)) continue; LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_newtuple = oldtuple = newtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_newtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, i, relinfo->ri_TrigFunctions, relinfo->ri_TrigInstrument, GetPerTupleMemoryContext(estate)); if (oldtuple != newtuple && oldtuple != slottuple) heap_freetuple(oldtuple); if (newtuple == NULL) return NULL; /* "do nothing" */ } if (newtuple != slottuple) { /* * Return the modified tuple using the es_trig_tuple_slot. We assume * the tuple was allocated in per-tuple memory context, and therefore * will go away by itself. The tuple table slot should not try to * clear it. */ TupleTableSlot *newslot = estate->es_trig_tuple_slot; TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); if (newslot->tts_tupleDescriptor != tupdesc) ExecSetSlotDescriptor(newslot, tupdesc); ExecStoreTuple(newtuple, newslot, InvalidBuffer, false); slot = newslot; } return slot; }
void FreeTriggerDesc | ( | TriggerDesc * | trigdesc | ) |
Definition at line 1685 of file trigger.c.
References i, NULL, TriggerDesc::numtriggers, pfree(), Trigger::tgargs, Trigger::tgattr, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgqual, and TriggerDesc::triggers.
Referenced by RelationBuildTriggers(), and RelationDestroyRelation().
{ Trigger *trigger; int i; if (trigdesc == NULL) return; trigger = trigdesc->triggers; for (i = 0; i < trigdesc->numtriggers; i++) { pfree(trigger->tgname); if (trigger->tgnattr > 0) pfree(trigger->tgattr); if (trigger->tgnargs > 0) { while (--(trigger->tgnargs) >= 0) pfree(trigger->tgargs[trigger->tgnargs]); pfree(trigger->tgargs); } if (trigger->tgqual) pfree(trigger->tgqual); trigger++; } pfree(trigdesc->triggers); pfree(trigdesc); }
Definition at line 1107 of file trigger.c.
References AccessShareLock, Anum_pg_trigger_tgname, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, CStringGetDatum, ereport, errcode(), errmsg(), ERROR, get_rel_name(), heap_close, heap_open(), HeapTupleGetOid, HeapTupleIsValid, ObjectIdGetDatum, ScanKeyInit(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), TriggerRelationId, and TriggerRelidNameIndexId.
Referenced by get_object_address_relobject().
{ Relation tgrel; ScanKeyData skey[2]; SysScanDesc tgscan; HeapTuple tup; Oid oid; /* * Find the trigger, verify permissions, set up object address */ tgrel = heap_open(TriggerRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ScanKeyInit(&skey[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(trigname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 2, skey); tup = systable_getnext(tgscan); if (!HeapTupleIsValid(tup)) { if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("trigger \"%s\" for table \"%s\" does not exist", trigname, get_rel_name(relid)))); oid = InvalidOid; } else { oid = HeapTupleGetOid(tup); } systable_endscan(tgscan); heap_close(tgrel, AccessShareLock); return oid; }
Datum pg_trigger_depth | ( | PG_FUNCTION_ARGS | ) |
Definition at line 4690 of file trigger.c.
References MyTriggerDepth, and PG_RETURN_INT32.
void RelationBuildTriggers | ( | Relation | relation | ) |
Definition at line 1436 of file trigger.c.
References AccessShareLock, Anum_pg_trigger_tgargs, Anum_pg_trigger_tgqual, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, CacheMemoryContext, CopyTriggerDesc(), DatumGetByteaP, DatumGetCString, DirectFunctionCall1, elog, ERROR, fastgetattr, FreeTriggerDesc(), GETSTRUCT, heap_close, heap_open(), HeapTupleGetOid, HeapTupleIsValid, i, MemoryContextSwitchTo(), NameGetDatum, nameout(), TriggerDesc::numtriggers, ObjectIdGetDatum, palloc(), palloc0(), pfree(), pstrdup(), RelationData::rd_att, RelationGetRelationName, RelationGetRelid, repalloc(), ScanKeyInit(), SetTriggerFlags(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), TextDatumGetCString, Trigger::tgargs, Trigger::tgattr, Trigger::tgconstraint, Trigger::tgconstrindid, Trigger::tgconstrrelid, Trigger::tgdeferrable, Trigger::tgenabled, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgisinternal, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgoid, Trigger::tgqual, Trigger::tgtype, RelationData::trigdesc, TriggerRelationId, TriggerRelidNameIndexId, TriggerDesc::triggers, val, and VARDATA.
Referenced by RelationBuildDesc(), and RelationCacheInitializePhase3().
{ TriggerDesc *trigdesc; int numtrigs; int maxtrigs; Trigger *triggers; Relation tgrel; ScanKeyData skey; SysScanDesc tgscan; HeapTuple htup; MemoryContext oldContext; int i; /* * Allocate a working array to hold the triggers (the array is extended if * necessary) */ maxtrigs = 16; triggers = (Trigger *) palloc(maxtrigs * sizeof(Trigger)); numtrigs = 0; /* * Note: since we scan the triggers using TriggerRelidNameIndexId, we will * be reading the triggers in name order, except possibly during * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn * ensures that triggers will be fired in name order. */ ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); tgrel = heap_open(TriggerRelationId, AccessShareLock); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 1, &skey); while (HeapTupleIsValid(htup = systable_getnext(tgscan))) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); Trigger *build; Datum datum; bool isnull; if (numtrigs >= maxtrigs) { maxtrigs *= 2; triggers = (Trigger *) repalloc(triggers, maxtrigs * sizeof(Trigger)); } build = &(triggers[numtrigs]); build->tgoid = HeapTupleGetOid(htup); build->tgname = DatumGetCString(DirectFunctionCall1(nameout, NameGetDatum(&pg_trigger->tgname))); build->tgfoid = pg_trigger->tgfoid; build->tgtype = pg_trigger->tgtype; build->tgenabled = pg_trigger->tgenabled; build->tgisinternal = pg_trigger->tgisinternal; build->tgconstrrelid = pg_trigger->tgconstrrelid; build->tgconstrindid = pg_trigger->tgconstrindid; build->tgconstraint = pg_trigger->tgconstraint; build->tgdeferrable = pg_trigger->tgdeferrable; build->tginitdeferred = pg_trigger->tginitdeferred; build->tgnargs = pg_trigger->tgnargs; /* tgattr is first var-width field, so OK to access directly */ build->tgnattr = pg_trigger->tgattr.dim1; if (build->tgnattr > 0) { build->tgattr = (int16 *) palloc(build->tgnattr * sizeof(int16)); memcpy(build->tgattr, &(pg_trigger->tgattr.values), build->tgnattr * sizeof(int16)); } else build->tgattr = NULL; if (build->tgnargs > 0) { bytea *val; char *p; val = DatumGetByteaP(fastgetattr(htup, Anum_pg_trigger_tgargs, tgrel->rd_att, &isnull)); if (isnull) elog(ERROR, "tgargs is null in trigger for relation \"%s\"", RelationGetRelationName(relation)); p = (char *) VARDATA(val); build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *)); for (i = 0; i < build->tgnargs; i++) { build->tgargs[i] = pstrdup(p); p += strlen(p) + 1; } } else build->tgargs = NULL; datum = fastgetattr(htup, Anum_pg_trigger_tgqual, tgrel->rd_att, &isnull); if (!isnull) build->tgqual = TextDatumGetCString(datum); else build->tgqual = NULL; numtrigs++; } systable_endscan(tgscan); heap_close(tgrel, AccessShareLock); /* There might not be any triggers */ if (numtrigs == 0) { pfree(triggers); return; } /* Build trigdesc */ trigdesc = (TriggerDesc *) palloc0(sizeof(TriggerDesc)); trigdesc->triggers = triggers; trigdesc->numtriggers = numtrigs; for (i = 0; i < numtrigs; i++) SetTriggerFlags(trigdesc, &(triggers[i])); /* Copy completed trigdesc into cache storage */ oldContext = MemoryContextSwitchTo(CacheMemoryContext); relation->trigdesc = CopyTriggerDesc(trigdesc); MemoryContextSwitchTo(oldContext); /* Release working memory */ FreeTriggerDesc(trigdesc); }
void RemoveTriggerById | ( | Oid | trigOid | ) |
Definition at line 1031 of file trigger.c.
References AccessExclusiveLock, allowSystemTableMods, BTEqualStrategyNumber, CacheInvalidateRelcache(), elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_close, heap_open(), HeapTupleIsValid, IsSystemRelation(), NoLock, ObjectIdAttributeNumber, ObjectIdGetDatum, RelationData::rd_rel, RelationGetRelationName, RELKIND_RELATION, RELKIND_VIEW, RowExclusiveLock, ScanKeyInit(), simple_heap_delete(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, TriggerOidIndexId, and TriggerRelationId.
Referenced by doDeletion().
{ Relation tgrel; SysScanDesc tgscan; ScanKeyData skey[1]; HeapTuple tup; Oid relid; Relation rel; tgrel = heap_open(TriggerRelationId, RowExclusiveLock); /* * Find the trigger to delete. */ ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(trigOid)); tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true, SnapshotNow, 1, skey); tup = systable_getnext(tgscan); if (!HeapTupleIsValid(tup)) elog(ERROR, "could not find tuple for trigger %u", trigOid); /* * Open and exclusive-lock the relation the trigger belongs to. */ relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid; rel = heap_open(relid, AccessExclusiveLock); if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_VIEW) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table or view", RelationGetRelationName(rel)))); if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); /* * Delete the pg_trigger tuple. */ simple_heap_delete(tgrel, &tup->t_self); systable_endscan(tgscan); heap_close(tgrel, RowExclusiveLock); /* * We do not bother to try to determine whether any other triggers remain, * which would be needed in order to decide whether it's safe to clear the * relation's relhastriggers. (In any case, there might be a concurrent * process adding new triggers.) Instead, just force a relcache inval to * make other backends (and this one too!) rebuild their relcache entries. * There's no great harm in leaving relhastriggers true even if there are * no triggers left. */ CacheInvalidateRelcache(rel); /* Keep lock on trigger's rel until end of xact */ heap_close(rel, NoLock); }
Oid renametrig | ( | RenameStmt * | stmt | ) |
Definition at line 1200 of file trigger.c.
References AccessExclusiveLock, Anum_pg_trigger_tgname, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogUpdateIndexes(), ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_close, heap_copytuple(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, InvokeObjectPostAlterHook, namestrcpy(), RenameStmt::newname, NoLock, NULL, ObjectIdGetDatum, PointerGetDatum, RangeVarCallbackForRenameTrigger(), RangeVarGetRelidExtended(), RenameStmt::relation, relation_close(), relation_open(), RelationGetRelationName, RowExclusiveLock, ScanKeyInit(), simple_heap_update(), SnapshotNow, RenameStmt::subname, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, TriggerRelationId, and TriggerRelidNameIndexId.
Referenced by ExecRenameStmt().
{ Oid tgoid; Relation targetrel; Relation tgrel; HeapTuple tuple; SysScanDesc tgscan; ScanKeyData key[2]; Oid relid; /* * Look up name, check permissions, and acquire lock (which we will NOT * release until end of transaction). */ relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, false, false, RangeVarCallbackForRenameTrigger, NULL); /* Have lock already, so just need to build relcache entry. */ targetrel = relation_open(relid, NoLock); /* * Scan pg_trigger twice for existing triggers on relation. We do this in * order to ensure a trigger does not exist with newname (The unique index * on tgrelid/tgname would complain anyway) and to ensure a trigger does * exist with oldname. * * NOTE that this is cool only because we have AccessExclusiveLock on the * relation, so the trigger set won't be changing underneath us. */ tgrel = heap_open(TriggerRelationId, RowExclusiveLock); /* * First pass -- look for name conflict */ ScanKeyInit(&key[0], Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ScanKeyInit(&key[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, PointerGetDatum(stmt->newname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("trigger \"%s\" for relation \"%s\" already exists", stmt->newname, RelationGetRelationName(targetrel)))); systable_endscan(tgscan); /* * Second pass -- look for trigger existing with oldname and update */ ScanKeyInit(&key[0], Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ScanKeyInit(&key[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, PointerGetDatum(stmt->subname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { tgoid = HeapTupleGetOid(tuple); /* * Update pg_trigger tuple with new tgname. */ tuple = heap_copytuple(tuple); /* need a modifiable copy */ namestrcpy(&((Form_pg_trigger) GETSTRUCT(tuple))->tgname, stmt->newname); simple_heap_update(tgrel, &tuple->t_self, tuple); /* keep system catalog indexes current */ CatalogUpdateIndexes(tgrel, tuple); InvokeObjectPostAlterHook(TriggerRelationId, HeapTupleGetOid(tuple), 0); /* * Invalidate relation's relcache entry so that other backends (and * this one too!) are sent SI message to make them rebuild relcache * entries. (Ideally this should happen automatically...) */ CacheInvalidateRelcache(targetrel); } else { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("trigger \"%s\" for table \"%s\" does not exist", stmt->subname, RelationGetRelationName(targetrel)))); } systable_endscan(tgscan); heap_close(tgrel, RowExclusiveLock); /* * Close rel, but keep exclusive lock! */ relation_close(targetrel, NoLock); return tgoid; }
bool RI_FKey_fk_upd_check_required | ( | Trigger * | trigger, | |
Relation | fk_rel, | |||
HeapTuple | old_row, | |||
HeapTuple | new_row | |||
) |
Definition at line 2140 of file ri_triggers.c.
References RI_ConstraintInfo::confmatchtype, elog, ereport, errcode(), errmsg(), ERROR, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, HeapTupleHeaderGetXmin, ri_FetchConstraintInfo(), RI_KEYS_ALL_NULL, RI_KEYS_NONE_NULL, RI_KEYS_SOME_NULL, ri_KeysEqual(), ri_NullCheck(), HeapTupleData::t_data, and TransactionIdIsCurrentTransactionId().
Referenced by AfterTriggerSaveEvent().
{ const RI_ConstraintInfo *riinfo; /* * Get arguments. */ riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false); switch (riinfo->confmatchtype) { case FKCONSTR_MATCH_SIMPLE: /* * If any new key value is NULL, the row must satisfy the * constraint, so no check is needed. */ if (ri_NullCheck(new_row, riinfo, false) != RI_KEYS_NONE_NULL) return false; /* * If the original row was inserted by our own transaction, we * must fire the trigger whether or not the keys are equal. This * is because our UPDATE will invalidate the INSERT so that the * INSERT RI trigger will not do anything; so we had better do the * UPDATE check. (We could skip this if we knew the INSERT * trigger already fired, but there is no easy way to know that.) */ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data))) return true; /* If all old and new key values are equal, no check is needed */ if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false)) return false; /* Else we need to fire the trigger. */ return true; case FKCONSTR_MATCH_FULL: /* * If all new key values are NULL, the row must satisfy the * constraint, so no check is needed. On the other hand, if only * some of them are NULL, the row must fail the constraint. We * must not throw error here, because the row might get * invalidated before the constraint is to be checked, but we * should queue the event to apply the check later. */ switch (ri_NullCheck(new_row, riinfo, false)) { case RI_KEYS_ALL_NULL: return false; case RI_KEYS_SOME_NULL: return true; case RI_KEYS_NONE_NULL: break; /* continue with the check */ } /* * If the original row was inserted by our own transaction, we * must fire the trigger whether or not the keys are equal. This * is because our UPDATE will invalidate the INSERT so that the * INSERT RI trigger will not do anything; so we had better do the * UPDATE check. (We could skip this if we knew the INSERT * trigger already fired, but there is no easy way to know that.) */ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data))) return true; /* If all old and new key values are equal, no check is needed */ if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false)) return false; /* Else we need to fire the trigger. */ return true; /* Handle MATCH PARTIAL check. */ case FKCONSTR_MATCH_PARTIAL: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("MATCH PARTIAL not yet implemented"))); break; default: elog(ERROR, "unrecognized confmatchtype: %d", riinfo->confmatchtype); break; } /* Never reached */ return false; }
bool RI_FKey_pk_upd_check_required | ( | Trigger * | trigger, | |
Relation | pk_rel, | |||
HeapTuple | old_row, | |||
HeapTuple | new_row | |||
) |
Definition at line 2083 of file ri_triggers.c.
References RI_ConstraintInfo::confmatchtype, elog, ereport, errcode(), errmsg(), ERROR, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, ri_FetchConstraintInfo(), RI_KEYS_NONE_NULL, ri_KeysEqual(), and ri_NullCheck().
Referenced by AfterTriggerSaveEvent().
{ const RI_ConstraintInfo *riinfo; /* * Get arguments. */ riinfo = ri_FetchConstraintInfo(trigger, pk_rel, true); switch (riinfo->confmatchtype) { case FKCONSTR_MATCH_SIMPLE: case FKCONSTR_MATCH_FULL: /* * If any old key value is NULL, the row could not have been * referenced by an FK row, so no check is needed. */ if (ri_NullCheck(old_row, riinfo, true) != RI_KEYS_NONE_NULL) return false; /* If all old and new key values are equal, no check is needed */ if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true)) return false; /* Else we need to fire the trigger. */ return true; /* Handle MATCH PARTIAL check. */ case FKCONSTR_MATCH_PARTIAL: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("MATCH PARTIAL not yet implemented"))); break; default: elog(ERROR, "unrecognized confmatchtype: %d", riinfo->confmatchtype); break; } /* Never reached */ return false; }
int RI_FKey_trigger_type | ( | Oid | tgfoid | ) |
Definition at line 3611 of file ri_triggers.c.
Referenced by AfterTriggerSaveEvent(), and CreateTrigger().
{ switch (tgfoid) { case F_RI_FKEY_CASCADE_DEL: case F_RI_FKEY_CASCADE_UPD: case F_RI_FKEY_RESTRICT_DEL: case F_RI_FKEY_RESTRICT_UPD: case F_RI_FKEY_SETNULL_DEL: case F_RI_FKEY_SETNULL_UPD: case F_RI_FKEY_SETDEFAULT_DEL: case F_RI_FKEY_SETDEFAULT_UPD: case F_RI_FKEY_NOACTION_DEL: case F_RI_FKEY_NOACTION_UPD: return RI_TRIGGER_PK; case F_RI_FKEY_CHECK_INS: case F_RI_FKEY_CHECK_UPD: return RI_TRIGGER_FK; } return RI_TRIGGER_NONE; }
Definition at line 2251 of file ri_triggers.c.
References appendStringInfo(), AtEOXact_GUC(), bms_add_member(), RI_ConstraintInfo::confmatchtype, RI_ConstraintInfo::conname, StringInfoData::data, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, errtableconstraint(), ExecCheckRTPerms(), RI_ConstraintInfo::fk_attnums, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, GetLatestSnapshot(), GUC_ACTION_SAVE, i, initStringInfo(), InvalidSnapshot, list_make2, maintenance_work_mem, makeNode, MAX_QUOTED_NAME_LEN, NameStr, NewGUCNestLevel(), RI_ConstraintInfo::nkeys, NULL, RI_ConstraintInfo::pf_eq_oprs, PGC_S_SESSION, PGC_USERSET, RI_ConstraintInfo::pk_attnums, quoteOneName(), quoteRelationName(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RangeTblEntry::relid, RangeTblEntry::relkind, RangeTblEntry::requiredPerms, ri_FetchConstraintInfo(), ri_GenerateQual(), ri_GenerateQualCollation(), RI_KEYS_NONE_NULL, ri_NullCheck(), RI_PLAN_CHECK_LOOKUPPK, ri_ReportViolation(), RIAttCollation, RIAttName, RIAttType, RangeTblEntry::rtekind, RangeTblEntry::selectedCols, set_config_option(), snprintf(), SPI_connect(), SPI_execute_snapshot(), SPI_finish(), SPI_OK_FINISH, SPI_OK_SELECT, SPI_prepare(), SPI_processed, SPI_result, SPI_tuptable, SPITupleTable::tupdesc, and SPITupleTable::vals.
Referenced by validateForeignKeyConstraint().
{ const RI_ConstraintInfo *riinfo; StringInfoData querybuf; char pkrelname[MAX_QUOTED_REL_NAME_LEN]; char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; RangeTblEntry *pkrte; RangeTblEntry *fkrte; const char *sep; int i; int save_nestlevel; char workmembuf[32]; int spi_result; SPIPlanPtr qplan; /* Fetch constraint info. */ riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false); /* * Check to make sure current user has enough permissions to do the test * query. (If not, caller can fall back to the trigger method, which * works because it changes user IDs on the fly.) * * XXX are there any other show-stopper conditions to check? */ pkrte = makeNode(RangeTblEntry); pkrte->rtekind = RTE_RELATION; pkrte->relid = RelationGetRelid(pk_rel); pkrte->relkind = pk_rel->rd_rel->relkind; pkrte->requiredPerms = ACL_SELECT; fkrte = makeNode(RangeTblEntry); fkrte->rtekind = RTE_RELATION; fkrte->relid = RelationGetRelid(fk_rel); fkrte->relkind = fk_rel->rd_rel->relkind; fkrte->requiredPerms = ACL_SELECT; for (i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); } if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) return false; /*---------- * The query string built is: * SELECT fk.keycols FROM ONLY relname fk * LEFT OUTER JOIN ONLY pkrelname pk * ON (pk.pkkeycol1=fk.keycol1 [AND ...]) * WHERE pk.pkkeycol1 IS NULL AND * For MATCH SIMPLE: * (fk.keycol1 IS NOT NULL [AND ...]) * For MATCH FULL: * (fk.keycol1 IS NOT NULL [OR ...]) * * We attach COLLATE clauses to the operators when comparing columns * that have different collations. *---------- */ initStringInfo(&querybuf); appendStringInfo(&querybuf, "SELECT "); sep = ""; for (i = 0; i < riinfo->nkeys; i++) { quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i])); appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname); sep = ", "; } quoteRelationName(pkrelname, pk_rel); quoteRelationName(fkrelname, fk_rel); appendStringInfo(&querybuf, " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON", fkrelname, pkrelname); strcpy(pkattname, "pk."); strcpy(fkattname, "fk."); sep = "("; for (i = 0; i < riinfo->nkeys; i++) { Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]); Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]); Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]); Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]); quoteOneName(pkattname + 3, RIAttName(pk_rel, riinfo->pk_attnums[i])); quoteOneName(fkattname + 3, RIAttName(fk_rel, riinfo->fk_attnums[i])); ri_GenerateQual(&querybuf, sep, pkattname, pk_type, riinfo->pf_eq_oprs[i], fkattname, fk_type); if (pk_coll != fk_coll) ri_GenerateQualCollation(&querybuf, pk_coll); sep = "AND"; } /* * It's sufficient to test any one pk attribute for null to detect a join * failure. */ quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0])); appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname); sep = ""; for (i = 0; i < riinfo->nkeys; i++) { quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i])); appendStringInfo(&querybuf, "%sfk.%s IS NOT NULL", sep, fkattname); switch (riinfo->confmatchtype) { case FKCONSTR_MATCH_SIMPLE: sep = " AND "; break; case FKCONSTR_MATCH_FULL: sep = " OR "; break; case FKCONSTR_MATCH_PARTIAL: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("MATCH PARTIAL not yet implemented"))); break; default: elog(ERROR, "unrecognized confmatchtype: %d", riinfo->confmatchtype); break; } } appendStringInfo(&querybuf, ")"); /* * Temporarily increase work_mem so that the check query can be executed * more efficiently. It seems okay to do this because the query is simple * enough to not use a multiple of work_mem, and one typically would not * have many large foreign-key validations happening concurrently. So * this seems to meet the criteria for being considered a "maintenance" * operation, and accordingly we use maintenance_work_mem. * * We use the equivalent of a function SET option to allow the setting to * persist for exactly the duration of the check query. guc.c also takes * care of undoing the setting on error. */ save_nestlevel = NewGUCNestLevel(); snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem); (void) set_config_option("work_mem", workmembuf, PGC_USERSET, PGC_S_SESSION, GUC_ACTION_SAVE, true, 0); if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); /* * Generate the plan. We don't need to cache it, and there are no * arguments to the plan. */ qplan = SPI_prepare(querybuf.data, 0, NULL); if (qplan == NULL) elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querybuf.data); /* * Run the plan. For safety we force a current snapshot to be used. (In * transaction-snapshot mode, this arguably violates transaction isolation * rules, but we really haven't got much choice.) We don't need to * register the snapshot, because SPI_execute_snapshot will see to it. We * need at most one tuple returned, so pass limit = 1. */ spi_result = SPI_execute_snapshot(qplan, NULL, NULL, GetLatestSnapshot(), InvalidSnapshot, true, false, 1); /* Check result */ if (spi_result != SPI_OK_SELECT) elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); /* Did we find a tuple violating the constraint? */ if (SPI_processed > 0) { HeapTuple tuple = SPI_tuptable->vals[0]; TupleDesc tupdesc = SPI_tuptable->tupdesc; RI_ConstraintInfo fake_riinfo; /* * The columns to look at in the result tuple are 1..N, not whatever * they are in the fk_rel. Hack up riinfo so that the subroutines * called here will behave properly. * * In addition to this, we have to pass the correct tupdesc to * ri_ReportViolation, overriding its normal habit of using the pk_rel * or fk_rel's tupdesc. */ memcpy(&fake_riinfo, riinfo, sizeof(RI_ConstraintInfo)); for (i = 0; i < fake_riinfo.nkeys; i++) fake_riinfo.fk_attnums[i] = i + 1; /* * If it's MATCH FULL, and there are any nulls in the FK keys, * complain about that rather than the lack of a match. MATCH FULL * disallows partially-null FK rows. */ if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL && ri_NullCheck(tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL) ereport(ERROR, (errcode(ERRCODE_FOREIGN_KEY_VIOLATION), errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"", RelationGetRelationName(fk_rel), NameStr(fake_riinfo.conname)), errdetail("MATCH FULL does not allow mixing of null and nonnull key values."), errtableconstraint(fk_rel, NameStr(fake_riinfo.conname)))); /* * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK * query, which isn't true, but will cause it to use * fake_riinfo.fk_attnums as we need. */ ri_ReportViolation(&fake_riinfo, pk_rel, fk_rel, tuple, tupdesc, RI_PLAN_CHECK_LOOKUPPK, false); } if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); /* * Restore work_mem. */ AtEOXact_GUC(true, save_nestlevel); return true; }
PGDLLIMPORT int SessionReplicationRole |
Definition at line 61 of file trigger.c.
Referenced by assign_session_replication_role(), filter_event_trigger(), matchLocks(), and TriggerEnabled().