PostgreSQL Source Code git master
Loading...
Searching...
No Matches
trigger.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/tupconvert.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/partition.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/instrument.h"
#include "miscadmin.h"
#include "nodes/bitmapset.h"
#include "nodes/makefuncs.h"
#include "optimizer/optimizer.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc_hooks.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/plancache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tuplestore.h"
Include dependency graph for trigger.c:

Go to the source code of this file.

Data Structures

struct  SetConstraintTriggerData
 
struct  SetConstraintStateData
 
struct  AfterTriggerSharedData
 
struct  AfterTriggerEventData
 
struct  AfterTriggerEventDataNoOids
 
struct  AfterTriggerEventDataOneCtid
 
struct  AfterTriggerEventDataZeroCtids
 
struct  AfterTriggerEventChunk
 
struct  AfterTriggerEventList
 
struct  AfterTriggersData
 
struct  AfterTriggersQueryData
 
struct  AfterTriggersTransData
 
struct  AfterTriggersTableData
 
struct  AfterTriggerCallbackItem
 

Macros

#define AFTER_TRIGGER_OFFSET   0x07FFFFFF /* must be low-order bits */
 
#define AFTER_TRIGGER_DONE   0x80000000
 
#define AFTER_TRIGGER_IN_PROGRESS   0x40000000
 
#define AFTER_TRIGGER_FDW_REUSE   0x00000000
 
#define AFTER_TRIGGER_FDW_FETCH   0x20000000
 
#define AFTER_TRIGGER_1CTID   0x10000000
 
#define AFTER_TRIGGER_2CTID   0x30000000
 
#define AFTER_TRIGGER_CP_UPDATE   0x08000000
 
#define AFTER_TRIGGER_TUP_BITS   0x38000000
 
#define SizeofTriggerEvent(evt)
 
#define GetTriggerSharedData(evt)    ((AfterTriggerShared) ((char *) (evt) + ((evt)->ate_flags & AFTER_TRIGGER_OFFSET)))
 
#define CHUNK_DATA_START(cptr)   ((char *) (cptr) + MAXALIGN(sizeof(AfterTriggerEventChunk)))
 
#define for_each_chunk(cptr, evtlist)    for (cptr = (evtlist).head; cptr != NULL; cptr = cptr->next)
 
#define for_each_event(eptr, cptr)
 
#define for_each_event_chunk(eptr, cptr, evtlist)    for_each_chunk(cptr, evtlist) for_each_event(eptr, cptr)
 
#define for_each_chunk_from(cptr)    for (; cptr != NULL; cptr = cptr->next)
 
#define for_each_event_from(eptr, cptr)
 
#define MIN_CHUNK_SIZE   1024
 
#define MAX_CHUNK_SIZE   (1024*1024)
 

Typedefs

typedef struct SetConstraintTriggerData SetConstraintTriggerData
 
typedef struct SetConstraintTriggerDataSetConstraintTrigger
 
typedef struct SetConstraintStateData SetConstraintStateData
 
typedef SetConstraintStateDataSetConstraintState
 
typedef uint32 TriggerFlags
 
typedef struct AfterTriggerSharedDataAfterTriggerShared
 
typedef struct AfterTriggerSharedData AfterTriggerSharedData
 
typedef struct AfterTriggerEventDataAfterTriggerEvent
 
typedef struct AfterTriggerEventData AfterTriggerEventData
 
typedef struct AfterTriggerEventDataNoOids AfterTriggerEventDataNoOids
 
typedef struct AfterTriggerEventDataOneCtid AfterTriggerEventDataOneCtid
 
typedef struct AfterTriggerEventDataZeroCtids AfterTriggerEventDataZeroCtids
 
typedef struct AfterTriggerEventChunk AfterTriggerEventChunk
 
typedef struct AfterTriggerEventList AfterTriggerEventList
 
typedef struct AfterTriggersQueryData AfterTriggersQueryData
 
typedef struct AfterTriggersTransData AfterTriggersTransData
 
typedef struct AfterTriggersTableData AfterTriggersTableData
 
typedef struct AfterTriggersData AfterTriggersData
 
typedef struct AfterTriggerCallbackItem AfterTriggerCallbackItem
 

Functions

static void renametrig_internal (Relation tgrel, Relation targetrel, HeapTuple trigtup, const char *newname, const char *expected_name)
 
static void renametrig_partition (Relation tgrel, Oid partitionId, Oid parentTriggerOid, const char *newname, const char *expected_name)
 
static void SetTriggerFlags (TriggerDesc *trigdesc, Trigger *trigger)
 
static bool GetTupleForTrigger (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, bool do_epq_recheck, TupleTableSlot **epqslot, TM_Result *tmresultp, TM_FailureData *tmfdp)
 
static bool TriggerEnabled (EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
static HeapTuple ExecCallTriggerFunc (TriggerData *trigdata, int tgindx, FmgrInfo *finfo, TriggerInstrumentation *instr, MemoryContext per_tuple_context)
 
static void AfterTriggerSaveEvent (EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldslot, TupleTableSlot *newslot, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
static void AfterTriggerEnlargeQueryState (void)
 
static bool before_stmt_triggers_fired (Oid relid, CmdType cmdType)
 
static HeapTuple check_modified_virtual_generated (TupleDesc tupdesc, HeapTuple tuple)
 
ObjectAddress CreateTrigger (const CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
 
ObjectAddress CreateTriggerFiringOn (const CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
 
void TriggerSetParentTrigger (Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
 
void RemoveTriggerById (Oid trigOid)
 
Oid get_trigger_oid (Oid relid, const char *trigname, bool missing_ok)
 
static void RangeVarCallbackForRenameTrigger (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
ObjectAddress renametrig (RenameStmt *stmt)
 
void EnableDisableTrigger (Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
 
void RelationBuildTriggers (Relation relation)
 
TriggerDescCopyTriggerDesc (TriggerDesc *trigdesc)
 
void FreeTriggerDesc (TriggerDesc *trigdesc)
 
const charFindTriggerIncompatibleWithInheritance (TriggerDesc *trigdesc)
 
void ExecBSInsertTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASInsertTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecARInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
bool ExecIRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecBSDeleteTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASDeleteTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRDeleteTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot, TM_Result *tmresult, TM_FailureData *tmfd, bool is_merge_delete)
 
void ExecARDeleteTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
bool ExecIRDeleteTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
 
void ExecBSUpdateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASUpdateTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRUpdateTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, TM_Result *tmresult, TM_FailureData *tmfd, bool is_merge_update)
 
void ExecARUpdateTriggers (EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
bool ExecIRUpdateTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
 
void ExecBSTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
static void AfterTriggerExecute (EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, ResultRelInfo *src_relInfo, ResultRelInfo *dst_relInfo, TriggerDesc *trigdesc, FmgrInfo *finfo, TriggerInstrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
 
static AfterTriggersTableDataGetAfterTriggersTableData (Oid relid, CmdType cmdType)
 
static TupleTableSlotGetAfterTriggersStoreSlot (AfterTriggersTableData *table, TupleDesc tupdesc)
 
static TuplestorestateGetAfterTriggersTransitionTable (int event, TupleTableSlot *oldslot, TupleTableSlot *newslot, TransitionCaptureState *transition_capture)
 
static void TransitionTableAddTuple (EState *estate, int event, TransitionCaptureState *transition_capture, ResultRelInfo *relinfo, TupleTableSlot *slot, TupleTableSlot *original_insert_tuple, Tuplestorestate *tuplestore)
 
static void AfterTriggerFreeQuery (AfterTriggersQueryData *qs)
 
static SetConstraintState SetConstraintStateCreate (int numalloc)
 
static SetConstraintState SetConstraintStateCopy (SetConstraintState origstate)
 
static SetConstraintState SetConstraintStateAddItem (SetConstraintState state, Oid tgoid, bool tgisdeferred)
 
static void cancel_prior_stmt_triggers (Oid relid, CmdType cmdType, int tgevent)
 
static void FireAfterTriggerBatchCallbacks (List *callbacks)
 
static TuplestorestateGetCurrentFDWTuplestore (void)
 
static bool afterTriggerCheckState (AfterTriggerShared evtshared)
 
static BitmapsetafterTriggerCopyBitmap (Bitmapset *src)
 
static void afterTriggerAddEvent (AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
 
static void afterTriggerFreeEventList (AfterTriggerEventList *events)
 
static void afterTriggerRestoreEventList (AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
 
static void afterTriggerDeleteHeadEventChunk (AfterTriggersQueryData *qs)
 
static bool afterTriggerMarkEvents (AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
 
static bool afterTriggerInvokeEvents (AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
 
TransitionCaptureStateMakeTransitionCaptureState (TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
 
void AfterTriggerBeginXact (void)
 
void AfterTriggerBeginQuery (void)
 
void AfterTriggerEndQuery (EState *estate)
 
void AfterTriggerFireDeferred (void)
 
void AfterTriggerEndXact (bool isCommit)
 
void AfterTriggerBeginSubXact (void)
 
void AfterTriggerEndSubXact (bool isCommit)
 
void AfterTriggerSetState (ConstraintsSetStmt *stmt)
 
bool AfterTriggerPendingOnRel (Oid relid)
 
void assign_session_replication_role (int newval, void *extra)
 
Datum pg_trigger_depth (PG_FUNCTION_ARGS)
 
void RegisterAfterTriggerBatchCallback (AfterTriggerBatchCallback callback, void *arg)
 
bool AfterTriggerIsActive (void)
 

Variables

int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN
 
static int MyTriggerDepth = 0
 
static AfterTriggersData afterTriggers
 

Macro Definition Documentation

◆ AFTER_TRIGGER_1CTID

#define AFTER_TRIGGER_1CTID   0x10000000

Definition at line 3714 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3715 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3716 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3709 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3713 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3712 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3710 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

#define AFTER_TRIGGER_OFFSET   0x07FFFFFF /* must be low-order bits */

Definition at line 3708 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3717 of file trigger.c.

◆ CHUNK_DATA_START

#define CHUNK_DATA_START (   cptr)    ((char *) (cptr) + MAXALIGN(sizeof(AfterTriggerEventChunk)))

Definition at line 3797 of file trigger.c.

◆ for_each_chunk

#define for_each_chunk (   cptr,
  evtlist 
)     for (cptr = (evtlist).head; cptr != NULL; cptr = cptr->next)

Definition at line 3808 of file trigger.c.

3907{
3908 CommandId firing_counter; /* next firing ID to assign */
3909 SetConstraintState state; /* the active S C state */
3910 AfterTriggerEventList events; /* deferred-event list */
3911 MemoryContext event_cxt; /* memory context for events, if any */
3912
3913 /* per-query-level data: */
3914 AfterTriggersQueryData *query_stack; /* array of structs shown below */
3915 int query_depth; /* current index in above array */
3916 int maxquerydepth; /* allocated len of above array */
3917
3918 /* per-subtransaction-level data: */
3919 AfterTriggersTransData *trans_stack; /* array of structs shown below */
3920 int maxtransdepth; /* allocated len of above array */
3921
3922 List *batch_callbacks; /* List of AfterTriggerCallbackItem; for
3923 * deferred constraints */
3924 bool firing_batch_callbacks; /* true when in
3925 * FireAfterTriggerBatchCallbacks() */
3926
3927 /*
3928 * Incremented around the trigger-firing loops in AfterTriggerEndQuery,
3929 * AfterTriggerFireDeferred, and AfterTriggerSetState. Used by
3930 * AfterTriggerIsActive() to signal that after-trigger firing is active.
3931 */
3932 int firing_depth;
3934
3936{
3937 AfterTriggerEventList events; /* events pending from this query */
3938 Tuplestorestate *fdw_tuplestore; /* foreign tuples for said events */
3939 List *tables; /* list of AfterTriggersTableData, see below */
3940 List *batch_callbacks; /* List of AfterTriggerCallbackItem */
3941};
3942
3944{
3945 /* these fields are just for resetting at subtrans abort: */
3946 SetConstraintState state; /* saved S C state, or NULL if not yet saved */
3947 AfterTriggerEventList events; /* saved list pointer */
3948 int query_depth; /* saved query_depth */
3949 CommandId firing_counter; /* saved firing_counter */
3950};
3951
3953{
3954 /* relid + cmdType form the lookup key for these structs: */
3955 Oid relid; /* target table's OID */
3956 CmdType cmdType; /* event type, CMD_INSERT/UPDATE/DELETE */
3957 bool closed; /* true when no longer OK to add tuples */
3958 bool before_trig_done; /* did we already queue BS triggers? */
3959 bool after_trig_done; /* did we already queue AS triggers? */
3960 AfterTriggerEventList after_trig_events; /* if so, saved list pointer */
3961
3962 /* "old" transition table for UPDATE/DELETE, if any */
3964 /* "new" transition table for INSERT/UPDATE, if any */
3966
3967 TupleTableSlot *storeslot; /* for converting to tuplestore's format */
3968};
3969
3970/* Entry in afterTriggers.batch_callbacks */
3971typedef struct AfterTriggerCallbackItem
3972{
3974 void *arg;
3976
3978
3979static void AfterTriggerExecute(EState *estate,
3980 AfterTriggerEvent event,
3984 TriggerDesc *trigdesc,
3985 FmgrInfo *finfo,
3991 CmdType cmdType);
3993 TupleDesc tupdesc);
3997 TransitionCaptureState *transition_capture);
3998static void TransitionTableAddTuple(EState *estate,
3999 int event,
4000 TransitionCaptureState *transition_capture,
4002 TupleTableSlot *slot,
4004 Tuplestorestate *tuplestore);
4006static SetConstraintState SetConstraintStateCreate(int numalloc);
4009 Oid tgoid, bool tgisdeferred);
4010static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent);
4011
4012static void FireAfterTriggerBatchCallbacks(List *callbacks);
4013
4014/*
4015 * Get the FDW tuplestore for the current trigger query level, creating it
4016 * if necessary.
4017 */
4018static Tuplestorestate *
4020{
4021 Tuplestorestate *ret;
4022
4024 if (ret == NULL)
4025 {
4028
4029 /*
4030 * Make the tuplestore valid until end of subtransaction. We really
4031 * only need it until AfterTriggerEndQuery().
4032 */
4036
4037 ret = tuplestore_begin_heap(false, false, work_mem);
4038
4041
4043 }
4044
4045 return ret;
4046}
4047
4048/* ----------
4049 * afterTriggerCheckState()
4050 *
4051 * Returns true if the trigger event is actually in state DEFERRED.
4052 * ----------
4053 */
4054static bool
4056{
4057 Oid tgoid = evtshared->ats_tgoid;
4059 int i;
4060
4061 /*
4062 * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
4063 * constraints declared NOT DEFERRABLE), the state is always false.
4064 */
4065 if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
4066 return false;
4067
4068 /*
4069 * If constraint state exists, SET CONSTRAINTS might have been executed
4070 * either for this trigger or for all triggers.
4071 */
4072 if (state != NULL)
4073 {
4074 /* Check for SET CONSTRAINTS for this specific trigger. */
4075 for (i = 0; i < state->numstates; i++)
4076 {
4077 if (state->trigstates[i].sct_tgoid == tgoid)
4078 return state->trigstates[i].sct_tgisdeferred;
4079 }
4080
4081 /* Check for SET CONSTRAINTS ALL. */
4082 if (state->all_isset)
4083 return state->all_isdeferred;
4084 }
4085
4086 /*
4087 * Otherwise return the default state for the trigger.
4088 */
4089 return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
4090}
4091
4092/* ----------
4093 * afterTriggerCopyBitmap()
4094 *
4095 * Copy bitmap into AfterTriggerEvents memory context, which is where the after
4096 * trigger events are kept.
4097 * ----------
4098 */
4099static Bitmapset *
4101{
4102 Bitmapset *dst;
4104
4105 if (src == NULL)
4106 return NULL;
4107
4109
4110 dst = bms_copy(src);
4111
4113
4114 return dst;
4115}
4116
4117/* ----------
4118 * afterTriggerAddEvent()
4119 *
4120 * Add a new trigger event to the specified queue.
4121 * The passed-in event data is copied.
4122 * ----------
4123 */
4124static void
4127{
4133
4134 /*
4135 * If empty list or not enough room in the tail chunk, make a new chunk.
4136 * We assume here that a new shared record will always be needed.
4137 */
4138 chunk = events->tail;
4139 if (chunk == NULL ||
4140 chunk->endfree - chunk->freeptr < needed)
4141 {
4143
4144 /* Create event context if we didn't already */
4148 "AfterTriggerEvents",
4150
4151 /*
4152 * Chunk size starts at 1KB and is allowed to increase up to 1MB.
4153 * These numbers are fairly arbitrary, though there is a hard limit at
4154 * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
4155 * shared records using the available space in ate_flags. Another
4156 * constraint is that if the chunk size gets too huge, the search loop
4157 * below would get slow given a (not too common) usage pattern with
4158 * many distinct event types in a chunk. Therefore, we double the
4159 * preceding chunk size only if there weren't too many shared records
4160 * in the preceding chunk; otherwise we halve it. This gives us some
4161 * ability to adapt to the actual usage pattern of the current query
4162 * while still having large chunk sizes in typical usage. All chunk
4163 * sizes used should be MAXALIGN multiples, to ensure that the shared
4164 * records will be aligned safely.
4165 */
4166#define MIN_CHUNK_SIZE 1024
4167#define MAX_CHUNK_SIZE (1024*1024)
4168
4169#if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
4170#error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
4171#endif
4172
4173 if (chunk == NULL)
4175 else
4176 {
4177 /* preceding chunk size... */
4178 chunksize = chunk->endptr - (char *) chunk;
4179 /* check number of shared records in preceding chunk */
4180 if ((chunk->endptr - chunk->endfree) <=
4181 (100 * sizeof(AfterTriggerSharedData)))
4182 chunksize *= 2; /* okay, double it */
4183 else
4184 chunksize /= 2; /* too many shared records */
4186 }
4188 chunk->next = NULL;
4189 chunk->freeptr = CHUNK_DATA_START(chunk);
4190 chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
4191 Assert(chunk->endfree - chunk->freeptr >= needed);
4192
4193 if (events->tail == NULL)
4194 {
4195 Assert(events->head == NULL);
4196 events->head = chunk;
4197 }
4198 else
4199 events->tail->next = chunk;
4200 events->tail = chunk;
4201 /* events->tailfree is now out of sync, but we'll fix it below */
4202 }
4203
4204 /*
4205 * Try to locate a matching shared-data record already in the chunk. If
4206 * none, make a new one. The search begins with the most recently added
4207 * record, since newer ones are most likely to match.
4208 */
4209 for (newshared = (AfterTriggerShared) chunk->endfree;
4210 (char *) newshared < chunk->endptr;
4211 newshared++)
4212 {
4213 /* compare fields roughly by probability of them being different */
4214 if (newshared->ats_tgoid == evtshared->ats_tgoid &&
4215 newshared->ats_event == evtshared->ats_event &&
4216 newshared->ats_firing_id == 0 &&
4217 newshared->ats_table == evtshared->ats_table &&
4218 newshared->ats_relid == evtshared->ats_relid &&
4219 newshared->ats_rolid == evtshared->ats_rolid &&
4220 bms_equal(newshared->ats_modifiedcols,
4221 evtshared->ats_modifiedcols))
4222 break;
4223 }
4224 if ((char *) newshared >= chunk->endptr)
4225 {
4226 newshared = ((AfterTriggerShared) chunk->endfree) - 1;
4227 *newshared = *evtshared;
4228 /* now we must make a suitably-long-lived copy of the bitmap */
4229 newshared->ats_modifiedcols = afterTriggerCopyBitmap(evtshared->ats_modifiedcols);
4230 newshared->ats_firing_id = 0; /* just to be sure */
4231 chunk->endfree = (char *) newshared;
4232 }
4233
4234 /* Insert the data */
4235 newevent = (AfterTriggerEvent) chunk->freeptr;
4236 memcpy(newevent, event, eventsize);
4237 /* ... and link the new event to its shared record */
4238 newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
4239 newevent->ate_flags |= (char *) newshared - (char *) newevent;
4240
4241 chunk->freeptr += eventsize;
4242 events->tailfree = chunk->freeptr;
4243}
4244
4245/* ----------
4246 * afterTriggerFreeEventList()
4247 *
4248 * Free all the event storage in the given list.
4249 * ----------
4250 */
4251static void
4253{
4255
4256 while ((chunk = events->head) != NULL)
4257 {
4258 events->head = chunk->next;
4259 pfree(chunk);
4260 }
4261 events->tail = NULL;
4262 events->tailfree = NULL;
4263}
4264
4265/* ----------
4266 * afterTriggerRestoreEventList()
4267 *
4268 * Restore an event list to its prior length, removing all the events
4269 * added since it had the value old_events.
4270 * ----------
4271 */
4272static void
4275{
4278
4279 if (old_events->tail == NULL)
4280 {
4281 /* restoring to a completely empty state, so free everything */
4283 }
4284 else
4285 {
4286 *events = *old_events;
4287 /* free any chunks after the last one we want to keep */
4288 for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4289 {
4290 next_chunk = chunk->next;
4291 pfree(chunk);
4292 }
4293 /* and clean up the tail chunk to be the right length */
4294 events->tail->next = NULL;
4295 events->tail->freeptr = events->tailfree;
4296
4297 /*
4298 * We don't make any effort to remove now-unused shared data records.
4299 * They might still be useful, anyway.
4300 */
4301 }
4302}
4303
4304/* ----------
4305 * afterTriggerDeleteHeadEventChunk()
4306 *
4307 * Remove the first chunk of events from the query level's event list.
4308 * Keep any event list pointers elsewhere in the query level's data
4309 * structures in sync.
4310 * ----------
4311 */
4312static void
4314{
4315 AfterTriggerEventChunk *target = qs->events.head;
4316 ListCell *lc;
4317
4318 Assert(target && target->next);
4319
4320 /*
4321 * First, update any pointers in the per-table data, so that they won't be
4322 * dangling. Resetting obsoleted pointers to NULL will make
4323 * cancel_prior_stmt_triggers start from the list head, which is fine.
4324 */
4325 foreach(lc, qs->tables)
4326 {
4328
4329 if (table->after_trig_done &&
4330 table->after_trig_events.tail == target)
4331 {
4332 table->after_trig_events.head = NULL;
4333 table->after_trig_events.tail = NULL;
4334 table->after_trig_events.tailfree = NULL;
4335 }
4336 }
4337
4338 /* Now we can flush the head chunk */
4339 qs->events.head = target->next;
4340 pfree(target);
4341}
4342
4343
4344/* ----------
4345 * AfterTriggerExecute()
4346 *
4347 * Fetch the required tuples back from the heap and fire one
4348 * single trigger function.
4349 *
4350 * Frequently, this will be fired many times in a row for triggers of
4351 * a single relation. Therefore, we cache the open relation and provide
4352 * fmgr lookup cache space at the caller level. (For triggers fired at
4353 * the end of a query, we can even piggyback on the executor's state.)
4354 *
4355 * When fired for a cross-partition update of a partitioned table, the old
4356 * tuple is fetched using 'src_relInfo' (the source leaf partition) and
4357 * the new tuple using 'dst_relInfo' (the destination leaf partition), though
4358 * both are converted into the root partitioned table's format before passing
4359 * to the trigger function.
4360 *
4361 * event: event currently being fired.
4362 * relInfo: result relation for event.
4363 * src_relInfo: source partition of a cross-partition update
4364 * dst_relInfo: its destination partition
4365 * trigdesc: working copy of rel's trigger info.
4366 * finfo: array of fmgr lookup cache entries (one per trigger in trigdesc).
4367 * instr: array of EXPLAIN ANALYZE instrumentation nodes (one per trigger),
4368 * or NULL if no instrumentation is wanted.
4369 * per_tuple_context: memory context to call trigger function in.
4370 * trig_tuple_slot1: scratch slot for tg_trigtuple (foreign tables only)
4371 * trig_tuple_slot2: scratch slot for tg_newtuple (foreign tables only)
4372 * ----------
4373 */
4374static void
4376 AfterTriggerEvent event,
4380 TriggerDesc *trigdesc,
4381 FmgrInfo *finfo, TriggerInstrumentation *instr,
4385{
4386 Relation rel = relInfo->ri_RelationDesc;
4387 Relation src_rel = src_relInfo->ri_RelationDesc;
4388 Relation dst_rel = dst_relInfo->ri_RelationDesc;
4390 Oid tgoid = evtshared->ats_tgoid;
4393 int save_sec_context;
4395 int tgindx;
4396 bool should_free_trig = false;
4397 bool should_free_new = false;
4398
4399 /*
4400 * Locate trigger in trigdesc. It might not be present, and in fact the
4401 * trigdesc could be NULL, if the trigger was dropped since the event was
4402 * queued. In that case, silently do nothing.
4403 */
4404 if (trigdesc == NULL)
4405 return;
4406 for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4407 {
4408 if (trigdesc->triggers[tgindx].tgoid == tgoid)
4409 {
4410 LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4411 break;
4412 }
4413 }
4414 if (LocTriggerData.tg_trigger == NULL)
4415 return;
4416
4417 /*
4418 * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4419 * to include time spent re-fetching tuples in the trigger cost.
4420 */
4421 if (instr)
4422 InstrStartTrigger(instr + tgindx);
4423
4424 /*
4425 * Fetch the required tuple(s).
4426 */
4427 switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4428 {
4430 {
4431 Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4432
4433 if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4435 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4436
4437 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4439 !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4441 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4442 }
4445
4446 /*
4447 * Store tuple in the slot so that tg_trigtuple does not reference
4448 * tuplestore memory. (It is formally possible for the trigger
4449 * function to queue trigger events that add to the same
4450 * tuplestore, which can push other tuples out of memory.) The
4451 * distinction is academic, because we start with a minimal tuple
4452 * that is stored as a heap tuple, constructed in different memory
4453 * context, in the slot anyway.
4454 */
4455 LocTriggerData.tg_trigslot = trig_tuple_slot1;
4456 LocTriggerData.tg_trigtuple =
4458
4459 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4461 {
4462 LocTriggerData.tg_newslot = trig_tuple_slot2;
4463 LocTriggerData.tg_newtuple =
4465 }
4466 else
4467 {
4468 LocTriggerData.tg_newtuple = NULL;
4469 }
4470 break;
4471
4472 default:
4473 if (ItemPointerIsValid(&(event->ate_ctid1)))
4474 {
4476 src_relInfo);
4477
4479 &(event->ate_ctid1),
4481 src_slot))
4482 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4483
4484 /*
4485 * Store the tuple fetched from the source partition into the
4486 * target (root partitioned) table slot, converting if needed.
4487 */
4488 if (src_relInfo != relInfo)
4489 {
4491
4492 LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4493 if (map)
4494 {
4496 src_slot,
4497 LocTriggerData.tg_trigslot);
4498 }
4499 else
4500 ExecCopySlot(LocTriggerData.tg_trigslot, src_slot);
4501 }
4502 else
4503 LocTriggerData.tg_trigslot = src_slot;
4504 LocTriggerData.tg_trigtuple =
4506 }
4507 else
4508 {
4509 LocTriggerData.tg_trigtuple = NULL;
4510 }
4511
4512 /* don't touch ctid2 if not there */
4514 (event->ate_flags & AFTER_TRIGGER_CP_UPDATE)) &&
4515 ItemPointerIsValid(&(event->ate_ctid2)))
4516 {
4518 dst_relInfo);
4519
4521 &(event->ate_ctid2),
4523 dst_slot))
4524 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4525
4526 /*
4527 * Store the tuple fetched from the destination partition into
4528 * the target (root partitioned) table slot, converting if
4529 * needed.
4530 */
4531 if (dst_relInfo != relInfo)
4532 {
4534
4535 LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4536 if (map)
4537 {
4539 dst_slot,
4540 LocTriggerData.tg_newslot);
4541 }
4542 else
4544 }
4545 else
4546 LocTriggerData.tg_newslot = dst_slot;
4547 LocTriggerData.tg_newtuple =
4549 }
4550 else
4551 {
4552 LocTriggerData.tg_newtuple = NULL;
4553 }
4554 }
4555
4556 /*
4557 * Set up the tuplestore information to let the trigger have access to
4558 * transition tables. When we first make a transition table available to
4559 * a trigger, mark it "closed" so that it cannot change anymore. If any
4560 * additional events of the same type get queued in the current trigger
4561 * query level, they'll go into new transition tables.
4562 */
4563 LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4564 if (evtshared->ats_table)
4565 {
4566 if (LocTriggerData.tg_trigger->tgoldtable)
4567 {
4568 LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4569 evtshared->ats_table->closed = true;
4570 }
4571
4572 if (LocTriggerData.tg_trigger->tgnewtable)
4573 {
4574 LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4575 evtshared->ats_table->closed = true;
4576 }
4577 }
4578
4579 /*
4580 * Setup the remaining trigger information
4581 */
4583 LocTriggerData.tg_event =
4585 LocTriggerData.tg_relation = rel;
4586 if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4587 LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4588
4590
4591 /*
4592 * If necessary, become the role that was active when the trigger got
4593 * queued. Note that the role might have been dropped since the trigger
4594 * was queued, but if that is a problem, we will get an error later.
4595 * Checking here would still leave a race condition.
4596 */
4597 GetUserIdAndSecContext(&save_rolid, &save_sec_context);
4598 if (save_rolid != evtshared->ats_rolid)
4600 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
4601
4602 /*
4603 * Call the trigger and throw away any possibly returned updated tuple.
4604 * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4605 */
4607 tgindx,
4608 finfo,
4609 NULL,
4611 if (rettuple != NULL &&
4612 rettuple != LocTriggerData.tg_trigtuple &&
4613 rettuple != LocTriggerData.tg_newtuple)
4615
4616 /* Restore the current role if necessary */
4617 if (save_rolid != evtshared->ats_rolid)
4618 SetUserIdAndSecContext(save_rolid, save_sec_context);
4619
4620 /*
4621 * Release resources
4622 */
4623 if (should_free_trig)
4624 heap_freetuple(LocTriggerData.tg_trigtuple);
4625 if (should_free_new)
4626 heap_freetuple(LocTriggerData.tg_newtuple);
4627
4628 /* don't clear slots' contents if foreign table */
4629 if (trig_tuple_slot1 == NULL)
4630 {
4631 if (LocTriggerData.tg_trigslot)
4632 ExecClearTuple(LocTriggerData.tg_trigslot);
4633 if (LocTriggerData.tg_newslot)
4634 ExecClearTuple(LocTriggerData.tg_newslot);
4635 }
4636
4637 /*
4638 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4639 * the firing of the trigger.
4640 */
4641 if (instr)
4642 InstrStopTrigger(instr + tgindx, 1);
4643}
4644
4645
4646/*
4647 * afterTriggerMarkEvents()
4648 *
4649 * Scan the given event list for not yet invoked events. Mark the ones
4650 * that can be invoked now with the current firing ID.
4651 *
4652 * If move_list isn't NULL, events that are not to be invoked now are
4653 * transferred to move_list.
4654 *
4655 * When immediate_only is true, do not invoke currently-deferred triggers.
4656 * (This will be false only at main transaction exit.)
4657 *
4658 * Returns true if any invokable events were found.
4659 */
4660static bool
4663 bool immediate_only)
4664{
4665 bool found = false;
4666 bool deferred_found = false;
4667 AfterTriggerEvent event;
4669
4670 for_each_event_chunk(event, chunk, *events)
4671 {
4673 bool defer_it = false;
4674
4675 if (!(event->ate_flags &
4677 {
4678 /*
4679 * This trigger hasn't been called or scheduled yet. Check if we
4680 * should call it now.
4681 */
4683 {
4684 defer_it = true;
4685 }
4686 else
4687 {
4688 /*
4689 * Mark it as to be fired in this firing cycle.
4690 */
4691 evtshared->ats_firing_id = afterTriggers.firing_counter;
4692 event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4693 found = true;
4694 }
4695 }
4696
4697 /*
4698 * If it's deferred, move it to move_list, if requested.
4699 */
4700 if (defer_it && move_list != NULL)
4701 {
4702 deferred_found = true;
4703 /* add it to move_list */
4705 /* mark original copy "done" so we don't do it again */
4706 event->ate_flags |= AFTER_TRIGGER_DONE;
4707 }
4708 }
4709
4710 /*
4711 * We could allow deferred triggers if, before the end of the
4712 * security-restricted operation, we were to verify that a SET CONSTRAINTS
4713 * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4714 */
4716 ereport(ERROR,
4718 errmsg("cannot fire deferred trigger within security-restricted operation")));
4719
4720 return found;
4721}
4722
4723/*
4724 * afterTriggerInvokeEvents()
4725 *
4726 * Scan the given event list for events that are marked as to be fired
4727 * in the current firing cycle, and fire them.
4728 *
4729 * If estate isn't NULL, we use its result relation info to avoid repeated
4730 * openings and closing of trigger target relations. If it is NULL, we
4731 * make one locally to cache the info in case there are multiple trigger
4732 * events per rel.
4733 *
4734 * When delete_ok is true, it's safe to delete fully-processed events.
4735 * (We are not very tense about that: we simply reset a chunk to be empty
4736 * if all its events got fired. The objective here is just to avoid useless
4737 * rescanning of events when a trigger queues new events during transaction
4738 * end, so it's not necessary to worry much about the case where only
4739 * some events are fired.)
4740 *
4741 * Returns true if no unfired events remain in the list (this allows us
4742 * to avoid repeating afterTriggerMarkEvents).
4743 */
4744static bool
4747 EState *estate,
4748 bool delete_ok)
4749{
4750 bool all_fired = true;
4753 bool local_estate = false;
4755 Relation rel = NULL;
4756 TriggerDesc *trigdesc = NULL;
4757 FmgrInfo *finfo = NULL;
4760 *slot2 = NULL;
4761
4762 /* Make a local EState if need be */
4763 if (estate == NULL)
4764 {
4765 estate = CreateExecutorState();
4766 local_estate = true;
4767 }
4768
4769 /* Make a per-tuple memory context for trigger function calls */
4772 "AfterTriggerTupleContext",
4774
4775 for_each_chunk(chunk, *events)
4776 {
4777 AfterTriggerEvent event;
4778 bool all_fired_in_chunk = true;
4779
4780 for_each_event(event, chunk)
4781 {
4783
4784 /*
4785 * Is it one for me to fire?
4786 */
4787 if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4788 evtshared->ats_firing_id == firing_id)
4789 {
4791 *dst_rInfo;
4792
4793 /*
4794 * So let's fire it... but first, find the correct relation if
4795 * this is not the same relation as before.
4796 */
4797 if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4798 {
4799 rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4800 NULL);
4801 rel = rInfo->ri_RelationDesc;
4802 /* Catch calls with insufficient relcache refcounting */
4804 trigdesc = rInfo->ri_TrigDesc;
4805 /* caution: trigdesc could be NULL here */
4806 finfo = rInfo->ri_TrigFunctions;
4807 instr = rInfo->ri_TrigInstrument;
4808 if (slot1 != NULL)
4809 {
4812 slot1 = slot2 = NULL;
4813 }
4814 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4815 {
4820 }
4821 }
4822
4823 /*
4824 * Look up source and destination partition result rels of a
4825 * cross-partition update event.
4826 */
4827 if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4829 {
4830 Assert(OidIsValid(event->ate_src_part) &&
4831 OidIsValid(event->ate_dst_part));
4833 event->ate_src_part,
4834 rInfo);
4836 event->ate_dst_part,
4837 rInfo);
4838 }
4839 else
4841
4842 /*
4843 * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4844 * still set, so recursive examinations of the event list
4845 * won't try to re-fire it.
4846 */
4847 AfterTriggerExecute(estate, event, rInfo,
4849 trigdesc, finfo, instr,
4851
4852 /*
4853 * Mark the event as done.
4854 */
4855 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4856 event->ate_flags |= AFTER_TRIGGER_DONE;
4857 }
4858 else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4859 {
4860 /* something remains to be done */
4861 all_fired = all_fired_in_chunk = false;
4862 }
4863 }
4864
4865 /* Clear the chunk if delete_ok and nothing left of interest */
4867 {
4868 chunk->freeptr = CHUNK_DATA_START(chunk);
4869 chunk->endfree = chunk->endptr;
4870
4871 /*
4872 * If it's last chunk, must sync event list's tailfree too. Note
4873 * that delete_ok must NOT be passed as true if there could be
4874 * additional AfterTriggerEventList values pointing at this event
4875 * list, since we'd fail to fix their copies of tailfree.
4876 */
4877 if (chunk == events->tail)
4878 events->tailfree = chunk->freeptr;
4879 }
4880 }
4881 if (slot1 != NULL)
4882 {
4885 }
4886
4887 /* Release working resources */
4889
4890 if (local_estate)
4891 {
4893 ExecResetTupleTable(estate->es_tupleTable, false);
4894 FreeExecutorState(estate);
4895 }
4896
4897 return all_fired;
4898}
4899
4900
4901/*
4902 * GetAfterTriggersTableData
4903 *
4904 * Find or create an AfterTriggersTableData struct for the specified
4905 * trigger event (relation + operation type). Ignore existing structs
4906 * marked "closed"; we don't want to put any additional tuples into them,
4907 * nor change their stmt-triggers-fired state.
4908 *
4909 * Note: the AfterTriggersTableData list is allocated in the current
4910 * (sub)transaction's CurTransactionContext. This is OK because
4911 * we don't need it to live past AfterTriggerEndQuery.
4912 */
4915{
4919 ListCell *lc;
4920
4921 /* At this level, cmdType should not be, eg, CMD_MERGE */
4922 Assert(cmdType == CMD_INSERT ||
4923 cmdType == CMD_UPDATE ||
4924 cmdType == CMD_DELETE);
4925
4926 /* Caller should have ensured query_depth is OK. */
4930
4931 foreach(lc, qs->tables)
4932 {
4934 if (table->relid == relid && table->cmdType == cmdType &&
4935 !table->closed)
4936 return table;
4937 }
4938
4940
4942 table->relid = relid;
4943 table->cmdType = cmdType;
4944 qs->tables = lappend(qs->tables, table);
4945
4947
4948 return table;
4949}
4950
4951/*
4952 * Returns a TupleTableSlot suitable for holding the tuples to be put
4953 * into AfterTriggersTableData's transition table tuplestores.
4954 */
4955static TupleTableSlot *
4957 TupleDesc tupdesc)
4958{
4959 /* Create it if not already done. */
4960 if (!table->storeslot)
4961 {
4963
4964 /*
4965 * We need this slot only until AfterTriggerEndQuery, but making it
4966 * last till end-of-subxact is good enough. It'll be freed by
4967 * AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4968 * a different lifespan, so we'd better make a copy of that.
4969 */
4971 tupdesc = CreateTupleDescCopy(tupdesc);
4972 table->storeslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
4974 }
4975
4976 return table->storeslot;
4977}
4978
4979/*
4980 * MakeTransitionCaptureState
4981 *
4982 * Make a TransitionCaptureState object for the given TriggerDesc, target
4983 * relation, and operation type. The TCS object holds all the state needed
4984 * to decide whether to capture tuples in transition tables.
4985 *
4986 * If there are no triggers in 'trigdesc' that request relevant transition
4987 * tables, then return NULL.
4988 *
4989 * The resulting object can be passed to the ExecAR* functions. When
4990 * dealing with child tables, the caller can set tcs_original_insert_tuple
4991 * to avoid having to reconstruct the original tuple in the root table's
4992 * format.
4993 *
4994 * Note that we copy the flags from a parent table into this struct (rather
4995 * than subsequently using the relation's TriggerDesc directly) so that we can
4996 * use it to control collection of transition tuples from child tables.
4997 *
4998 * Per SQL spec, all operations of the same kind (INSERT/UPDATE/DELETE)
4999 * on the same table during one query should share one transition table.
5000 * Therefore, the Tuplestores are owned by an AfterTriggersTableData struct
5001 * looked up using the table OID + CmdType, and are merely referenced by
5002 * the TransitionCaptureState objects we hand out to callers.
5003 */
5005MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
5006{
5008 bool need_old_upd,
5017
5018 if (trigdesc == NULL)
5019 return NULL;
5020
5021 /* Detect which table(s) we need. */
5022 switch (cmdType)
5023 {
5024 case CMD_INSERT:
5027 break;
5028 case CMD_UPDATE:
5031 need_old_del = need_new_ins = false;
5032 break;
5033 case CMD_DELETE:
5036 break;
5037 case CMD_MERGE:
5042 break;
5043 default:
5044 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
5045 /* keep compiler quiet */
5047 break;
5048 }
5050 return NULL;
5051
5052 /* Check state, like AfterTriggerSaveEvent. */
5053 if (afterTriggers.query_depth < 0)
5054 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
5055
5056 /* Be sure we have enough space to record events at this query depth. */
5059
5060 /*
5061 * Find or create AfterTriggersTableData struct(s) to hold the
5062 * tuplestore(s). If there's a matching struct but it's marked closed,
5063 * ignore it; we need a newer one.
5064 *
5065 * Note: MERGE must use the same AfterTriggersTableData structs as INSERT,
5066 * UPDATE, and DELETE, so that any MERGE'd tuples are added to the same
5067 * tuplestores as tuples from any INSERT, UPDATE, or DELETE commands
5068 * running in the same top-level command (e.g., in a writable CTE).
5069 *
5070 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
5071 * allocated in the current (sub)transaction's CurTransactionContext, and
5072 * the tuplestores are managed by the (sub)transaction's resource owner.
5073 * This is sufficient lifespan because we do not allow triggers using
5074 * transition tables to be deferrable; they will be fired during
5075 * AfterTriggerEndQuery, after which it's okay to delete the data.
5076 */
5077 if (need_new_ins)
5079 else
5080 ins_table = NULL;
5081
5084 else
5085 upd_table = NULL;
5086
5087 if (need_old_del)
5089 else
5090 del_table = NULL;
5091
5092 /* Now create required tuplestore(s), if we don't have them already. */
5096
5097 if (need_old_upd && upd_table->old_tuplestore == NULL)
5098 upd_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5099 if (need_new_upd && upd_table->new_tuplestore == NULL)
5100 upd_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5101 if (need_old_del && del_table->old_tuplestore == NULL)
5102 del_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5103 if (need_new_ins && ins_table->new_tuplestore == NULL)
5104 ins_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5105
5108
5109 /* Now build the TransitionCaptureState struct, in caller's context */
5111 state->tcs_delete_old_table = need_old_del;
5112 state->tcs_update_old_table = need_old_upd;
5113 state->tcs_update_new_table = need_new_upd;
5114 state->tcs_insert_new_table = need_new_ins;
5115 state->tcs_insert_private = ins_table;
5116 state->tcs_update_private = upd_table;
5117 state->tcs_delete_private = del_table;
5118
5119 return state;
5120}
5121
5122
5123/* ----------
5124 * AfterTriggerBeginXact()
5125 *
5126 * Called at transaction start (either BEGIN or implicit for single
5127 * statement outside of transaction block).
5128 * ----------
5129 */
5130void
5132{
5133 /*
5134 * Initialize after-trigger state structure to empty
5135 */
5136 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5141
5142 /*
5143 * Verify that there is no leftover state remaining. If these assertions
5144 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5145 * up properly.
5146 */
5154}
5155
5156
5157/* ----------
5158 * AfterTriggerBeginQuery()
5159 *
5160 * Called just before we start processing a single query within a
5161 * transaction (or subtransaction). Most of the real work gets deferred
5162 * until somebody actually tries to queue a trigger event.
5163 * ----------
5164 */
5165void
5167{
5168 /* Increase the query stack depth */
5170}
5171
5172
5173/* ----------
5174 * AfterTriggerEndQuery()
5175 *
5176 * Called after one query has been completely processed. At this time
5177 * we invoke all AFTER IMMEDIATE trigger events queued by the query, and
5178 * transfer deferred trigger events to the global deferred-trigger list.
5179 *
5180 * Note that this must be called BEFORE closing down the executor
5181 * with ExecutorEnd, because we make use of the EState's info about
5182 * target relations. Normally it is called from ExecutorFinish.
5183 * ----------
5184 */
5185void
5187{
5189
5190 /* Must be inside a query, too */
5192
5193 /*
5194 * If we never even got as far as initializing the event stack, there
5195 * certainly won't be any events, so exit quickly.
5196 */
5198 {
5200 return;
5201 }
5202
5203 /*
5204 * Process all immediate-mode triggers queued by the query, and move the
5205 * deferred ones to the main list of deferred events.
5206 *
5207 * Notice that we decide which ones will be fired, and put the deferred
5208 * ones on the main list, before anything is actually fired. This ensures
5209 * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5210 * IMMEDIATE: all events we have decided to defer will be available for it
5211 * to fire.
5212 *
5213 * We loop in case a trigger queues more events at the same query level.
5214 * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5215 * will instead fire any triggers in a dedicated query level. Foreign key
5216 * enforcement triggers do add to the current query level, thanks to their
5217 * passing fire_triggers = false to SPI_execute_snapshot(). Other
5218 * C-language triggers might do likewise.
5219 *
5220 * If we find no firable events, we don't have to increment
5221 * firing_counter.
5222 */
5224
5226 for (;;)
5227 {
5228 if (afterTriggerMarkEvents(&qs->events, &afterTriggers.events, true))
5229 {
5231 AfterTriggerEventChunk *oldtail = qs->events.tail;
5232
5233 if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5234 break; /* all fired */
5235
5236 /*
5237 * Firing a trigger could result in query_stack being repalloc'd,
5238 * so we must recalculate qs after each afterTriggerInvokeEvents
5239 * call. Furthermore, it's unsafe to pass delete_ok = true here,
5240 * because that could cause afterTriggerInvokeEvents to try to
5241 * access qs->events after the stack has been repalloc'd.
5242 */
5244
5245 /*
5246 * We'll need to scan the events list again. To reduce the cost
5247 * of doing so, get rid of completely-fired chunks. We know that
5248 * all events were marked IN_PROGRESS or DONE at the conclusion of
5249 * afterTriggerMarkEvents, so any still-interesting events must
5250 * have been added after that, and so must be in the chunk that
5251 * was then the tail chunk, or in later chunks. So, zap all
5252 * chunks before oldtail. This is approximately the same set of
5253 * events we would have gotten rid of by passing delete_ok = true.
5254 */
5255 Assert(oldtail != NULL);
5256 while (qs->events.head != oldtail)
5258 }
5259 else
5260 break;
5261 }
5262
5263 /*
5264 * Fire batch callbacks before releasing query-level storage and before
5265 * decrementing query_depth. Callbacks may do real work (index probes,
5266 * error reporting).
5267 *
5268 * Recompute qs first: the loop above refreshes it after each
5269 * afterTriggerInvokeEvents() call (see comment there), but the "all
5270 * fired" break exits without doing so, leaving qs potentially stale here.
5271 */
5273 FireAfterTriggerBatchCallbacks(qs->batch_callbacks);
5274
5275 /* Release query-level-local storage, including tuplestores if any */
5277
5280}
5281
5282
5283/*
5284 * AfterTriggerFreeQuery
5285 * Release subsidiary storage for a trigger query level.
5286 * This includes closing down tuplestores.
5287 * Note: it's important for this to be safe if interrupted by an error
5288 * and then called again for the same query level.
5289 */
5290static void
5292{
5293 Tuplestorestate *ts;
5294 List *tables;
5295 ListCell *lc;
5296
5297 /* Drop the trigger events */
5298 afterTriggerFreeEventList(&qs->events);
5299
5300 /* Drop FDW tuplestore if any */
5301 ts = qs->fdw_tuplestore;
5302 qs->fdw_tuplestore = NULL;
5303 if (ts)
5304 tuplestore_end(ts);
5305
5306 /* Release per-table subsidiary storage */
5307 tables = qs->tables;
5308 foreach(lc, tables)
5309 {
5311
5312 ts = table->old_tuplestore;
5313 table->old_tuplestore = NULL;
5314 if (ts)
5315 tuplestore_end(ts);
5316 ts = table->new_tuplestore;
5317 table->new_tuplestore = NULL;
5318 if (ts)
5319 tuplestore_end(ts);
5320 if (table->storeslot)
5321 {
5322 TupleTableSlot *slot = table->storeslot;
5323
5324 table->storeslot = NULL;
5326 }
5327 }
5328
5329 /*
5330 * Now free the AfterTriggersTableData structs and list cells. Reset list
5331 * pointer first; if list_free_deep somehow gets an error, better to leak
5332 * that storage than have an infinite loop.
5333 */
5334 qs->tables = NIL;
5335 list_free_deep(tables);
5336
5337 list_free_deep(qs->batch_callbacks);
5338 qs->batch_callbacks = NIL;
5339}
5340
5341
5342/* ----------
5343 * AfterTriggerFireDeferred()
5344 *
5345 * Called just before the current transaction is committed. At this
5346 * time we invoke all pending DEFERRED triggers.
5347 *
5348 * It is possible for other modules to queue additional deferred triggers
5349 * during pre-commit processing; therefore xact.c may have to call this
5350 * multiple times.
5351 * ----------
5352 */
5353void
5355{
5356 AfterTriggerEventList *events;
5357 bool snap_pushed = false;
5358
5359 /* Must not be inside a query */
5361
5362 /*
5363 * If there are any triggers to fire, make sure we have set a snapshot for
5364 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5365 * can't assume ActiveSnapshot is valid on entry.)
5366 */
5367 events = &afterTriggers.events;
5368 if (events->head != NULL)
5369 {
5371 snap_pushed = true;
5372 }
5373
5374 /*
5375 * Run all the remaining triggers. Loop until they are all gone, in case
5376 * some trigger queues more for us to do.
5377 */
5379 while (afterTriggerMarkEvents(events, NULL, false))
5380 {
5382
5383 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5384 break; /* all fired */
5385 }
5386
5387 /* Flush any fast-path batches accumulated by the triggers just fired. */
5389
5391
5392 /*
5393 * We don't bother freeing the event list or batch_callbacks, since they
5394 * will go away anyway (and more efficiently than via pfree) in
5395 * AfterTriggerEndXact.
5396 */
5397
5398 if (snap_pushed)
5400}
5401
5402
5403/* ----------
5404 * AfterTriggerEndXact()
5405 *
5406 * The current transaction is finishing.
5407 *
5408 * Any unfired triggers are canceled so we simply throw
5409 * away anything we know.
5410 *
5411 * Note: it is possible for this to be called repeatedly in case of
5412 * error during transaction abort; therefore, do not complain if
5413 * already closed down.
5414 * ----------
5415 */
5416void
5418{
5419 /*
5420 * Forget the pending-events list.
5421 *
5422 * Since all the info is in TopTransactionContext or children thereof, we
5423 * don't really need to do anything to reclaim memory. However, the
5424 * pending-events list could be large, and so it's useful to discard it as
5425 * soon as possible --- especially if we are aborting because we ran out
5426 * of memory for the list!
5427 */
5429 {
5435 }
5436
5437 /*
5438 * Forget any subtransaction state as well. Since this can't be very
5439 * large, we let the eventual reset of TopTransactionContext free the
5440 * memory instead of doing it here.
5441 */
5444
5445
5446 /*
5447 * Forget the query stack and constraint-related state information. As
5448 * with the subtransaction state information, we don't bother freeing the
5449 * memory here.
5450 */
5454
5455 /* No more afterTriggers manipulation until next transaction starts. */
5457
5459
5463}
5464
5465/*
5466 * AfterTriggerBeginSubXact()
5467 *
5468 * Start a subtransaction.
5469 */
5470void
5472{
5473 int my_level = GetCurrentTransactionNestLevel();
5474
5475 /*
5476 * Allocate more space in the trans_stack if needed. (Note: because the
5477 * minimum nest level of a subtransaction is 2, we waste the first couple
5478 * entries of the array; not worth the notational effort to avoid it.)
5479 */
5480 while (my_level >= afterTriggers.maxtransdepth)
5481 {
5483 {
5484 /* Arbitrarily initialize for max of 8 subtransaction levels */
5487 8 * sizeof(AfterTriggersTransData));
5489 }
5490 else
5491 {
5492 /* repalloc will keep the stack in the same context */
5494
5499 }
5500 }
5501
5502 /*
5503 * Push the current information into the stack. The SET CONSTRAINTS state
5504 * is not saved until/unless changed. Likewise, we don't make a
5505 * per-subtransaction event context until needed.
5506 */
5507 afterTriggers.trans_stack[my_level].state = NULL;
5511}
5512
5513/*
5514 * AfterTriggerEndSubXact()
5515 *
5516 * The current subtransaction is ending.
5517 */
5518void
5520{
5521 int my_level = GetCurrentTransactionNestLevel();
5523 AfterTriggerEvent event;
5526
5527 /*
5528 * Pop the prior state if needed.
5529 */
5530 if (isCommit)
5531 {
5533 /* If we saved a prior state, we don't need it anymore */
5535 if (state != NULL)
5536 pfree(state);
5537 /* this avoids double pfree if error later: */
5538 afterTriggers.trans_stack[my_level].state = NULL;
5541 }
5542 else
5543 {
5544 /*
5545 * Aborting. It is possible subxact start failed before calling
5546 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5547 * trans_stack levels that aren't there.
5548 */
5549 if (my_level >= afterTriggers.maxtransdepth)
5550 return;
5551
5552 /*
5553 * Release query-level storage for queries being aborted, and restore
5554 * query_depth to its pre-subxact value. This assumes that a
5555 * subtransaction will not add events to query levels started in a
5556 * earlier transaction state.
5557 */
5559 {
5563 }
5566
5567 /*
5568 * Restore the global deferred-event list to its former length,
5569 * discarding any events queued by the subxact.
5570 */
5572 &afterTriggers.trans_stack[my_level].events);
5573
5574 /*
5575 * Restore the trigger state. If the saved state is NULL, then this
5576 * subxact didn't save it, so it doesn't need restoring.
5577 */
5579 if (state != NULL)
5580 {
5583 }
5584 /* this avoids double pfree if error later: */
5585 afterTriggers.trans_stack[my_level].state = NULL;
5586
5587 /*
5588 * Scan for any remaining deferred events that were marked DONE or IN
5589 * PROGRESS by this subxact or a child, and un-mark them. We can
5590 * recognize such events because they have a firing ID greater than or
5591 * equal to the firing_counter value we saved at subtransaction start.
5592 * (This essentially assumes that the current subxact includes all
5593 * subxacts started after it.)
5594 */
5597 {
5599
5600 if (event->ate_flags &
5602 {
5603 if (evtshared->ats_firing_id >= subxact_firing_id)
5604 event->ate_flags &=
5606 }
5607 }
5608 }
5609
5610 /* Reset in case a callback threw an error while firing. */
5612}
5613
5614/*
5615 * Get the transition table for the given event and depending on whether we are
5616 * processing the old or the new tuple.
5617 */
5618static Tuplestorestate *
5622 TransitionCaptureState *transition_capture)
5623{
5624 Tuplestorestate *tuplestore = NULL;
5625 bool delete_old_table = transition_capture->tcs_delete_old_table;
5626 bool update_old_table = transition_capture->tcs_update_old_table;
5627 bool update_new_table = transition_capture->tcs_update_new_table;
5628 bool insert_new_table = transition_capture->tcs_insert_new_table;
5629
5630 /*
5631 * For INSERT events NEW should be non-NULL, for DELETE events OLD should
5632 * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
5633 * non-NULL. But for UPDATE events fired for capturing transition tuples
5634 * during UPDATE partition-key row movement, OLD is NULL when the event is
5635 * for a row being inserted, whereas NEW is NULL when the event is for a
5636 * row being deleted.
5637 */
5639 TupIsNull(oldslot)));
5641 TupIsNull(newslot)));
5642
5643 if (!TupIsNull(oldslot))
5644 {
5646 if (event == TRIGGER_EVENT_DELETE && delete_old_table)
5647 tuplestore = transition_capture->tcs_delete_private->old_tuplestore;
5648 else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
5649 tuplestore = transition_capture->tcs_update_private->old_tuplestore;
5650 }
5651 else if (!TupIsNull(newslot))
5652 {
5654 if (event == TRIGGER_EVENT_INSERT && insert_new_table)
5655 tuplestore = transition_capture->tcs_insert_private->new_tuplestore;
5656 else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
5657 tuplestore = transition_capture->tcs_update_private->new_tuplestore;
5658 }
5659
5660 return tuplestore;
5661}
5662
5663/*
5664 * Add the given heap tuple to the given tuplestore, applying the conversion
5665 * map if necessary.
5666 *
5667 * If original_insert_tuple is given, we can add that tuple without conversion.
5668 */
5669static void
5671 int event,
5672 TransitionCaptureState *transition_capture,
5674 TupleTableSlot *slot,
5676 Tuplestorestate *tuplestore)
5677{
5678 TupleConversionMap *map;
5679
5680 /*
5681 * Nothing needs to be done if we don't have a tuplestore.
5682 */
5683 if (tuplestore == NULL)
5684 return;
5685
5688 else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5689 {
5691 TupleTableSlot *storeslot;
5692
5693 switch (event)
5694 {
5696 table = transition_capture->tcs_insert_private;
5697 break;
5699 table = transition_capture->tcs_update_private;
5700 break;
5702 table = transition_capture->tcs_delete_private;
5703 break;
5704 default:
5705 elog(ERROR, "invalid after-trigger event code: %d", event);
5706 table = NULL; /* keep compiler quiet */
5707 break;
5708 }
5709
5710 storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5711 execute_attr_map_slot(map->attrMap, slot, storeslot);
5712 tuplestore_puttupleslot(tuplestore, storeslot);
5713 }
5714 else
5715 tuplestore_puttupleslot(tuplestore, slot);
5716}
5717
5718/* ----------
5719 * AfterTriggerEnlargeQueryState()
5720 *
5721 * Prepare the necessary state so that we can record AFTER trigger events
5722 * queued by a query. It is allowed to have nested queries within a
5723 * (sub)transaction, so we need to have separate state for each query
5724 * nesting level.
5725 * ----------
5726 */
5727static void
5729{
5731
5733
5735 {
5736 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5737
5742 }
5743 else
5744 {
5745 /* repalloc will keep the stack in the same context */
5748 old_alloc * 2);
5749
5754 }
5755
5756 /* Initialize new array entries to empty */
5758 {
5760
5761 qs->events.head = NULL;
5762 qs->events.tail = NULL;
5763 qs->events.tailfree = NULL;
5764 qs->fdw_tuplestore = NULL;
5765 qs->tables = NIL;
5766 qs->batch_callbacks = NIL;
5767
5768 ++init_depth;
5769 }
5770}
5771
5772/*
5773 * Create an empty SetConstraintState with room for numalloc trigstates
5774 */
5775static SetConstraintState
5776SetConstraintStateCreate(int numalloc)
5777{
5779
5780 /* Behave sanely with numalloc == 0 */
5781 if (numalloc <= 0)
5782 numalloc = 1;
5783
5784 /*
5785 * We assume that zeroing will correctly initialize the state values.
5786 */
5789 offsetof(SetConstraintStateData, trigstates) +
5790 numalloc * sizeof(SetConstraintTriggerData));
5791
5792 state->numalloc = numalloc;
5793
5794 return state;
5795}
5796
5797/*
5798 * Copy a SetConstraintState
5799 */
5800static SetConstraintState
5802{
5804
5806
5807 state->all_isset = origstate->all_isset;
5808 state->all_isdeferred = origstate->all_isdeferred;
5809 state->numstates = origstate->numstates;
5810 memcpy(state->trigstates, origstate->trigstates,
5811 origstate->numstates * sizeof(SetConstraintTriggerData));
5812
5813 return state;
5814}
5815
5816/*
5817 * Add a per-trigger item to a SetConstraintState. Returns possibly-changed
5818 * pointer to the state object (it will change if we have to repalloc).
5819 */
5820static SetConstraintState
5822 Oid tgoid, bool tgisdeferred)
5823{
5824 if (state->numstates >= state->numalloc)
5825 {
5826 int newalloc = state->numalloc * 2;
5827
5828 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5831 offsetof(SetConstraintStateData, trigstates) +
5833 state->numalloc = newalloc;
5834 Assert(state->numstates < state->numalloc);
5835 }
5836
5837 state->trigstates[state->numstates].sct_tgoid = tgoid;
5838 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5839 state->numstates++;
5840
5841 return state;
5842}
5843
5844/* ----------
5845 * AfterTriggerSetState()
5846 *
5847 * Execute the SET CONSTRAINTS ... utility command.
5848 * ----------
5849 */
5850void
5852{
5853 int my_level = GetCurrentTransactionNestLevel();
5854
5855 /* If we haven't already done so, initialize our state. */
5856 if (afterTriggers.state == NULL)
5858
5859 /*
5860 * If in a subtransaction, and we didn't save the current state already,
5861 * save it so it can be restored if the subtransaction aborts.
5862 */
5863 if (my_level > 1 &&
5864 afterTriggers.trans_stack[my_level].state == NULL)
5865 {
5866 afterTriggers.trans_stack[my_level].state =
5868 }
5869
5870 /*
5871 * Handle SET CONSTRAINTS ALL ...
5872 */
5873 if (stmt->constraints == NIL)
5874 {
5875 /*
5876 * Forget any previous SET CONSTRAINTS commands in this transaction.
5877 */
5879
5880 /*
5881 * Set the per-transaction ALL state to known.
5882 */
5885 }
5886 else
5887 {
5890 List *conoidlist = NIL;
5891 List *tgoidlist = NIL;
5892 ListCell *lc;
5893
5894 /*
5895 * Handle SET CONSTRAINTS constraint-name [, ...]
5896 *
5897 * First, identify all the named constraints and make a list of their
5898 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5899 * the same name within a schema, the specifications are not
5900 * necessarily unique. Our strategy is to target all matching
5901 * constraints within the first search-path schema that has any
5902 * matches, but disregard matches in schemas beyond the first match.
5903 * (This is a bit odd but it's the historical behavior.)
5904 *
5905 * A constraint in a partitioned table may have corresponding
5906 * constraints in the partitions. Grab those too.
5907 */
5909
5910 foreach(lc, stmt->constraints)
5911 {
5912 RangeVar *constraint = lfirst(lc);
5913 bool found;
5915 ListCell *nslc;
5916
5917 if (constraint->catalogname)
5918 {
5919 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5920 ereport(ERROR,
5922 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5923 constraint->catalogname, constraint->schemaname,
5924 constraint->relname)));
5925 }
5926
5927 /*
5928 * If we're given the schema name with the constraint, look only
5929 * in that schema. If given a bare constraint name, use the
5930 * search path to find the first matching constraint.
5931 */
5932 if (constraint->schemaname)
5933 {
5935 false);
5936
5938 }
5939 else
5940 {
5942 }
5943
5944 found = false;
5945 foreach(nslc, namespacelist)
5946 {
5949 ScanKeyData skey[2];
5950 HeapTuple tup;
5951
5952 ScanKeyInit(&skey[0],
5955 CStringGetDatum(constraint->relname));
5956 ScanKeyInit(&skey[1],
5960
5962 true, NULL, 2, skey);
5963
5965 {
5967
5968 if (con->condeferrable)
5969 conoidlist = lappend_oid(conoidlist, con->oid);
5970 else if (stmt->deferred)
5971 ereport(ERROR,
5973 errmsg("constraint \"%s\" is not deferrable",
5974 constraint->relname)));
5975 found = true;
5976 }
5977
5979
5980 /*
5981 * Once we've found a matching constraint we do not search
5982 * later parts of the search path.
5983 */
5984 if (found)
5985 break;
5986 }
5987
5989
5990 /*
5991 * Not found ?
5992 */
5993 if (!found)
5994 ereport(ERROR,
5996 errmsg("constraint \"%s\" does not exist",
5997 constraint->relname)));
5998 }
5999
6000 /*
6001 * Scan for any possible descendants of the constraints. We append
6002 * whatever we find to the same list that we're scanning; this has the
6003 * effect that we create new scans for those, too, so if there are
6004 * further descendents, we'll also catch them.
6005 */
6006 foreach(lc, conoidlist)
6007 {
6008 Oid parent = lfirst_oid(lc);
6010 SysScanDesc scan;
6011 HeapTuple tuple;
6012
6013 ScanKeyInit(&key,
6016 ObjectIdGetDatum(parent));
6017
6018 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
6019
6020 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
6021 {
6023
6024 conoidlist = lappend_oid(conoidlist, con->oid);
6025 }
6026
6027 systable_endscan(scan);
6028 }
6029
6031
6032 /*
6033 * Now, locate the trigger(s) implementing each of these constraints,
6034 * and make a list of their OIDs.
6035 */
6037
6038 foreach(lc, conoidlist)
6039 {
6040 Oid conoid = lfirst_oid(lc);
6043 HeapTuple htup;
6044
6048 ObjectIdGetDatum(conoid));
6049
6051 NULL, 1, &skey);
6052
6054 {
6056
6057 /*
6058 * Silently skip triggers that are marked as non-deferrable in
6059 * pg_trigger. This is not an error condition, since a
6060 * deferrable RI constraint may have some non-deferrable
6061 * actions.
6062 */
6063 if (pg_trigger->tgdeferrable)
6065 }
6066
6068 }
6069
6071
6072 /*
6073 * Now we can set the trigger states of individual triggers for this
6074 * xact.
6075 */
6076 foreach(lc, tgoidlist)
6077 {
6078 Oid tgoid = lfirst_oid(lc);
6080 bool found = false;
6081 int i;
6082
6083 for (i = 0; i < state->numstates; i++)
6084 {
6085 if (state->trigstates[i].sct_tgoid == tgoid)
6086 {
6087 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
6088 found = true;
6089 break;
6090 }
6091 }
6092 if (!found)
6093 {
6095 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
6096 }
6097 }
6098 }
6099
6100 /*
6101 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
6102 * checks against that constraint must be made when the SET CONSTRAINTS
6103 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
6104 * apply retroactively. We've updated the constraints state, so scan the
6105 * list of previously deferred events to fire any that have now become
6106 * immediate.
6107 *
6108 * Obviously, if this was SET ... DEFERRED then it can't have converted
6109 * any unfired events to immediate, so we need do nothing in that case.
6110 */
6111 if (!stmt->deferred)
6112 {
6114 bool snapshot_set = false;
6115
6117 while (afterTriggerMarkEvents(events, NULL, true))
6118 {
6120
6121 /*
6122 * Make sure a snapshot has been established in case trigger
6123 * functions need one. Note that we avoid setting a snapshot if
6124 * we don't find at least one trigger that has to be fired now.
6125 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6126 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6127 * at the start of a transaction it's not possible for any trigger
6128 * events to be queued yet.)
6129 */
6130 if (!snapshot_set)
6131 {
6133 snapshot_set = true;
6134 }
6135
6136 /*
6137 * We can delete fired events if we are at top transaction level,
6138 * but we'd better not if inside a subtransaction, since the
6139 * subtransaction could later get rolled back.
6140 */
6142 !IsSubTransaction()))
6143 break; /* all fired */
6144 }
6145
6146 /*
6147 * Flush any fast-path batches accumulated by the triggers just fired.
6148 */
6153
6154 if (snapshot_set)
6156 }
6157}
6158
6159/* ----------
6160 * AfterTriggerPendingOnRel()
6161 * Test to see if there are any pending after-trigger events for rel.
6162 *
6163 * This is used by TRUNCATE, CLUSTER, ALTER TABLE, etc to detect whether
6164 * it is unsafe to perform major surgery on a relation. Note that only
6165 * local pending events are examined. We assume that having exclusive lock
6166 * on a rel guarantees there are no unserviced events in other backends ---
6167 * but having a lock does not prevent there being such events in our own.
6168 *
6169 * In some scenarios it'd be reasonable to remove pending events (more
6170 * specifically, mark them DONE by the current subxact) but without a lot
6171 * of knowledge of the trigger semantics we can't do this in general.
6172 * ----------
6173 */
6174bool
6176{
6177 AfterTriggerEvent event;
6179 int depth;
6180
6181 /* Scan queued events */
6183 {
6185
6186 /*
6187 * We can ignore completed events. (Even if a DONE flag is rolled
6188 * back by subxact abort, it's OK because the effects of the TRUNCATE
6189 * or whatever must get rolled back too.)
6190 */
6191 if (event->ate_flags & AFTER_TRIGGER_DONE)
6192 continue;
6193
6194 if (evtshared->ats_relid == relid)
6195 return true;
6196 }
6197
6198 /*
6199 * Also scan events queued by incomplete queries. This could only matter
6200 * if TRUNCATE/etc is executed by a function or trigger within an updating
6201 * query on the same relation, which is pretty perverse, but let's check.
6202 */
6203 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6204 {
6206 {
6208
6209 if (event->ate_flags & AFTER_TRIGGER_DONE)
6210 continue;
6211
6212 if (evtshared->ats_relid == relid)
6213 return true;
6214 }
6215 }
6216
6217 return false;
6218}
6219
6220/* ----------
6221 * AfterTriggerSaveEvent()
6222 *
6223 * Called by ExecA[RS]...Triggers() to queue up the triggers that should
6224 * be fired for an event.
6225 *
6226 * NOTE: this is called whenever there are any triggers associated with
6227 * the event (even if they are disabled). This function decides which
6228 * triggers actually need to be queued. It is also called after each row,
6229 * even if there are no triggers for that event, if there are any AFTER
6230 * STATEMENT triggers for the statement which use transition tables, so that
6231 * the transition tuplestores can be built. Furthermore, if the transition
6232 * capture is happening for UPDATEd rows being moved to another partition due
6233 * to the partition-key being changed, then this function is called once when
6234 * the row is deleted (to capture OLD row), and once when the row is inserted
6235 * into another partition (to capture NEW row). This is done separately because
6236 * DELETE and INSERT happen on different tables.
6237 *
6238 * Transition tuplestores are built now, rather than when events are pulled
6239 * off of the queue because AFTER ROW triggers are allowed to select from the
6240 * transition tables for the statement.
6241 *
6242 * This contains special support to queue the update events for the case where
6243 * a partitioned table undergoing a cross-partition update may have foreign
6244 * keys pointing into it. Normally, a partitioned table's row triggers are
6245 * not fired because the leaf partition(s) which are modified as a result of
6246 * the operation on the partitioned table contain the same triggers which are
6247 * fired instead. But that general scheme can cause problematic behavior with
6248 * foreign key triggers during cross-partition updates, which are implemented
6249 * as DELETE on the source partition followed by INSERT into the destination
6250 * partition. Specifically, firing DELETE triggers would lead to the wrong
6251 * foreign key action to be enforced considering that the original command is
6252 * UPDATE; in this case, this function is called with relinfo as the
6253 * partitioned table, and src_partinfo and dst_partinfo referring to the
6254 * source and target leaf partitions, respectively.
6255 *
6256 * is_crosspart_update is true either when a DELETE event is fired on the
6257 * source partition (which is to be ignored) or an UPDATE event is fired on
6258 * the root partitioned table.
6259 * ----------
6260 */
6261static void
6265 int event, bool row_trigger,
6268 TransitionCaptureState *transition_capture,
6270{
6271 Relation rel = relinfo->ri_RelationDesc;
6272 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
6275 char relkind = rel->rd_rel->relkind;
6276 int tgtype_event;
6277 int tgtype_level;
6278 int i;
6279 Tuplestorestate *fdw_tuplestore = NULL;
6280
6281 /*
6282 * Check state. We use a normal test not Assert because it is possible to
6283 * reach here in the wrong state given misconfigured RI triggers, in
6284 * particular deferring a cascade action trigger.
6285 */
6286 if (afterTriggers.query_depth < 0)
6287 elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
6288
6289 /* Be sure we have enough space to record events at this query depth. */
6292
6293 /*
6294 * If the directly named relation has any triggers with transition tables,
6295 * then we need to capture transition tuples.
6296 */
6297 if (row_trigger && transition_capture != NULL)
6298 {
6300
6301 /*
6302 * Capture the old tuple in the appropriate transition table based on
6303 * the event.
6304 */
6305 if (!TupIsNull(oldslot))
6306 {
6307 Tuplestorestate *old_tuplestore;
6308
6309 old_tuplestore = GetAfterTriggersTransitionTable(event,
6310 oldslot,
6311 NULL,
6312 transition_capture);
6313 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6314 oldslot, NULL, old_tuplestore);
6315 }
6316
6317 /*
6318 * Capture the new tuple in the appropriate transition table based on
6319 * the event.
6320 */
6321 if (!TupIsNull(newslot))
6322 {
6323 Tuplestorestate *new_tuplestore;
6324
6325 new_tuplestore = GetAfterTriggersTransitionTable(event,
6326 NULL,
6327 newslot,
6328 transition_capture);
6329 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6330 newslot, original_insert_tuple, new_tuplestore);
6331 }
6332
6333 /*
6334 * If transition tables are the only reason we're here, return. As
6335 * mentioned above, we can also be here during update tuple routing in
6336 * presence of transition tables, in which case this function is
6337 * called separately for OLD and NEW, so we expect exactly one of them
6338 * to be NULL.
6339 */
6340 if (trigdesc == NULL ||
6341 (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
6342 (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
6343 (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
6345 return;
6346 }
6347
6348 /*
6349 * We normally don't see partitioned tables here for row level triggers
6350 * except in the special case of a cross-partition update. In that case,
6351 * nodeModifyTable.c:ExecCrossPartitionUpdateForeignKey() calls here to
6352 * queue an update event on the root target partitioned table, also
6353 * passing the source and destination partitions and their tuples.
6354 */
6356 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
6358 TRIGGER_FIRED_BY_UPDATE(event) &&
6360
6361 /*
6362 * Validate the event code and collect the associated tuple CTIDs.
6363 *
6364 * The event code will be used both as a bitmask and an array offset, so
6365 * validation is important to make sure we don't walk off the edge of our
6366 * arrays.
6367 *
6368 * Also, if we're considering statement-level triggers, check whether we
6369 * already queued a set of them for this event, and cancel the prior set
6370 * if so. This preserves the behavior that statement-level triggers fire
6371 * just once per statement and fire after row-level triggers.
6372 */
6373 switch (event)
6374 {
6377 if (row_trigger)
6378 {
6379 Assert(oldslot == NULL);
6380 Assert(newslot != NULL);
6381 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
6382 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6383 }
6384 else
6385 {
6386 Assert(oldslot == NULL);
6387 Assert(newslot == NULL);
6388 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6389 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6391 CMD_INSERT, event);
6392 }
6393 break;
6396 if (row_trigger)
6397 {
6398 Assert(oldslot != NULL);
6399 Assert(newslot == NULL);
6400 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6401 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6402 }
6403 else
6404 {
6405 Assert(oldslot == NULL);
6406 Assert(newslot == NULL);
6407 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6408 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6410 CMD_DELETE, event);
6411 }
6412 break;
6415 if (row_trigger)
6416 {
6417 Assert(oldslot != NULL);
6418 Assert(newslot != NULL);
6419 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6420 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
6421
6422 /*
6423 * Also remember the OIDs of partitions to fetch these tuples
6424 * out of later in AfterTriggerExecute().
6425 */
6426 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6427 {
6429 new_event.ate_src_part =
6430 RelationGetRelid(src_partinfo->ri_RelationDesc);
6431 new_event.ate_dst_part =
6432 RelationGetRelid(dst_partinfo->ri_RelationDesc);
6433 }
6434 }
6435 else
6436 {
6437 Assert(oldslot == NULL);
6438 Assert(newslot == NULL);
6439 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6440 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6442 CMD_UPDATE, event);
6443 }
6444 break;
6447 Assert(oldslot == NULL);
6448 Assert(newslot == NULL);
6449 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6450 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6451 break;
6452 default:
6453 elog(ERROR, "invalid after-trigger event code: %d", event);
6454 tgtype_event = 0; /* keep compiler quiet */
6455 break;
6456 }
6457
6458 /* Determine flags */
6459 if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
6460 {
6461 if (row_trigger && event == TRIGGER_EVENT_UPDATE)
6462 {
6463 if (relkind == RELKIND_PARTITIONED_TABLE)
6465 else
6466 new_event.ate_flags = AFTER_TRIGGER_2CTID;
6467 }
6468 else
6469 new_event.ate_flags = AFTER_TRIGGER_1CTID;
6470 }
6471
6472 /* else, we'll initialize ate_flags for each trigger */
6473
6475
6476 /*
6477 * Must convert/copy the source and destination partition tuples into the
6478 * root partitioned table's format/slot, because the processing in the
6479 * loop below expects both oldslot and newslot tuples to be in that form.
6480 */
6481 if (row_trigger && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6482 {
6484 TupleConversionMap *map;
6485
6488 if (map)
6490 oldslot,
6491 rootslot);
6492 else
6494
6497 if (map)
6499 newslot,
6500 rootslot);
6501 else
6503 }
6504
6505 for (i = 0; i < trigdesc->numtriggers; i++)
6506 {
6507 Trigger *trigger = &trigdesc->triggers[i];
6508
6509 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
6512 tgtype_event))
6513 continue;
6514 if (!TriggerEnabled(estate, relinfo, trigger, event,
6516 continue;
6517
6518 if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
6519 {
6520 if (fdw_tuplestore == NULL)
6521 {
6522 fdw_tuplestore = GetCurrentFDWTuplestore();
6524 }
6525 else
6526 /* subsequent event for the same tuple */
6528 }
6529
6530 /*
6531 * If the trigger is a foreign key enforcement trigger, there are
6532 * certain cases where we can skip queueing the event because we can
6533 * tell by inspection that the FK constraint will still pass. There
6534 * are also some cases during cross-partition updates of a partitioned
6535 * table where queuing the event can be skipped.
6536 */
6538 {
6539 switch (RI_FKey_trigger_type(trigger->tgfoid))
6540 {
6541 case RI_TRIGGER_PK:
6542
6543 /*
6544 * For cross-partitioned updates of partitioned PK table,
6545 * skip the event fired by the component delete on the
6546 * source leaf partition unless the constraint originates
6547 * in the partition itself (!tgisclone), because the
6548 * update event that will be fired on the root
6549 * (partitioned) target table will be used to perform the
6550 * necessary foreign key enforcement action.
6551 */
6552 if (is_crosspart_update &&
6553 TRIGGER_FIRED_BY_DELETE(event) &&
6554 trigger->tgisclone)
6555 continue;
6556
6557 /* Update or delete on trigger's PK table */
6559 oldslot, newslot))
6560 {
6561 /* skip queuing this event */
6562 continue;
6563 }
6564 break;
6565
6566 case RI_TRIGGER_FK:
6567
6568 /*
6569 * Update on trigger's FK table. We can skip the update
6570 * event fired on a partitioned table during a
6571 * cross-partition of that table, because the insert event
6572 * that is fired on the destination leaf partition would
6573 * suffice to perform the necessary foreign key check.
6574 * Moreover, RI_FKey_fk_upd_check_required() expects to be
6575 * passed a tuple that contains system attributes, most of
6576 * which are not present in the virtual slot belonging to
6577 * a partitioned table.
6578 */
6579 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
6581 oldslot, newslot))
6582 {
6583 /* skip queuing this event */
6584 continue;
6585 }
6586 break;
6587
6588 case RI_TRIGGER_NONE:
6589
6590 /*
6591 * Not an FK trigger. No need to queue the update event
6592 * fired during a cross-partitioned update of a
6593 * partitioned table, because the same row trigger must be
6594 * present in the leaf partition(s) that are affected as
6595 * part of this update and the events fired on them are
6596 * queued instead.
6597 */
6598 if (row_trigger &&
6599 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6600 continue;
6601 break;
6602 }
6603 }
6604
6605 /*
6606 * If the trigger is a deferred unique constraint check trigger, only
6607 * queue it if the unique constraint was potentially violated, which
6608 * we know from index insertion time.
6609 */
6610 if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
6611 {
6612 if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
6613 continue; /* Uniqueness definitely not violated */
6614 }
6615
6616 /*
6617 * Fill in event structure and add it to the current query's queue.
6618 * Note we set ats_table to NULL whenever this trigger doesn't use
6619 * transition tables, to improve sharability of the shared event data.
6620 */
6621 new_shared.ats_event =
6622 (event & TRIGGER_EVENT_OPMASK) |
6624 (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
6625 (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
6626 new_shared.ats_tgoid = trigger->tgoid;
6627 new_shared.ats_relid = RelationGetRelid(rel);
6628 new_shared.ats_rolid = GetUserId();
6629 new_shared.ats_firing_id = 0;
6630 if ((trigger->tgoldtable || trigger->tgnewtable) &&
6631 transition_capture != NULL)
6632 {
6633 switch (event)
6634 {
6636 new_shared.ats_table = transition_capture->tcs_insert_private;
6637 break;
6639 new_shared.ats_table = transition_capture->tcs_update_private;
6640 break;
6642 new_shared.ats_table = transition_capture->tcs_delete_private;
6643 break;
6644 default:
6645 /* Must be TRUNCATE, see switch above */
6646 new_shared.ats_table = NULL;
6647 break;
6648 }
6649 }
6650 else
6651 new_shared.ats_table = NULL;
6652 new_shared.ats_modifiedcols = modifiedCols;
6653
6656 }
6657
6658 /*
6659 * Finally, spool any foreign tuple(s). The tuplestore squashes them to
6660 * minimal tuples, so this loses any system columns. The executor lost
6661 * those columns before us, for an unrelated reason, so this is fine.
6662 */
6663 if (fdw_tuplestore)
6664 {
6665 if (oldslot != NULL)
6666 tuplestore_puttupleslot(fdw_tuplestore, oldslot);
6667 if (newslot != NULL)
6668 tuplestore_puttupleslot(fdw_tuplestore, newslot);
6669 }
6670}
6671
6672/*
6673 * Detect whether we already queued BEFORE STATEMENT triggers for the given
6674 * relation + operation, and set the flag so the next call will report "true".
6675 */
6676static bool
6678{
6679 bool result;
6681
6682 /* Check state, like AfterTriggerSaveEvent. */
6683 if (afterTriggers.query_depth < 0)
6684 elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6685
6686 /* Be sure we have enough space to record events at this query depth. */
6689
6690 /*
6691 * We keep this state in the AfterTriggersTableData that also holds
6692 * transition tables for the relation + operation. In this way, if we are
6693 * forced to make a new set of transition tables because more tuples get
6694 * entered after we've already fired triggers, we will allow a new set of
6695 * statement triggers to get queued.
6696 */
6697 table = GetAfterTriggersTableData(relid, cmdType);
6698 result = table->before_trig_done;
6699 table->before_trig_done = true;
6700 return result;
6701}
6702
6703/*
6704 * If we previously queued a set of AFTER STATEMENT triggers for the given
6705 * relation + operation, and they've not been fired yet, cancel them. The
6706 * caller will queue a fresh set that's after any row-level triggers that may
6707 * have been queued by the current sub-statement, preserving (as much as
6708 * possible) the property that AFTER ROW triggers fire before AFTER STATEMENT
6709 * triggers, and that the latter only fire once. This deals with the
6710 * situation where several FK enforcement triggers sequentially queue triggers
6711 * for the same table into the same trigger query level. We can't fully
6712 * prevent odd behavior though: if there are AFTER ROW triggers taking
6713 * transition tables, we don't want to change the transition tables once the
6714 * first such trigger has seen them. In such a case, any additional events
6715 * will result in creating new transition tables and allowing new firings of
6716 * statement triggers.
6717 *
6718 * This also saves the current event list location so that a later invocation
6719 * of this function can cheaply find the triggers we're about to queue and
6720 * cancel them.
6721 */
6722static void
6723cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
6724{
6727
6728 /*
6729 * We keep this state in the AfterTriggersTableData that also holds
6730 * transition tables for the relation + operation. In this way, if we are
6731 * forced to make a new set of transition tables because more tuples get
6732 * entered after we've already fired triggers, we will allow a new set of
6733 * statement triggers to get queued without canceling the old ones.
6734 */
6735 table = GetAfterTriggersTableData(relid, cmdType);
6736
6737 if (table->after_trig_done)
6738 {
6739 /*
6740 * We want to start scanning from the tail location that existed just
6741 * before we inserted any statement triggers. But the events list
6742 * might've been entirely empty then, in which case scan from the
6743 * current head.
6744 */
6745 AfterTriggerEvent event;
6747
6748 if (table->after_trig_events.tail)
6749 {
6750 chunk = table->after_trig_events.tail;
6751 event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6752 }
6753 else
6754 {
6755 chunk = qs->events.head;
6756 event = NULL;
6757 }
6758
6759 for_each_chunk_from(chunk)
6760 {
6761 if (event == NULL)
6762 event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6763 for_each_event_from(event, chunk)
6764 {
6766
6767 /*
6768 * Exit loop when we reach events that aren't AS triggers for
6769 * the target relation.
6770 */
6771 if (evtshared->ats_relid != relid)
6772 goto done;
6773 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6774 goto done;
6775 if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6776 goto done;
6777 if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6778 goto done;
6779 /* OK, mark it DONE */
6780 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6781 event->ate_flags |= AFTER_TRIGGER_DONE;
6782 }
6783 /* signal we must reinitialize event ptr for next chunk */
6784 event = NULL;
6785 }
6786 }
6787done:
6788
6789 /* In any case, save current insertion point for next time */
6790 table->after_trig_done = true;
6791 table->after_trig_events = qs->events;
6792}
6793
6794/*
6795 * GUC assign_hook for session_replication_role
6796 */
6797void
6799{
6800 /*
6801 * Must flush the plan cache when changing replication role; but don't
6802 * flush unnecessarily.
6803 */
6806}
6807
6808/*
6809 * SQL function pg_trigger_depth()
6810 */
6811Datum
6813{
6815}
6816
6817/*
6818 * Check whether a trigger modified a virtual generated column and replace the
6819 * value with null if so.
6820 *
6821 * We need to check this so that we don't end up storing a non-null value in a
6822 * virtual generated column.
6823 *
6824 * We don't need to check for stored generated columns, since those will be
6825 * overwritten later anyway.
6826 */
6827static HeapTuple
6829{
6830 if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
6831 return tuple;
6832
6833 for (int i = 0; i < tupdesc->natts; i++)
6834 {
6835 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6836 {
6837 if (!heap_attisnull(tuple, i + 1, tupdesc))
6838 {
6839 int replCol = i + 1;
6840 Datum replValue = 0;
6841 bool replIsnull = true;
6842
6843 tuple = heap_modify_tuple_by_cols(tuple, tupdesc, 1, &replCol, &replValue, &replIsnull);
6844 }
6845 }
6846 }
6847
6848 return tuple;
6849}
6850
6851/*
6852 * RegisterAfterTriggerBatchCallback
6853 * Register a function to be called when the current trigger-firing
6854 * batch completes.
6855 *
6856 * Must be called from within a trigger function's execution context
6857 * (i.e., while afterTriggers state is active).
6858 *
6859 * The callback list is cleared after invocation, so the caller must
6860 * re-register for each new batch if needed.
6861 */
6862void
6864 void *arg)
6865{
6868
6869 /*
6870 * Allocate in TopTransactionContext so the item survives for the duration
6871 * of the batch, which may span multiple trigger invocations.
6872 *
6873 * Must be called while afterTriggers is active; callbacks registered
6874 * outside a trigger-firing context would never fire.
6875 */
6879 item = palloc(sizeof(AfterTriggerCallbackItem));
6880 item->callback = callback;
6881 item->arg = arg;
6882 if (afterTriggers.query_depth >= 0)
6883 {
6886
6887 qs->batch_callbacks = lappend(qs->batch_callbacks, item);
6888 }
6889 else
6893}
6894
6895/*
6896 * FireAfterTriggerBatchCallbacks
6897 * Invoke all callbacks in the given list.
6898 *
6899 * Memory cleanup of the list and its items is handled by the caller
6900 * (AfterTriggerFreeQuery for query-level callbacks, AfterTriggerEndXact
6901 * for top-level deferred callbacks).
6902 */
6903static void
6905{
6906 ListCell *lc;
6907
6910 foreach(lc, callbacks)
6911 {
6913
6914 item->callback(item->arg);
6915 }
6917}
6918
6919/*
6920 * AfterTriggerIsActive
6921 * Returns true if we're inside the after-trigger framework where
6922 * registered batch callbacks will actually be invoked.
6923 *
6924 * This is false during validateForeignKeyConstraint(), which calls
6925 * RI trigger functions directly outside the after-trigger framework.
6926 */
6927bool
6929{
6930 return afterTriggers.firing_depth > 0;
6931}
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:143
Bitmapset * bms_copy(const Bitmapset *a)
Definition bitmapset.c:123
#define Min(x, y)
Definition c.h:1131
#define Max(x, y)
Definition c.h:1125
#define Assert(condition)
Definition c.h:1002
#define pg_fallthrough
Definition c.h:220
uint32 CommandId
Definition c.h:809
#define OidIsValid(objectId)
Definition c.h:917
size_t Size
Definition c.h:748
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
Datum arg
Definition elog.c:1323
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid, ResultRelInfo *rootRelInfo)
Definition execMain.c:1409
void ExecCloseResultRelations(EState *estate)
Definition execMain.c:1641
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
const TupleTableSlotOps TTSOpsMinimalTuple
Definition execTuples.c:86
TupleTableSlot * ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
Definition execUtils.c:1252
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition execUtils.c:1326
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition execUtils.c:1230
void FreeExecutorState(EState *estate)
Definition execUtils.c:197
EState * CreateExecutorState(void)
Definition execUtils.c:90
#define palloc0_object(type)
Definition fe_memutils.h:90
#define PG_RETURN_INT32(x)
Definition fmgr.h:355
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
int work_mem
Definition globals.c:133
Oid MyDatabaseId
Definition globals.c:96
#define newval
HeapTuple heap_modify_tuple_by_cols(HeapTuple tuple, TupleDesc tupleDesc, int nCols, const int *replCols, const Datum *replValues, const bool *replIsnull)
Definition heaptuple.c:1186
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition heaptuple.c:456
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
#define stmt
void InstrStartTrigger(TriggerInstrumentation *tginstr)
Definition instrument.c:265
void InstrStopTrigger(TriggerInstrumentation *tginstr, int64 firings)
Definition instrument.c:271
int i
Definition isn.c:77
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition itemptr.h:184
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition itemptr.h:172
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition itemptr.h:83
List * lappend(List *list, void *datum)
Definition list.c:339
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
void list_free(List *list)
Definition list.c:1546
bool list_member_oid(const List *list, Oid datum)
Definition list.c:722
void list_free_deep(List *list)
Definition list.c:1560
#define AccessShareLock
Definition lockdefs.h:36
char * get_database_name(Oid dbid)
Definition lsyscache.c:1392
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1235
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1269
MemoryContext TopTransactionContext
Definition mcxt.c:172
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1635
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc(Size size)
Definition mcxt.c:1390
MemoryContext CurTransactionContext
Definition mcxt.c:173
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define SECURITY_LOCAL_USERID_CHANGE
Definition miscadmin.h:321
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:613
bool InSecurityRestrictedOperation(void)
Definition miscinit.c:640
Oid GetUserId(void)
Definition miscinit.c:470
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:620
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition namespace.c:3457
List * fetch_search_path(bool includeImplicit)
Definition namespace.c:4891
CmdType
Definition nodes.h:271
@ CMD_MERGE
Definition nodes.h:277
@ CMD_INSERT
Definition nodes.h:275
@ CMD_DELETE
Definition nodes.h:276
@ CMD_UPDATE
Definition nodes.h:274
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
#define list_make1_oid(x1)
Definition pg_list.h:274
#define lfirst_oid(lc)
Definition pg_list.h:174
static const struct lconv_member_info table[]
END_CATALOG_STRUCT typedef FormData_pg_trigger * Form_pg_trigger
Definition pg_trigger.h:84
void ResetPlanCache(void)
Definition plancache.c:2328
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
unsigned int Oid
static int fb(int x)
#define RelationHasReferenceCountZero(relation)
Definition rel.h:500
#define RelationGetRelid(relation)
Definition rel.h:516
ResourceOwner CurrentResourceOwner
Definition resowner.c:173
ResourceOwner CurTransactionResourceOwner
Definition resowner.c:174
bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
int RI_FKey_trigger_type(Oid tgfoid)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition snapmgr.c:682
void PopActiveSnapshot(void)
Definition snapmgr.c:775
#define SnapshotAny
Definition snapmgr.h:33
#define BTEqualStrategyNumber
Definition stratnum.h:31
AfterTriggerBatchCallback callback
Definition trigger.c:3973
struct AfterTriggerEventChunk * next
Definition trigger.c:3790
ItemPointerData ate_ctid2
Definition trigger.c:3737
TriggerFlags ate_flags
Definition trigger.c:3735
ItemPointerData ate_ctid1
Definition trigger.c:3736
AfterTriggerEventChunk * head
Definition trigger.c:3802
AfterTriggerEventChunk * tail
Definition trigger.c:3803
SetConstraintState state
Definition trigger.c:3909
AfterTriggersQueryData * query_stack
Definition trigger.c:3914
MemoryContext event_cxt
Definition trigger.c:3911
List * batch_callbacks
Definition trigger.c:3922
bool firing_batch_callbacks
Definition trigger.c:3924
CommandId firing_counter
Definition trigger.c:3908
AfterTriggersTransData * trans_stack
Definition trigger.c:3919
AfterTriggerEventList events
Definition trigger.c:3910
Tuplestorestate * fdw_tuplestore
Definition trigger.c:3938
AfterTriggerEventList events
Definition trigger.c:3937
Tuplestorestate * new_tuplestore
Definition trigger.c:3965
TupleTableSlot * storeslot
Definition trigger.c:3967
Tuplestorestate * old_tuplestore
Definition trigger.c:3963
AfterTriggerEventList after_trig_events
Definition trigger.c:3960
AfterTriggerEventList events
Definition trigger.c:3947
SetConstraintState state
Definition trigger.c:3946
CommandId firing_counter
Definition trigger.c:3949
List * es_tupleTable
Definition execnodes.h:749
Definition pg_list.h:54
char * relname
Definition primnodes.h:84
char * catalogname
Definition primnodes.h:78
char * schemaname
Definition primnodes.h:81
TupleDesc rd_att
Definition rel.h:112
Form_pg_class rd_rel
Definition rel.h:111
TupleTableSlot * tcs_original_insert_tuple
Definition trigger.h:76
struct AfterTriggersTableData * tcs_insert_private
Definition trigger.h:81
struct AfterTriggersTableData * tcs_update_private
Definition trigger.h:82
struct AfterTriggersTableData * tcs_delete_private
Definition trigger.h:83
int numtriggers
Definition reltrigger.h:50
Trigger * triggers
Definition reltrigger.h:49
bool trig_update_after_row
Definition reltrigger.h:62
bool trig_update_new_table
Definition reltrigger.h:77
bool trig_insert_after_row
Definition reltrigger.h:57
bool trig_insert_new_table
Definition reltrigger.h:75
bool trig_delete_old_table
Definition reltrigger.h:78
bool trig_delete_after_row
Definition reltrigger.h:67
bool trig_update_old_table
Definition reltrigger.h:76
Oid tgoid
Definition reltrigger.h:25
bool has_generated_virtual
Definition tupdesc.h:47
AttrMap * attrMap
Definition tupconvert.h:28
TupleDesc outdesc
Definition tupconvert.h:27
TupleConstr * constr
Definition tupdesc.h:159
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition tableam.h:1344
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
static SetConstraintState SetConstraintStateCopy(SetConstraintState origstate)
Definition trigger.c:5801
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition trigger.c:6723
static AfterTriggersData afterTriggers
Definition trigger.c:3977
#define AFTER_TRIGGER_FDW_FETCH
Definition trigger.c:3713
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition trigger.c:5821
#define AFTER_TRIGGER_IN_PROGRESS
Definition trigger.c:3710
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition trigger.c:5005
static void FireAfterTriggerBatchCallbacks(List *callbacks)
Definition trigger.c:6904
void AfterTriggerBeginXact(void)
Definition trigger.c:5131
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition trigger.c:4313
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition trigger.c:4914
static Bitmapset * afterTriggerCopyBitmap(Bitmapset *src)
Definition trigger.c:4100
#define CHUNK_DATA_START(cptr)
Definition trigger.c:3797
void AfterTriggerEndSubXact(bool isCommit)
Definition trigger.c:5519
void RegisterAfterTriggerBatchCallback(AfterTriggerBatchCallback callback, void *arg)
Definition trigger.c:6863
#define AFTER_TRIGGER_TUP_BITS
Definition trigger.c:3717
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition trigger.c:3509
#define AFTER_TRIGGER_1CTID
Definition trigger.c:3714
bool AfterTriggerIsActive(void)
Definition trigger.c:6928
Datum pg_trigger_depth(PG_FUNCTION_ARGS)
Definition trigger.c:6812
#define MAX_CHUNK_SIZE
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition trigger.c:5291
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition trigger.c:4252
#define GetTriggerSharedData(evt)
Definition trigger.c:3778
static int MyTriggerDepth
Definition trigger.c:68
#define for_each_chunk_from(cptr)
Definition trigger.c:3819
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition trigger.c:4661
static Tuplestorestate * GetAfterTriggersTransitionTable(int event, TupleTableSlot *oldslot, TupleTableSlot *newslot, TransitionCaptureState *transition_capture)
Definition trigger.c:5619
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition trigger.c:4745
struct AfterTriggerSharedData * AfterTriggerShared
Definition trigger.c:3718
void AfterTriggerSetState(ConstraintsSetStmt *stmt)
Definition trigger.c:5851
static void AfterTriggerExecute(EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, ResultRelInfo *src_relInfo, ResultRelInfo *dst_relInfo, TriggerDesc *trigdesc, FmgrInfo *finfo, TriggerInstrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
Definition trigger.c:4375
static bool before_stmt_triggers_fired(Oid relid, CmdType cmdType)
Definition trigger.c:6677
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition trigger.c:4125
#define AFTER_TRIGGER_2CTID
Definition trigger.c:3715
#define SizeofTriggerEvent(evt)
Definition trigger.c:3769
int SessionReplicationRole
Definition trigger.c:65
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, TriggerInstrumentation *instr, MemoryContext per_tuple_context)
Definition trigger.c:2336
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition trigger.c:4055
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition trigger.c:5776
void AfterTriggerFireDeferred(void)
Definition trigger.c:5354
static void TransitionTableAddTuple(EState *estate, int event, TransitionCaptureState *transition_capture, ResultRelInfo *relinfo, TupleTableSlot *slot, TupleTableSlot *original_insert_tuple, Tuplestorestate *tuplestore)
Definition trigger.c:5670
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition trigger.c:4273
#define for_each_event_from(eptr, cptr)
Definition trigger.c:3821
#define MIN_CHUNK_SIZE
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition trigger.c:4019
void assign_session_replication_role(int newval, void *extra)
Definition trigger.c:6798
void AfterTriggerEndXact(bool isCommit)
Definition trigger.c:5417
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6175
#define AFTER_TRIGGER_FDW_REUSE
Definition trigger.c:3712
void AfterTriggerBeginSubXact(void)
Definition trigger.c:5471
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldslot, TupleTableSlot *newslot, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition trigger.c:6262
static HeapTuple check_modified_virtual_generated(TupleDesc tupdesc, HeapTuple tuple)
Definition trigger.c:6828
static TupleTableSlot * GetAfterTriggersStoreSlot(AfterTriggersTableData *table, TupleDesc tupdesc)
Definition trigger.c:4956
#define AFTER_TRIGGER_CP_UPDATE
Definition trigger.c:3716
void AfterTriggerEndQuery(EState *estate)
Definition trigger.c:5186
#define AFTER_TRIGGER_DONE
Definition trigger.c:3709
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition trigger.c:3815
SetConstraintStateData * SetConstraintState
Definition trigger.c:3661
static void AfterTriggerEnlargeQueryState(void)
Definition trigger.c:5728
#define for_each_event(eptr, cptr)
Definition trigger.c:3810
struct AfterTriggerEventData * AfterTriggerEvent
Definition trigger.c:3731
#define for_each_chunk(cptr, evtlist)
Definition trigger.c:3808
void AfterTriggerBeginQuery(void)
Definition trigger.c:5166
#define AFTER_TRIGGER_DEFERRABLE
Definition trigger.h:109
#define TRIGGER_FIRED_FOR_STATEMENT(event)
Definition trigger.h:127
#define TRIGGER_EVENT_UPDATE
Definition trigger.h:96
#define RI_TRIGGER_FK
Definition trigger.h:287
#define TRIGGER_FIRED_BY_DELETE(event)
Definition trigger.h:115
#define TRIGGER_EVENT_DELETE
Definition trigger.h:95
#define TRIGGER_EVENT_OPMASK
Definition trigger.h:98
#define RI_TRIGGER_NONE
Definition trigger.h:288
#define AFTER_TRIGGER_INITDEFERRED
Definition trigger.h:110
#define TRIGGER_EVENT_ROW
Definition trigger.h:100
#define TRIGGER_FIRED_AFTER(event)
Definition trigger.h:133
void(* AfterTriggerBatchCallback)(void *arg)
Definition trigger.h:307
#define TRIGGER_EVENT_INSERT
Definition trigger.h:94
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition trigger.h:118
#define TRIGGER_EVENT_TRUNCATE
Definition trigger.h:97
#define RI_TRIGGER_PK
Definition trigger.h:286
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition tupconvert.c:193
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition tupdesc.c:242
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition tuplestore.c:743
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition tuplestore.c:331
void tuplestore_end(Tuplestorestate *state)
Definition tuplestore.c:493
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
#define TupIsNull(slot)
Definition tuptable.h:325
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:544
int GetCurrentTransactionNestLevel(void)
Definition xact.c:931
bool IsSubTransaction(void)
Definition xact.c:5098

◆ for_each_chunk_from

#define for_each_chunk_from (   cptr)     for (; cptr != NULL; cptr = cptr->next)

Definition at line 3819 of file trigger.c.

◆ for_each_event

#define for_each_event (   eptr,
  cptr 
)
Value:
(char *) eptr < (cptr)->freeptr; \

Definition at line 3810 of file trigger.c.

◆ for_each_event_chunk

#define for_each_event_chunk (   eptr,
  cptr,
  evtlist 
)     for_each_chunk(cptr, evtlist) for_each_event(eptr, cptr)

Definition at line 3815 of file trigger.c.

◆ for_each_event_from

#define for_each_event_from (   eptr,
  cptr 
)
Value:
for (; \
(char *) eptr < (cptr)->freeptr; \

Definition at line 3821 of file trigger.c.

◆ GetTriggerSharedData

#define GetTriggerSharedData (   evt)     ((AfterTriggerShared) ((char *) (evt) + ((evt)->ate_flags & AFTER_TRIGGER_OFFSET)))

Definition at line 3778 of file trigger.c.

◆ MAX_CHUNK_SIZE

#define MAX_CHUNK_SIZE   (1024*1024)

◆ MIN_CHUNK_SIZE

#define MIN_CHUNK_SIZE   1024

◆ SizeofTriggerEvent

Typedef Documentation

◆ AfterTriggerCallbackItem

◆ AfterTriggerEvent

Definition at line 3731 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3718 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

◆ AfterTriggersTableData

◆ AfterTriggersTransData

◆ SetConstraintState

◆ SetConstraintStateData

◆ SetConstraintTrigger

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3706 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

static void afterTriggerAddEvent ( AfterTriggerEventList events,
AfterTriggerEvent  event,
AfterTriggerShared  evtshared 
)
static

Definition at line 4125 of file trigger.c.

4127{
4133
4134 /*
4135 * If empty list or not enough room in the tail chunk, make a new chunk.
4136 * We assume here that a new shared record will always be needed.
4137 */
4138 chunk = events->tail;
4139 if (chunk == NULL ||
4140 chunk->endfree - chunk->freeptr < needed)
4141 {
4143
4144 /* Create event context if we didn't already */
4148 "AfterTriggerEvents",
4150
4151 /*
4152 * Chunk size starts at 1KB and is allowed to increase up to 1MB.
4153 * These numbers are fairly arbitrary, though there is a hard limit at
4154 * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
4155 * shared records using the available space in ate_flags. Another
4156 * constraint is that if the chunk size gets too huge, the search loop
4157 * below would get slow given a (not too common) usage pattern with
4158 * many distinct event types in a chunk. Therefore, we double the
4159 * preceding chunk size only if there weren't too many shared records
4160 * in the preceding chunk; otherwise we halve it. This gives us some
4161 * ability to adapt to the actual usage pattern of the current query
4162 * while still having large chunk sizes in typical usage. All chunk
4163 * sizes used should be MAXALIGN multiples, to ensure that the shared
4164 * records will be aligned safely.
4165 */
4166#define MIN_CHUNK_SIZE 1024
4167#define MAX_CHUNK_SIZE (1024*1024)
4168
4169#if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
4170#error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
4171#endif
4172
4173 if (chunk == NULL)
4175 else
4176 {
4177 /* preceding chunk size... */
4178 chunksize = chunk->endptr - (char *) chunk;
4179 /* check number of shared records in preceding chunk */
4180 if ((chunk->endptr - chunk->endfree) <=
4181 (100 * sizeof(AfterTriggerSharedData)))
4182 chunksize *= 2; /* okay, double it */
4183 else
4184 chunksize /= 2; /* too many shared records */
4186 }
4188 chunk->next = NULL;
4189 chunk->freeptr = CHUNK_DATA_START(chunk);
4190 chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
4191 Assert(chunk->endfree - chunk->freeptr >= needed);
4192
4193 if (events->tail == NULL)
4194 {
4195 Assert(events->head == NULL);
4196 events->head = chunk;
4197 }
4198 else
4199 events->tail->next = chunk;
4200 events->tail = chunk;
4201 /* events->tailfree is now out of sync, but we'll fix it below */
4202 }
4203
4204 /*
4205 * Try to locate a matching shared-data record already in the chunk. If
4206 * none, make a new one. The search begins with the most recently added
4207 * record, since newer ones are most likely to match.
4208 */
4209 for (newshared = (AfterTriggerShared) chunk->endfree;
4210 (char *) newshared < chunk->endptr;
4211 newshared++)
4212 {
4213 /* compare fields roughly by probability of them being different */
4214 if (newshared->ats_tgoid == evtshared->ats_tgoid &&
4215 newshared->ats_event == evtshared->ats_event &&
4216 newshared->ats_firing_id == 0 &&
4217 newshared->ats_table == evtshared->ats_table &&
4218 newshared->ats_relid == evtshared->ats_relid &&
4219 newshared->ats_rolid == evtshared->ats_rolid &&
4220 bms_equal(newshared->ats_modifiedcols,
4221 evtshared->ats_modifiedcols))
4222 break;
4223 }
4224 if ((char *) newshared >= chunk->endptr)
4225 {
4226 newshared = ((AfterTriggerShared) chunk->endfree) - 1;
4227 *newshared = *evtshared;
4228 /* now we must make a suitably-long-lived copy of the bitmap */
4229 newshared->ats_modifiedcols = afterTriggerCopyBitmap(evtshared->ats_modifiedcols);
4230 newshared->ats_firing_id = 0; /* just to be sure */
4231 chunk->endfree = (char *) newshared;
4232 }
4233
4234 /* Insert the data */
4235 newevent = (AfterTriggerEvent) chunk->freeptr;
4236 memcpy(newevent, event, eventsize);
4237 /* ... and link the new event to its shared record */
4238 newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
4239 newevent->ate_flags |= (char *) newshared - (char *) newevent;
4240
4241 chunk->freeptr += eventsize;
4242 events->tailfree = chunk->freeptr;
4243}

References afterTriggerCopyBitmap(), afterTriggers, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, bms_equal(), CHUNK_DATA_START, AfterTriggersData::event_cxt, fb(), AfterTriggerEventList::head, MAX_CHUNK_SIZE, memcpy(), MemoryContextAlloc(), Min, MIN_CHUNK_SIZE, AfterTriggerEventChunk::next, SizeofTriggerEvent, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TopTransactionContext.

Referenced by afterTriggerMarkEvents(), and AfterTriggerSaveEvent().

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 5166 of file trigger.c.

5167{
5168 /* Increase the query stack depth */
5170}

References afterTriggers, and AfterTriggersData::query_depth.

Referenced by CopyFrom(), create_edata_for_relation(), ExecForPortionOfLeftovers(), ExecuteTruncateGuts(), and standard_ExecutorStart().

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5471 of file trigger.c.

5472{
5473 int my_level = GetCurrentTransactionNestLevel();
5474
5475 /*
5476 * Allocate more space in the trans_stack if needed. (Note: because the
5477 * minimum nest level of a subtransaction is 2, we waste the first couple
5478 * entries of the array; not worth the notational effort to avoid it.)
5479 */
5480 while (my_level >= afterTriggers.maxtransdepth)
5481 {
5483 {
5484 /* Arbitrarily initialize for max of 8 subtransaction levels */
5487 8 * sizeof(AfterTriggersTransData));
5489 }
5490 else
5491 {
5492 /* repalloc will keep the stack in the same context */
5494
5499 }
5500 }
5501
5502 /*
5503 * Push the current information into the stack. The SET CONSTRAINTS state
5504 * is not saved until/unless changed. Likewise, we don't make a
5505 * per-subtransaction event context until needed.
5506 */
5507 afterTriggers.trans_stack[my_level].state = NULL;
5511}

References afterTriggers, AfterTriggersData::events, AfterTriggersTransData::events, fb(), AfterTriggersData::firing_counter, AfterTriggersTransData::firing_counter, GetCurrentTransactionNestLevel(), AfterTriggersData::maxtransdepth, MemoryContextAlloc(), AfterTriggersData::query_depth, AfterTriggersTransData::query_depth, repalloc(), AfterTriggersTransData::state, TopTransactionContext, and AfterTriggersData::trans_stack.

Referenced by StartSubTransaction().

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

Definition at line 5131 of file trigger.c.

5132{
5133 /*
5134 * Initialize after-trigger state structure to empty
5135 */
5136 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5141
5142 /*
5143 * Verify that there is no leftover state remaining. If these assertions
5144 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5145 * up properly.
5146 */
5154}

References afterTriggers, Assert, AfterTriggersData::batch_callbacks, AfterTriggersData::event_cxt, AfterTriggersData::events, fb(), AfterTriggersData::firing_batch_callbacks, AfterTriggersData::firing_counter, AfterTriggersData::firing_depth, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, NIL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, and AfterTriggersData::trans_stack.

Referenced by StartTransaction().

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

Definition at line 4055 of file trigger.c.

4056{
4057 Oid tgoid = evtshared->ats_tgoid;
4059 int i;
4060
4061 /*
4062 * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
4063 * constraints declared NOT DEFERRABLE), the state is always false.
4064 */
4065 if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
4066 return false;
4067
4068 /*
4069 * If constraint state exists, SET CONSTRAINTS might have been executed
4070 * either for this trigger or for all triggers.
4071 */
4072 if (state != NULL)
4073 {
4074 /* Check for SET CONSTRAINTS for this specific trigger. */
4075 for (i = 0; i < state->numstates; i++)
4076 {
4077 if (state->trigstates[i].sct_tgoid == tgoid)
4078 return state->trigstates[i].sct_tgisdeferred;
4079 }
4080
4081 /* Check for SET CONSTRAINTS ALL. */
4082 if (state->all_isset)
4083 return state->all_isdeferred;
4084 }
4085
4086 /*
4087 * Otherwise return the default state for the trigger.
4088 */
4089 return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
4090}

References AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_INITDEFERRED, afterTriggers, fb(), i, and AfterTriggersData::state.

Referenced by afterTriggerMarkEvents().

◆ afterTriggerCopyBitmap()

static Bitmapset * afterTriggerCopyBitmap ( Bitmapset src)
static

Definition at line 4100 of file trigger.c.

4101{
4102 Bitmapset *dst;
4104
4105 if (src == NULL)
4106 return NULL;
4107
4109
4110 dst = bms_copy(src);
4111
4113
4114 return dst;
4115}

References afterTriggers, bms_copy(), AfterTriggersData::event_cxt, fb(), and MemoryContextSwitchTo().

Referenced by afterTriggerAddEvent().

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 4313 of file trigger.c.

4314{
4315 AfterTriggerEventChunk *target = qs->events.head;
4316 ListCell *lc;
4317
4318 Assert(target && target->next);
4319
4320 /*
4321 * First, update any pointers in the per-table data, so that they won't be
4322 * dangling. Resetting obsoleted pointers to NULL will make
4323 * cancel_prior_stmt_triggers start from the list head, which is fine.
4324 */
4325 foreach(lc, qs->tables)
4326 {
4328
4329 if (table->after_trig_done &&
4330 table->after_trig_events.tail == target)
4331 {
4332 table->after_trig_events.head = NULL;
4333 table->after_trig_events.tail = NULL;
4334 table->after_trig_events.tailfree = NULL;
4335 }
4336 }
4337
4338 /* Now we can flush the head chunk */
4339 qs->events.head = target->next;
4340 pfree(target);
4341}

References Assert, fb(), lfirst, AfterTriggerEventChunk::next, pfree(), and table.

Referenced by AfterTriggerEndQuery().

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 5186 of file trigger.c.

5187{
5189
5190 /* Must be inside a query, too */
5192
5193 /*
5194 * If we never even got as far as initializing the event stack, there
5195 * certainly won't be any events, so exit quickly.
5196 */
5198 {
5200 return;
5201 }
5202
5203 /*
5204 * Process all immediate-mode triggers queued by the query, and move the
5205 * deferred ones to the main list of deferred events.
5206 *
5207 * Notice that we decide which ones will be fired, and put the deferred
5208 * ones on the main list, before anything is actually fired. This ensures
5209 * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5210 * IMMEDIATE: all events we have decided to defer will be available for it
5211 * to fire.
5212 *
5213 * We loop in case a trigger queues more events at the same query level.
5214 * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5215 * will instead fire any triggers in a dedicated query level. Foreign key
5216 * enforcement triggers do add to the current query level, thanks to their
5217 * passing fire_triggers = false to SPI_execute_snapshot(). Other
5218 * C-language triggers might do likewise.
5219 *
5220 * If we find no firable events, we don't have to increment
5221 * firing_counter.
5222 */
5224
5226 for (;;)
5227 {
5228 if (afterTriggerMarkEvents(&qs->events, &afterTriggers.events, true))
5229 {
5231 AfterTriggerEventChunk *oldtail = qs->events.tail;
5232
5233 if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5234 break; /* all fired */
5235
5236 /*
5237 * Firing a trigger could result in query_stack being repalloc'd,
5238 * so we must recalculate qs after each afterTriggerInvokeEvents
5239 * call. Furthermore, it's unsafe to pass delete_ok = true here,
5240 * because that could cause afterTriggerInvokeEvents to try to
5241 * access qs->events after the stack has been repalloc'd.
5242 */
5244
5245 /*
5246 * We'll need to scan the events list again. To reduce the cost
5247 * of doing so, get rid of completely-fired chunks. We know that
5248 * all events were marked IN_PROGRESS or DONE at the conclusion of
5249 * afterTriggerMarkEvents, so any still-interesting events must
5250 * have been added after that, and so must be in the chunk that
5251 * was then the tail chunk, or in later chunks. So, zap all
5252 * chunks before oldtail. This is approximately the same set of
5253 * events we would have gotten rid of by passing delete_ok = true.
5254 */
5255 Assert(oldtail != NULL);
5256 while (qs->events.head != oldtail)
5258 }
5259 else
5260 break;
5261 }
5262
5263 /*
5264 * Fire batch callbacks before releasing query-level storage and before
5265 * decrementing query_depth. Callbacks may do real work (index probes,
5266 * error reporting).
5267 *
5268 * Recompute qs first: the loop above refreshes it after each
5269 * afterTriggerInvokeEvents() call (see comment there), but the "all
5270 * fired" break exits without doing so, leaving qs potentially stale here.
5271 */
5273 FireAfterTriggerBatchCallbacks(qs->batch_callbacks);
5274
5275 /* Release query-level-local storage, including tuplestores if any */
5277
5280}

References afterTriggerDeleteHeadEventChunk(), AfterTriggerFreeQuery(), afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, Assert, AfterTriggersData::events, fb(), FireAfterTriggerBatchCallbacks(), AfterTriggersData::firing_counter, AfterTriggersData::firing_depth, AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.

Referenced by CopyFrom(), ExecForPortionOfLeftovers(), ExecuteTruncateGuts(), finish_edata(), and standard_ExecutorFinish().

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

Definition at line 5519 of file trigger.c.

5520{
5521 int my_level = GetCurrentTransactionNestLevel();
5523 AfterTriggerEvent event;
5526
5527 /*
5528 * Pop the prior state if needed.
5529 */
5530 if (isCommit)
5531 {
5533 /* If we saved a prior state, we don't need it anymore */
5535 if (state != NULL)
5536 pfree(state);
5537 /* this avoids double pfree if error later: */
5538 afterTriggers.trans_stack[my_level].state = NULL;
5541 }
5542 else
5543 {
5544 /*
5545 * Aborting. It is possible subxact start failed before calling
5546 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5547 * trans_stack levels that aren't there.
5548 */
5549 if (my_level >= afterTriggers.maxtransdepth)
5550 return;
5551
5552 /*
5553 * Release query-level storage for queries being aborted, and restore
5554 * query_depth to its pre-subxact value. This assumes that a
5555 * subtransaction will not add events to query levels started in a
5556 * earlier transaction state.
5557 */
5559 {
5563 }
5566
5567 /*
5568 * Restore the global deferred-event list to its former length,
5569 * discarding any events queued by the subxact.
5570 */
5572 &afterTriggers.trans_stack[my_level].events);
5573
5574 /*
5575 * Restore the trigger state. If the saved state is NULL, then this
5576 * subxact didn't save it, so it doesn't need restoring.
5577 */
5579 if (state != NULL)
5580 {
5583 }
5584 /* this avoids double pfree if error later: */
5585 afterTriggers.trans_stack[my_level].state = NULL;
5586
5587 /*
5588 * Scan for any remaining deferred events that were marked DONE or IN
5589 * PROGRESS by this subxact or a child, and un-mark them. We can
5590 * recognize such events because they have a firing ID greater than or
5591 * equal to the firing_counter value we saved at subtransaction start.
5592 * (This essentially assumes that the current subxact includes all
5593 * subxacts started after it.)
5594 */
5597 {
5599
5600 if (event->ate_flags &
5602 {
5603 if (evtshared->ats_firing_id >= subxact_firing_id)
5604 event->ate_flags &=
5606 }
5607 }
5608 }
5609
5610 /* Reset in case a callback threw an error while firing. */
5612}

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AfterTriggerFreeQuery(), afterTriggerRestoreEventList(), afterTriggers, Assert, AfterTriggerEventData::ate_flags, AfterTriggersData::events, AfterTriggersTransData::events, fb(), AfterTriggersData::firing_batch_callbacks, AfterTriggersTransData::firing_counter, for_each_event_chunk, GetCurrentTransactionNestLevel(), GetTriggerSharedData, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, pfree(), AfterTriggersData::query_depth, AfterTriggersTransData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, AfterTriggersTransData::state, and AfterTriggersData::trans_stack.

Referenced by AbortSubTransaction(), and CommitSubTransaction().

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

Definition at line 5417 of file trigger.c.

5418{
5419 /*
5420 * Forget the pending-events list.
5421 *
5422 * Since all the info is in TopTransactionContext or children thereof, we
5423 * don't really need to do anything to reclaim memory. However, the
5424 * pending-events list could be large, and so it's useful to discard it as
5425 * soon as possible --- especially if we are aborting because we ran out
5426 * of memory for the list!
5427 */
5429 {
5435 }
5436
5437 /*
5438 * Forget any subtransaction state as well. Since this can't be very
5439 * large, we let the eventual reset of TopTransactionContext free the
5440 * memory instead of doing it here.
5441 */
5444
5445
5446 /*
5447 * Forget the query stack and constraint-related state information. As
5448 * with the subtransaction state information, we don't bother freeing the
5449 * memory here.
5450 */
5454
5455 /* No more afterTriggers manipulation until next transaction starts. */
5457
5459
5463}

References afterTriggers, AfterTriggersData::batch_callbacks, AfterTriggersData::event_cxt, AfterTriggersData::events, fb(), AfterTriggersData::firing_batch_callbacks, AfterTriggersData::firing_depth, AfterTriggerEventList::head, list_free_deep(), AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, MemoryContextDelete(), NIL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and AfterTriggersData::trans_stack.

Referenced by AbortTransaction(), CommitTransaction(), and PrepareTransaction().

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

Definition at line 5728 of file trigger.c.

5729{
5731
5733
5735 {
5736 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5737
5742 }
5743 else
5744 {
5745 /* repalloc will keep the stack in the same context */
5748 old_alloc * 2);
5749
5754 }
5755
5756 /* Initialize new array entries to empty */
5758 {
5760
5761 qs->events.head = NULL;
5762 qs->events.tail = NULL;
5763 qs->events.tailfree = NULL;
5764 qs->fdw_tuplestore = NULL;
5765 qs->tables = NIL;
5766 qs->batch_callbacks = NIL;
5767
5768 ++init_depth;
5769 }
5770}

References afterTriggers, Assert, AfterTriggersQueryData::events, fb(), AfterTriggerEventList::head, Max, AfterTriggersData::maxquerydepth, MemoryContextAlloc(), NIL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, repalloc(), and TopTransactionContext.

Referenced by AfterTriggerSaveEvent(), before_stmt_triggers_fired(), and MakeTransitionCaptureState().

◆ AfterTriggerExecute()

static void AfterTriggerExecute ( EState estate,
AfterTriggerEvent  event,
ResultRelInfo relInfo,
ResultRelInfo src_relInfo,
ResultRelInfo dst_relInfo,
TriggerDesc trigdesc,
FmgrInfo finfo,
TriggerInstrumentation instr,
MemoryContext  per_tuple_context,
TupleTableSlot trig_tuple_slot1,
TupleTableSlot trig_tuple_slot2 
)
static

Definition at line 4375 of file trigger.c.

4385{
4386 Relation rel = relInfo->ri_RelationDesc;
4387 Relation src_rel = src_relInfo->ri_RelationDesc;
4388 Relation dst_rel = dst_relInfo->ri_RelationDesc;
4390 Oid tgoid = evtshared->ats_tgoid;
4393 int save_sec_context;
4395 int tgindx;
4396 bool should_free_trig = false;
4397 bool should_free_new = false;
4398
4399 /*
4400 * Locate trigger in trigdesc. It might not be present, and in fact the
4401 * trigdesc could be NULL, if the trigger was dropped since the event was
4402 * queued. In that case, silently do nothing.
4403 */
4404 if (trigdesc == NULL)
4405 return;
4406 for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4407 {
4408 if (trigdesc->triggers[tgindx].tgoid == tgoid)
4409 {
4410 LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4411 break;
4412 }
4413 }
4414 if (LocTriggerData.tg_trigger == NULL)
4415 return;
4416
4417 /*
4418 * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4419 * to include time spent re-fetching tuples in the trigger cost.
4420 */
4421 if (instr)
4422 InstrStartTrigger(instr + tgindx);
4423
4424 /*
4425 * Fetch the required tuple(s).
4426 */
4427 switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4428 {
4430 {
4431 Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4432
4433 if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4435 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4436
4437 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4439 !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4441 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4442 }
4445
4446 /*
4447 * Store tuple in the slot so that tg_trigtuple does not reference
4448 * tuplestore memory. (It is formally possible for the trigger
4449 * function to queue trigger events that add to the same
4450 * tuplestore, which can push other tuples out of memory.) The
4451 * distinction is academic, because we start with a minimal tuple
4452 * that is stored as a heap tuple, constructed in different memory
4453 * context, in the slot anyway.
4454 */
4455 LocTriggerData.tg_trigslot = trig_tuple_slot1;
4456 LocTriggerData.tg_trigtuple =
4458
4459 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4461 {
4462 LocTriggerData.tg_newslot = trig_tuple_slot2;
4463 LocTriggerData.tg_newtuple =
4465 }
4466 else
4467 {
4468 LocTriggerData.tg_newtuple = NULL;
4469 }
4470 break;
4471
4472 default:
4473 if (ItemPointerIsValid(&(event->ate_ctid1)))
4474 {
4476 src_relInfo);
4477
4479 &(event->ate_ctid1),
4481 src_slot))
4482 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4483
4484 /*
4485 * Store the tuple fetched from the source partition into the
4486 * target (root partitioned) table slot, converting if needed.
4487 */
4488 if (src_relInfo != relInfo)
4489 {
4491
4492 LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4493 if (map)
4494 {
4496 src_slot,
4497 LocTriggerData.tg_trigslot);
4498 }
4499 else
4500 ExecCopySlot(LocTriggerData.tg_trigslot, src_slot);
4501 }
4502 else
4503 LocTriggerData.tg_trigslot = src_slot;
4504 LocTriggerData.tg_trigtuple =
4506 }
4507 else
4508 {
4509 LocTriggerData.tg_trigtuple = NULL;
4510 }
4511
4512 /* don't touch ctid2 if not there */
4514 (event->ate_flags & AFTER_TRIGGER_CP_UPDATE)) &&
4515 ItemPointerIsValid(&(event->ate_ctid2)))
4516 {
4518 dst_relInfo);
4519
4521 &(event->ate_ctid2),
4523 dst_slot))
4524 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4525
4526 /*
4527 * Store the tuple fetched from the destination partition into
4528 * the target (root partitioned) table slot, converting if
4529 * needed.
4530 */
4531 if (dst_relInfo != relInfo)
4532 {
4534
4535 LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4536 if (map)
4537 {
4539 dst_slot,
4540 LocTriggerData.tg_newslot);
4541 }
4542 else
4544 }
4545 else
4546 LocTriggerData.tg_newslot = dst_slot;
4547 LocTriggerData.tg_newtuple =
4549 }
4550 else
4551 {
4552 LocTriggerData.tg_newtuple = NULL;
4553 }
4554 }
4555
4556 /*
4557 * Set up the tuplestore information to let the trigger have access to
4558 * transition tables. When we first make a transition table available to
4559 * a trigger, mark it "closed" so that it cannot change anymore. If any
4560 * additional events of the same type get queued in the current trigger
4561 * query level, they'll go into new transition tables.
4562 */
4563 LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4564 if (evtshared->ats_table)
4565 {
4566 if (LocTriggerData.tg_trigger->tgoldtable)
4567 {
4568 LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4569 evtshared->ats_table->closed = true;
4570 }
4571
4572 if (LocTriggerData.tg_trigger->tgnewtable)
4573 {
4574 LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4575 evtshared->ats_table->closed = true;
4576 }
4577 }
4578
4579 /*
4580 * Setup the remaining trigger information
4581 */
4583 LocTriggerData.tg_event =
4585 LocTriggerData.tg_relation = rel;
4586 if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4587 LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4588
4590
4591 /*
4592 * If necessary, become the role that was active when the trigger got
4593 * queued. Note that the role might have been dropped since the trigger
4594 * was queued, but if that is a problem, we will get an error later.
4595 * Checking here would still leave a race condition.
4596 */
4597 GetUserIdAndSecContext(&save_rolid, &save_sec_context);
4598 if (save_rolid != evtshared->ats_rolid)
4600 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
4601
4602 /*
4603 * Call the trigger and throw away any possibly returned updated tuple.
4604 * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4605 */
4607 tgindx,
4608 finfo,
4609 NULL,
4611 if (rettuple != NULL &&
4612 rettuple != LocTriggerData.tg_trigtuple &&
4613 rettuple != LocTriggerData.tg_newtuple)
4615
4616 /* Restore the current role if necessary */
4617 if (save_rolid != evtshared->ats_rolid)
4618 SetUserIdAndSecContext(save_rolid, save_sec_context);
4619
4620 /*
4621 * Release resources
4622 */
4623 if (should_free_trig)
4624 heap_freetuple(LocTriggerData.tg_trigtuple);
4625 if (should_free_new)
4626 heap_freetuple(LocTriggerData.tg_newtuple);
4627
4628 /* don't clear slots' contents if foreign table */
4629 if (trig_tuple_slot1 == NULL)
4630 {
4631 if (LocTriggerData.tg_trigslot)
4632 ExecClearTuple(LocTriggerData.tg_trigslot);
4633 if (LocTriggerData.tg_newslot)
4634 ExecClearTuple(LocTriggerData.tg_newslot);
4635 }
4636
4637 /*
4638 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4639 * the firing of the trigger.
4640 */
4641 if (instr)
4642 InstrStopTrigger(instr + tgindx, 1);
4643}

References AFTER_TRIGGER_2CTID, AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_TUP_BITS, AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_flags, TupleConversionMap::attrMap, elog, ERROR, ExecCallTriggerFunc(), ExecClearTuple(), ExecCopySlot(), ExecFetchSlotHeapTuple(), ExecGetChildToRootMap(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), execute_attr_map_slot(), fb(), GetCurrentFDWTuplestore(), GetTriggerSharedData, GetUserIdAndSecContext(), heap_freetuple(), InstrStartTrigger(), InstrStopTrigger(), ItemPointerIsValid(), MemoryContextReset(), TriggerDesc::numtriggers, pg_fallthrough, SECURITY_LOCAL_USERID_CHANGE, SetUserIdAndSecContext(), SnapshotAny, table_tuple_fetch_row_version(), Trigger::tgoid, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerDesc::triggers, and tuplestore_gettupleslot().

Referenced by afterTriggerInvokeEvents().

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

Definition at line 5354 of file trigger.c.

5355{
5356 AfterTriggerEventList *events;
5357 bool snap_pushed = false;
5358
5359 /* Must not be inside a query */
5361
5362 /*
5363 * If there are any triggers to fire, make sure we have set a snapshot for
5364 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5365 * can't assume ActiveSnapshot is valid on entry.)
5366 */
5367 events = &afterTriggers.events;
5368 if (events->head != NULL)
5369 {
5371 snap_pushed = true;
5372 }
5373
5374 /*
5375 * Run all the remaining triggers. Loop until they are all gone, in case
5376 * some trigger queues more for us to do.
5377 */
5379 while (afterTriggerMarkEvents(events, NULL, false))
5380 {
5382
5383 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5384 break; /* all fired */
5385 }
5386
5387 /* Flush any fast-path batches accumulated by the triggers just fired. */
5389
5391
5392 /*
5393 * We don't bother freeing the event list or batch_callbacks, since they
5394 * will go away anyway (and more efficiently than via pfree) in
5395 * AfterTriggerEndXact.
5396 */
5397
5398 if (snap_pushed)
5400}

References afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, Assert, AfterTriggersData::batch_callbacks, AfterTriggersData::events, fb(), FireAfterTriggerBatchCallbacks(), AfterTriggersData::firing_counter, AfterTriggersData::firing_depth, GetTransactionSnapshot(), AfterTriggerEventList::head, PopActiveSnapshot(), PushActiveSnapshot(), and AfterTriggersData::query_depth.

Referenced by CommitTransaction(), and PrepareTransaction().

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4252 of file trigger.c.

4253{
4255
4256 while ((chunk = events->head) != NULL)
4257 {
4258 events->head = chunk->next;
4259 pfree(chunk);
4260 }
4261 events->tail = NULL;
4262 events->tailfree = NULL;
4263}

References fb(), AfterTriggerEventList::head, AfterTriggerEventChunk::next, pfree(), AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 5291 of file trigger.c.

5292{
5293 Tuplestorestate *ts;
5294 List *tables;
5295 ListCell *lc;
5296
5297 /* Drop the trigger events */
5298 afterTriggerFreeEventList(&qs->events);
5299
5300 /* Drop FDW tuplestore if any */
5301 ts = qs->fdw_tuplestore;
5302 qs->fdw_tuplestore = NULL;
5303 if (ts)
5304 tuplestore_end(ts);
5305
5306 /* Release per-table subsidiary storage */
5307 tables = qs->tables;
5308 foreach(lc, tables)
5309 {
5311
5312 ts = table->old_tuplestore;
5313 table->old_tuplestore = NULL;
5314 if (ts)
5315 tuplestore_end(ts);
5316 ts = table->new_tuplestore;
5317 table->new_tuplestore = NULL;
5318 if (ts)
5319 tuplestore_end(ts);
5320 if (table->storeslot)
5321 {
5322 TupleTableSlot *slot = table->storeslot;
5323
5324 table->storeslot = NULL;
5326 }
5327 }
5328
5329 /*
5330 * Now free the AfterTriggersTableData structs and list cells. Reset list
5331 * pointer first; if list_free_deep somehow gets an error, better to leak
5332 * that storage than have an infinite loop.
5333 */
5334 qs->tables = NIL;
5335 list_free_deep(tables);
5336
5337 list_free_deep(qs->batch_callbacks);
5338 qs->batch_callbacks = NIL;
5339}

References afterTriggerFreeEventList(), ExecDropSingleTupleTableSlot(), fb(), lfirst, list_free_deep(), NIL, table, and tuplestore_end().

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

◆ afterTriggerInvokeEvents()

static bool afterTriggerInvokeEvents ( AfterTriggerEventList events,
CommandId  firing_id,
EState estate,
bool  delete_ok 
)
static

Definition at line 4745 of file trigger.c.

4749{
4750 bool all_fired = true;
4753 bool local_estate = false;
4755 Relation rel = NULL;
4756 TriggerDesc *trigdesc = NULL;
4757 FmgrInfo *finfo = NULL;
4760 *slot2 = NULL;
4761
4762 /* Make a local EState if need be */
4763 if (estate == NULL)
4764 {
4765 estate = CreateExecutorState();
4766 local_estate = true;
4767 }
4768
4769 /* Make a per-tuple memory context for trigger function calls */
4772 "AfterTriggerTupleContext",
4774
4775 for_each_chunk(chunk, *events)
4776 {
4777 AfterTriggerEvent event;
4778 bool all_fired_in_chunk = true;
4779
4780 for_each_event(event, chunk)
4781 {
4783
4784 /*
4785 * Is it one for me to fire?
4786 */
4787 if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4788 evtshared->ats_firing_id == firing_id)
4789 {
4791 *dst_rInfo;
4792
4793 /*
4794 * So let's fire it... but first, find the correct relation if
4795 * this is not the same relation as before.
4796 */
4797 if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4798 {
4799 rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4800 NULL);
4801 rel = rInfo->ri_RelationDesc;
4802 /* Catch calls with insufficient relcache refcounting */
4804 trigdesc = rInfo->ri_TrigDesc;
4805 /* caution: trigdesc could be NULL here */
4806 finfo = rInfo->ri_TrigFunctions;
4807 instr = rInfo->ri_TrigInstrument;
4808 if (slot1 != NULL)
4809 {
4812 slot1 = slot2 = NULL;
4813 }
4814 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4815 {
4820 }
4821 }
4822
4823 /*
4824 * Look up source and destination partition result rels of a
4825 * cross-partition update event.
4826 */
4827 if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4829 {
4830 Assert(OidIsValid(event->ate_src_part) &&
4831 OidIsValid(event->ate_dst_part));
4833 event->ate_src_part,
4834 rInfo);
4836 event->ate_dst_part,
4837 rInfo);
4838 }
4839 else
4841
4842 /*
4843 * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4844 * still set, so recursive examinations of the event list
4845 * won't try to re-fire it.
4846 */
4847 AfterTriggerExecute(estate, event, rInfo,
4849 trigdesc, finfo, instr,
4851
4852 /*
4853 * Mark the event as done.
4854 */
4855 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4856 event->ate_flags |= AFTER_TRIGGER_DONE;
4857 }
4858 else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4859 {
4860 /* something remains to be done */
4861 all_fired = all_fired_in_chunk = false;
4862 }
4863 }
4864
4865 /* Clear the chunk if delete_ok and nothing left of interest */
4867 {
4868 chunk->freeptr = CHUNK_DATA_START(chunk);
4869 chunk->endfree = chunk->endptr;
4870
4871 /*
4872 * If it's last chunk, must sync event list's tailfree too. Note
4873 * that delete_ok must NOT be passed as true if there could be
4874 * additional AfterTriggerEventList values pointing at this event
4875 * list, since we'd fail to fix their copies of tailfree.
4876 */
4877 if (chunk == events->tail)
4878 events->tailfree = chunk->freeptr;
4879 }
4880 }
4881 if (slot1 != NULL)
4882 {
4885 }
4886
4887 /* Release working resources */
4889
4890 if (local_estate)
4891 {
4893 ExecResetTupleTable(estate->es_tupleTable, false);
4894 FreeExecutorState(estate);
4895 }
4896
4897 return all_fired;
4898}

References AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AFTER_TRIGGER_TUP_BITS, AfterTriggerExecute(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, AfterTriggerEventData::ate_dst_part, AfterTriggerEventData::ate_flags, AfterTriggerEventData::ate_src_part, CHUNK_DATA_START, CreateExecutorState(), CurrentMemoryContext, AfterTriggerEventChunk::endfree, AfterTriggerEventChunk::endptr, EState::es_tupleTable, ExecCloseResultRelations(), ExecDropSingleTupleTableSlot(), ExecGetTriggerResultRel(), ExecResetTupleTable(), fb(), for_each_chunk, for_each_event, FreeExecutorState(), AfterTriggerEventChunk::freeptr, GetTriggerSharedData, MakeSingleTupleTableSlot(), MemoryContextDelete(), OidIsValid, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, RelationHasReferenceCountZero, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TTSOpsMinimalTuple.

Referenced by AfterTriggerEndQuery(), AfterTriggerFireDeferred(), and AfterTriggerSetState().

◆ AfterTriggerIsActive()

bool AfterTriggerIsActive ( void  )

Definition at line 6928 of file trigger.c.

6929{
6930 return afterTriggers.firing_depth > 0;
6931}

References afterTriggers, and AfterTriggersData::firing_depth.

Referenced by RI_FKey_check().

◆ afterTriggerMarkEvents()

static bool afterTriggerMarkEvents ( AfterTriggerEventList events,
AfterTriggerEventList move_list,
bool  immediate_only 
)
static

Definition at line 4661 of file trigger.c.

4664{
4665 bool found = false;
4666 bool deferred_found = false;
4667 AfterTriggerEvent event;
4669
4670 for_each_event_chunk(event, chunk, *events)
4671 {
4673 bool defer_it = false;
4674
4675 if (!(event->ate_flags &
4677 {
4678 /*
4679 * This trigger hasn't been called or scheduled yet. Check if we
4680 * should call it now.
4681 */
4683 {
4684 defer_it = true;
4685 }
4686 else
4687 {
4688 /*
4689 * Mark it as to be fired in this firing cycle.
4690 */
4691 evtshared->ats_firing_id = afterTriggers.firing_counter;
4692 event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4693 found = true;
4694 }
4695 }
4696
4697 /*
4698 * If it's deferred, move it to move_list, if requested.
4699 */
4700 if (defer_it && move_list != NULL)
4701 {
4702 deferred_found = true;
4703 /* add it to move_list */
4705 /* mark original copy "done" so we don't do it again */
4706 event->ate_flags |= AFTER_TRIGGER_DONE;
4707 }
4708 }
4709
4710 /*
4711 * We could allow deferred triggers if, before the end of the
4712 * security-restricted operation, we were to verify that a SET CONSTRAINTS
4713 * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4714 */
4716 ereport(ERROR,
4718 errmsg("cannot fire deferred trigger within security-restricted operation")));
4719
4720 return found;
4721}

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, afterTriggerAddEvent(), afterTriggerCheckState(), afterTriggers, AfterTriggerEventData::ate_flags, ereport, errcode(), errmsg, ERROR, fb(), AfterTriggersData::firing_counter, for_each_event_chunk, GetTriggerSharedData, and InSecurityRestrictedOperation().

Referenced by AfterTriggerEndQuery(), AfterTriggerFireDeferred(), and AfterTriggerSetState().

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

Definition at line 6175 of file trigger.c.

6176{
6177 AfterTriggerEvent event;
6179 int depth;
6180
6181 /* Scan queued events */
6183 {
6185
6186 /*
6187 * We can ignore completed events. (Even if a DONE flag is rolled
6188 * back by subxact abort, it's OK because the effects of the TRUNCATE
6189 * or whatever must get rolled back too.)
6190 */
6191 if (event->ate_flags & AFTER_TRIGGER_DONE)
6192 continue;
6193
6194 if (evtshared->ats_relid == relid)
6195 return true;
6196 }
6197
6198 /*
6199 * Also scan events queued by incomplete queries. This could only matter
6200 * if TRUNCATE/etc is executed by a function or trigger within an updating
6201 * query on the same relation, which is pretty perverse, but let's check.
6202 */
6203 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6204 {
6206 {
6208
6209 if (event->ate_flags & AFTER_TRIGGER_DONE)
6210 continue;
6211
6212 if (evtshared->ats_relid == relid)
6213 return true;
6214 }
6215 }
6216
6217 return false;
6218}

References AFTER_TRIGGER_DONE, afterTriggers, AfterTriggerEventData::ate_flags, AfterTriggersData::events, AfterTriggersQueryData::events, fb(), for_each_event_chunk, GetTriggerSharedData, AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.

Referenced by CheckTableNotInUse().

◆ afterTriggerRestoreEventList()

static void afterTriggerRestoreEventList ( AfterTriggerEventList events,
const AfterTriggerEventList old_events 
)
static

Definition at line 4273 of file trigger.c.

4275{
4278
4279 if (old_events->tail == NULL)
4280 {
4281 /* restoring to a completely empty state, so free everything */
4283 }
4284 else
4285 {
4286 *events = *old_events;
4287 /* free any chunks after the last one we want to keep */
4288 for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4289 {
4290 next_chunk = chunk->next;
4291 pfree(chunk);
4292 }
4293 /* and clean up the tail chunk to be the right length */
4294 events->tail->next = NULL;
4295 events->tail->freeptr = events->tailfree;
4296
4297 /*
4298 * We don't make any effort to remove now-unused shared data records.
4299 * They might still be useful, anyway.
4300 */
4301 }
4302}

References afterTriggerFreeEventList(), fb(), AfterTriggerEventChunk::freeptr, AfterTriggerEventChunk::next, pfree(), AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.

Referenced by AfterTriggerEndSubXact().

◆ AfterTriggerSaveEvent()

static void AfterTriggerSaveEvent ( EState estate,
ResultRelInfo relinfo,
ResultRelInfo src_partinfo,
ResultRelInfo dst_partinfo,
int  event,
bool  row_trigger,
TupleTableSlot oldslot,
TupleTableSlot newslot,
List recheckIndexes,
Bitmapset modifiedCols,
TransitionCaptureState transition_capture,
bool  is_crosspart_update 
)
static

Definition at line 6262 of file trigger.c.

6270{
6271 Relation rel = relinfo->ri_RelationDesc;
6272 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
6275 char relkind = rel->rd_rel->relkind;
6276 int tgtype_event;
6277 int tgtype_level;
6278 int i;
6279 Tuplestorestate *fdw_tuplestore = NULL;
6280
6281 /*
6282 * Check state. We use a normal test not Assert because it is possible to
6283 * reach here in the wrong state given misconfigured RI triggers, in
6284 * particular deferring a cascade action trigger.
6285 */
6286 if (afterTriggers.query_depth < 0)
6287 elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
6288
6289 /* Be sure we have enough space to record events at this query depth. */
6292
6293 /*
6294 * If the directly named relation has any triggers with transition tables,
6295 * then we need to capture transition tuples.
6296 */
6297 if (row_trigger && transition_capture != NULL)
6298 {
6300
6301 /*
6302 * Capture the old tuple in the appropriate transition table based on
6303 * the event.
6304 */
6305 if (!TupIsNull(oldslot))
6306 {
6307 Tuplestorestate *old_tuplestore;
6308
6309 old_tuplestore = GetAfterTriggersTransitionTable(event,
6310 oldslot,
6311 NULL,
6312 transition_capture);
6313 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6314 oldslot, NULL, old_tuplestore);
6315 }
6316
6317 /*
6318 * Capture the new tuple in the appropriate transition table based on
6319 * the event.
6320 */
6321 if (!TupIsNull(newslot))
6322 {
6323 Tuplestorestate *new_tuplestore;
6324
6325 new_tuplestore = GetAfterTriggersTransitionTable(event,
6326 NULL,
6327 newslot,
6328 transition_capture);
6329 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6330 newslot, original_insert_tuple, new_tuplestore);
6331 }
6332
6333 /*
6334 * If transition tables are the only reason we're here, return. As
6335 * mentioned above, we can also be here during update tuple routing in
6336 * presence of transition tables, in which case this function is
6337 * called separately for OLD and NEW, so we expect exactly one of them
6338 * to be NULL.
6339 */
6340 if (trigdesc == NULL ||
6341 (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
6342 (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
6343 (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
6345 return;
6346 }
6347
6348 /*
6349 * We normally don't see partitioned tables here for row level triggers
6350 * except in the special case of a cross-partition update. In that case,
6351 * nodeModifyTable.c:ExecCrossPartitionUpdateForeignKey() calls here to
6352 * queue an update event on the root target partitioned table, also
6353 * passing the source and destination partitions and their tuples.
6354 */
6356 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
6358 TRIGGER_FIRED_BY_UPDATE(event) &&
6360
6361 /*
6362 * Validate the event code and collect the associated tuple CTIDs.
6363 *
6364 * The event code will be used both as a bitmask and an array offset, so
6365 * validation is important to make sure we don't walk off the edge of our
6366 * arrays.
6367 *
6368 * Also, if we're considering statement-level triggers, check whether we
6369 * already queued a set of them for this event, and cancel the prior set
6370 * if so. This preserves the behavior that statement-level triggers fire
6371 * just once per statement and fire after row-level triggers.
6372 */
6373 switch (event)
6374 {
6377 if (row_trigger)
6378 {
6379 Assert(oldslot == NULL);
6380 Assert(newslot != NULL);
6381 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
6382 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6383 }
6384 else
6385 {
6386 Assert(oldslot == NULL);
6387 Assert(newslot == NULL);
6388 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6389 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6391 CMD_INSERT, event);
6392 }
6393 break;
6396 if (row_trigger)
6397 {
6398 Assert(oldslot != NULL);
6399 Assert(newslot == NULL);
6400 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6401 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6402 }
6403 else
6404 {
6405 Assert(oldslot == NULL);
6406 Assert(newslot == NULL);
6407 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6408 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6410 CMD_DELETE, event);
6411 }
6412 break;
6415 if (row_trigger)
6416 {
6417 Assert(oldslot != NULL);
6418 Assert(newslot != NULL);
6419 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6420 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
6421
6422 /*
6423 * Also remember the OIDs of partitions to fetch these tuples
6424 * out of later in AfterTriggerExecute().
6425 */
6426 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6427 {
6429 new_event.ate_src_part =
6430 RelationGetRelid(src_partinfo->ri_RelationDesc);
6431 new_event.ate_dst_part =
6432 RelationGetRelid(dst_partinfo->ri_RelationDesc);
6433 }
6434 }
6435 else
6436 {
6437 Assert(oldslot == NULL);
6438 Assert(newslot == NULL);
6439 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6440 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6442 CMD_UPDATE, event);
6443 }
6444 break;
6447 Assert(oldslot == NULL);
6448 Assert(newslot == NULL);
6449 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6450 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6451 break;
6452 default:
6453 elog(ERROR, "invalid after-trigger event code: %d", event);
6454 tgtype_event = 0; /* keep compiler quiet */
6455 break;
6456 }
6457
6458 /* Determine flags */
6459 if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
6460 {
6461 if (row_trigger && event == TRIGGER_EVENT_UPDATE)
6462 {
6463 if (relkind == RELKIND_PARTITIONED_TABLE)
6465 else
6466 new_event.ate_flags = AFTER_TRIGGER_2CTID;
6467 }
6468 else
6469 new_event.ate_flags = AFTER_TRIGGER_1CTID;
6470 }
6471
6472 /* else, we'll initialize ate_flags for each trigger */
6473
6475
6476 /*
6477 * Must convert/copy the source and destination partition tuples into the
6478 * root partitioned table's format/slot, because the processing in the
6479 * loop below expects both oldslot and newslot tuples to be in that form.
6480 */
6481 if (row_trigger && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6482 {
6484 TupleConversionMap *map;
6485
6488 if (map)
6490 oldslot,
6491 rootslot);
6492 else
6494
6497 if (map)
6499 newslot,
6500 rootslot);
6501 else
6503 }
6504
6505 for (i = 0; i < trigdesc->numtriggers; i++)
6506 {
6507 Trigger *trigger = &trigdesc->triggers[i];
6508
6509 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
6512 tgtype_event))
6513 continue;
6514 if (!TriggerEnabled(estate, relinfo, trigger, event,
6516 continue;
6517
6518 if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
6519 {
6520 if (fdw_tuplestore == NULL)
6521 {
6522 fdw_tuplestore = GetCurrentFDWTuplestore();
6524 }
6525 else
6526 /* subsequent event for the same tuple */
6528 }
6529
6530 /*
6531 * If the trigger is a foreign key enforcement trigger, there are
6532 * certain cases where we can skip queueing the event because we can
6533 * tell by inspection that the FK constraint will still pass. There
6534 * are also some cases during cross-partition updates of a partitioned
6535 * table where queuing the event can be skipped.
6536 */
6538 {
6539 switch (RI_FKey_trigger_type(trigger->tgfoid))
6540 {
6541 case RI_TRIGGER_PK:
6542
6543 /*
6544 * For cross-partitioned updates of partitioned PK table,
6545 * skip the event fired by the component delete on the
6546 * source leaf partition unless the constraint originates
6547 * in the partition itself (!tgisclone), because the
6548 * update event that will be fired on the root
6549 * (partitioned) target table will be used to perform the
6550 * necessary foreign key enforcement action.
6551 */
6552 if (is_crosspart_update &&
6553 TRIGGER_FIRED_BY_DELETE(event) &&
6554 trigger->tgisclone)
6555 continue;
6556
6557 /* Update or delete on trigger's PK table */
6559 oldslot, newslot))
6560 {
6561 /* skip queuing this event */
6562 continue;
6563 }
6564 break;
6565
6566 case RI_TRIGGER_FK:
6567
6568 /*
6569 * Update on trigger's FK table. We can skip the update
6570 * event fired on a partitioned table during a
6571 * cross-partition of that table, because the insert event
6572 * that is fired on the destination leaf partition would
6573 * suffice to perform the necessary foreign key check.
6574 * Moreover, RI_FKey_fk_upd_check_required() expects to be
6575 * passed a tuple that contains system attributes, most of
6576 * which are not present in the virtual slot belonging to
6577 * a partitioned table.
6578 */
6579 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
6581 oldslot, newslot))
6582 {
6583 /* skip queuing this event */
6584 continue;
6585 }
6586 break;
6587
6588 case RI_TRIGGER_NONE:
6589
6590 /*
6591 * Not an FK trigger. No need to queue the update event
6592 * fired during a cross-partitioned update of a
6593 * partitioned table, because the same row trigger must be
6594 * present in the leaf partition(s) that are affected as
6595 * part of this update and the events fired on them are
6596 * queued instead.
6597 */
6598 if (row_trigger &&
6599 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6600 continue;
6601 break;
6602 }
6603 }
6604
6605 /*
6606 * If the trigger is a deferred unique constraint check trigger, only
6607 * queue it if the unique constraint was potentially violated, which
6608 * we know from index insertion time.
6609 */
6610 if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
6611 {
6612 if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
6613 continue; /* Uniqueness definitely not violated */
6614 }
6615
6616 /*
6617 * Fill in event structure and add it to the current query's queue.
6618 * Note we set ats_table to NULL whenever this trigger doesn't use
6619 * transition tables, to improve sharability of the shared event data.
6620 */
6621 new_shared.ats_event =
6622 (event & TRIGGER_EVENT_OPMASK) |
6624 (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
6625 (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
6626 new_shared.ats_tgoid = trigger->tgoid;
6627 new_shared.ats_relid = RelationGetRelid(rel);
6628 new_shared.ats_rolid = GetUserId();
6629 new_shared.ats_firing_id = 0;
6630 if ((trigger->tgoldtable || trigger->tgnewtable) &&
6631 transition_capture != NULL)
6632 {
6633 switch (event)
6634 {
6636 new_shared.ats_table = transition_capture->tcs_insert_private;
6637 break;
6639 new_shared.ats_table = transition_capture->tcs_update_private;
6640 break;
6642 new_shared.ats_table = transition_capture->tcs_delete_private;
6643 break;
6644 default:
6645 /* Must be TRUNCATE, see switch above */
6646 new_shared.ats_table = NULL;
6647 break;
6648 }
6649 }
6650 else
6651 new_shared.ats_table = NULL;
6652 new_shared.ats_modifiedcols = modifiedCols;
6653
6656 }
6657
6658 /*
6659 * Finally, spool any foreign tuple(s). The tuplestore squashes them to
6660 * minimal tuples, so this loses any system columns. The executor lost
6661 * those columns before us, for an unrelated reason, so this is fine.
6662 */
6663 if (fdw_tuplestore)
6664 {
6665 if (oldslot != NULL)
6666 tuplestore_puttupleslot(fdw_tuplestore, oldslot);
6667 if (newslot != NULL)
6668 tuplestore_puttupleslot(fdw_tuplestore, newslot);
6669 }
6670}

References AFTER_TRIGGER_1CTID, AFTER_TRIGGER_2CTID, AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_INITDEFERRED, afterTriggerAddEvent(), AfterTriggerEnlargeQueryState(), afterTriggers, Assert, TupleConversionMap::attrMap, cancel_prior_stmt_triggers(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, AfterTriggersQueryData::events, ExecCopySlot(), ExecGetChildToRootMap(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), execute_attr_map_slot(), fb(), GetAfterTriggersTransitionTable(), GetCurrentFDWTuplestore(), GetUserId(), i, ItemPointerCopy(), ItemPointerSetInvalid(), list_member_oid(), AfterTriggersData::maxquerydepth, TriggerDesc::numtriggers, AfterTriggersData::query_depth, AfterTriggersData::query_stack, RelationData::rd_rel, RelationGetRelid, RI_FKey_fk_upd_check_required(), RI_FKey_pk_upd_check_required(), RI_FKey_trigger_type(), RI_TRIGGER_FK, RI_TRIGGER_NONE, RI_TRIGGER_PK, TransitionCaptureState::tcs_delete_private, TransitionCaptureState::tcs_insert_private, TransitionCaptureState::tcs_original_insert_tuple, TransitionCaptureState::tcs_update_private, TransitionTableAddTuple(), TriggerDesc::trig_delete_after_row, TriggerDesc::trig_insert_after_row, TriggerDesc::trig_update_after_row, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_TRUNCATE, TRIGGER_EVENT_UPDATE, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TupIsNull, and tuplestore_puttupleslot().

Referenced by ExecARDeleteTriggers(), ExecARInsertTriggers(), ExecARUpdateTriggers(), ExecASDeleteTriggers(), ExecASInsertTriggers(), ExecASTruncateTriggers(), and ExecASUpdateTriggers().

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5851 of file trigger.c.

5852{
5853 int my_level = GetCurrentTransactionNestLevel();
5854
5855 /* If we haven't already done so, initialize our state. */
5856 if (afterTriggers.state == NULL)
5858
5859 /*
5860 * If in a subtransaction, and we didn't save the current state already,
5861 * save it so it can be restored if the subtransaction aborts.
5862 */
5863 if (my_level > 1 &&
5864 afterTriggers.trans_stack[my_level].state == NULL)
5865 {
5866 afterTriggers.trans_stack[my_level].state =
5868 }
5869
5870 /*
5871 * Handle SET CONSTRAINTS ALL ...
5872 */
5873 if (stmt->constraints == NIL)
5874 {
5875 /*
5876 * Forget any previous SET CONSTRAINTS commands in this transaction.
5877 */
5879
5880 /*
5881 * Set the per-transaction ALL state to known.
5882 */
5885 }
5886 else
5887 {
5890 List *conoidlist = NIL;
5891 List *tgoidlist = NIL;
5892 ListCell *lc;
5893
5894 /*
5895 * Handle SET CONSTRAINTS constraint-name [, ...]
5896 *
5897 * First, identify all the named constraints and make a list of their
5898 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5899 * the same name within a schema, the specifications are not
5900 * necessarily unique. Our strategy is to target all matching
5901 * constraints within the first search-path schema that has any
5902 * matches, but disregard matches in schemas beyond the first match.
5903 * (This is a bit odd but it's the historical behavior.)
5904 *
5905 * A constraint in a partitioned table may have corresponding
5906 * constraints in the partitions. Grab those too.
5907 */
5909
5910 foreach(lc, stmt->constraints)
5911 {
5912 RangeVar *constraint = lfirst(lc);
5913 bool found;
5915 ListCell *nslc;
5916
5917 if (constraint->catalogname)
5918 {
5919 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5920 ereport(ERROR,
5922 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5923 constraint->catalogname, constraint->schemaname,
5924 constraint->relname)));
5925 }
5926
5927 /*
5928 * If we're given the schema name with the constraint, look only
5929 * in that schema. If given a bare constraint name, use the
5930 * search path to find the first matching constraint.
5931 */
5932 if (constraint->schemaname)
5933 {
5935 false);
5936
5938 }
5939 else
5940 {
5942 }
5943
5944 found = false;
5945 foreach(nslc, namespacelist)
5946 {
5949 ScanKeyData skey[2];
5950 HeapTuple tup;
5951
5952 ScanKeyInit(&skey[0],
5955 CStringGetDatum(constraint->relname));
5956 ScanKeyInit(&skey[1],
5960
5962 true, NULL, 2, skey);
5963
5965 {
5967
5968 if (con->condeferrable)
5969 conoidlist = lappend_oid(conoidlist, con->oid);
5970 else if (stmt->deferred)
5971 ereport(ERROR,
5973 errmsg("constraint \"%s\" is not deferrable",
5974 constraint->relname)));
5975 found = true;
5976 }
5977
5979
5980 /*
5981 * Once we've found a matching constraint we do not search
5982 * later parts of the search path.
5983 */
5984 if (found)
5985 break;
5986 }
5987
5989
5990 /*
5991 * Not found ?
5992 */
5993 if (!found)
5994 ereport(ERROR,
5996 errmsg("constraint \"%s\" does not exist",
5997 constraint->relname)));
5998 }
5999
6000 /*
6001 * Scan for any possible descendants of the constraints. We append
6002 * whatever we find to the same list that we're scanning; this has the
6003 * effect that we create new scans for those, too, so if there are
6004 * further descendents, we'll also catch them.
6005 */
6006 foreach(lc, conoidlist)
6007 {
6008 Oid parent = lfirst_oid(lc);
6010 SysScanDesc scan;
6011 HeapTuple tuple;
6012
6013 ScanKeyInit(&key,
6016 ObjectIdGetDatum(parent));
6017
6018 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
6019
6020 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
6021 {
6023
6024 conoidlist = lappend_oid(conoidlist, con->oid);
6025 }
6026
6027 systable_endscan(scan);
6028 }
6029
6031
6032 /*
6033 * Now, locate the trigger(s) implementing each of these constraints,
6034 * and make a list of their OIDs.
6035 */
6037
6038 foreach(lc, conoidlist)
6039 {
6040 Oid conoid = lfirst_oid(lc);
6043 HeapTuple htup;
6044
6048 ObjectIdGetDatum(conoid));
6049
6051 NULL, 1, &skey);
6052
6054 {
6056
6057 /*
6058 * Silently skip triggers that are marked as non-deferrable in
6059 * pg_trigger. This is not an error condition, since a
6060 * deferrable RI constraint may have some non-deferrable
6061 * actions.
6062 */
6063 if (pg_trigger->tgdeferrable)
6065 }
6066
6068 }
6069
6071
6072 /*
6073 * Now we can set the trigger states of individual triggers for this
6074 * xact.
6075 */
6076 foreach(lc, tgoidlist)
6077 {
6078 Oid tgoid = lfirst_oid(lc);
6080 bool found = false;
6081 int i;
6082
6083 for (i = 0; i < state->numstates; i++)
6084 {
6085 if (state->trigstates[i].sct_tgoid == tgoid)
6086 {
6087 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
6088 found = true;
6089 break;
6090 }
6091 }
6092 if (!found)
6093 {
6095 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
6096 }
6097 }
6098 }
6099
6100 /*
6101 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
6102 * checks against that constraint must be made when the SET CONSTRAINTS
6103 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
6104 * apply retroactively. We've updated the constraints state, so scan the
6105 * list of previously deferred events to fire any that have now become
6106 * immediate.
6107 *
6108 * Obviously, if this was SET ... DEFERRED then it can't have converted
6109 * any unfired events to immediate, so we need do nothing in that case.
6110 */
6111 if (!stmt->deferred)
6112 {
6114 bool snapshot_set = false;
6115
6117 while (afterTriggerMarkEvents(events, NULL, true))
6118 {
6120
6121 /*
6122 * Make sure a snapshot has been established in case trigger
6123 * functions need one. Note that we avoid setting a snapshot if
6124 * we don't find at least one trigger that has to be fired now.
6125 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6126 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6127 * at the start of a transaction it's not possible for any trigger
6128 * events to be queued yet.)
6129 */
6130 if (!snapshot_set)
6131 {
6133 snapshot_set = true;
6134 }
6135
6136 /*
6137 * We can delete fired events if we are at top transaction level,
6138 * but we'd better not if inside a subtransaction, since the
6139 * subtransaction could later get rolled back.
6140 */
6142 !IsSubTransaction()))
6143 break; /* all fired */
6144 }
6145
6146 /*
6147 * Flush any fast-path batches accumulated by the triggers just fired.
6148 */
6153
6154 if (snapshot_set)
6156 }
6157}

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, AfterTriggersData::batch_callbacks, BTEqualStrategyNumber, RangeVar::catalogname, CStringGetDatum(), ereport, errcode(), errmsg, ERROR, AfterTriggersData::events, fb(), fetch_search_path(), FireAfterTriggerBatchCallbacks(), AfterTriggersData::firing_counter, AfterTriggersData::firing_depth, Form_pg_constraint, Form_pg_trigger, get_database_name(), GetCurrentTransactionNestLevel(), GETSTRUCT(), GetTransactionSnapshot(), HeapTupleIsValid, i, IsSubTransaction(), lappend_oid(), lfirst, lfirst_oid, list_free(), list_free_deep(), list_make1_oid, LookupExplicitNamespace(), MyDatabaseId, NIL, SetConstraintStateData::numstates, ObjectIdGetDatum(), PopActiveSnapshot(), PushActiveSnapshot(), RangeVar::relname, ScanKeyInit(), RangeVar::schemaname, SetConstraintStateAddItem(), SetConstraintStateCopy(), SetConstraintStateCreate(), AfterTriggersData::state, AfterTriggersTransData::state, stmt, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and AfterTriggersData::trans_stack.

Referenced by standard_ProcessUtility().

◆ assign_session_replication_role()

void assign_session_replication_role ( int  newval,
void extra 
)

Definition at line 6798 of file trigger.c.

6799{
6800 /*
6801 * Must flush the plan cache when changing replication role; but don't
6802 * flush unnecessarily.
6803 */
6806}

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6677 of file trigger.c.

6678{
6679 bool result;
6681
6682 /* Check state, like AfterTriggerSaveEvent. */
6683 if (afterTriggers.query_depth < 0)
6684 elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6685
6686 /* Be sure we have enough space to record events at this query depth. */
6689
6690 /*
6691 * We keep this state in the AfterTriggersTableData that also holds
6692 * transition tables for the relation + operation. In this way, if we are
6693 * forced to make a new set of transition tables because more tuples get
6694 * entered after we've already fired triggers, we will allow a new set of
6695 * statement triggers to get queued.
6696 */
6697 table = GetAfterTriggersTableData(relid, cmdType);
6698 result = table->before_trig_done;
6699 table->before_trig_done = true;
6700 return result;
6701}

References AfterTriggerEnlargeQueryState(), afterTriggers, elog, ERROR, GetAfterTriggersTableData(), AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, result, and table.

Referenced by ExecBSDeleteTriggers(), ExecBSInsertTriggers(), and ExecBSUpdateTriggers().

◆ cancel_prior_stmt_triggers()

static void cancel_prior_stmt_triggers ( Oid  relid,
CmdType  cmdType,
int  tgevent 
)
static

Definition at line 6723 of file trigger.c.

6724{
6727
6728 /*
6729 * We keep this state in the AfterTriggersTableData that also holds
6730 * transition tables for the relation + operation. In this way, if we are
6731 * forced to make a new set of transition tables because more tuples get
6732 * entered after we've already fired triggers, we will allow a new set of
6733 * statement triggers to get queued without canceling the old ones.
6734 */
6735 table = GetAfterTriggersTableData(relid, cmdType);
6736
6737 if (table->after_trig_done)
6738 {
6739 /*
6740 * We want to start scanning from the tail location that existed just
6741 * before we inserted any statement triggers. But the events list
6742 * might've been entirely empty then, in which case scan from the
6743 * current head.
6744 */
6745 AfterTriggerEvent event;
6747
6748 if (table->after_trig_events.tail)
6749 {
6750 chunk = table->after_trig_events.tail;
6751 event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6752 }
6753 else
6754 {
6755 chunk = qs->events.head;
6756 event = NULL;
6757 }
6758
6759 for_each_chunk_from(chunk)
6760 {
6761 if (event == NULL)
6762 event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6763 for_each_event_from(event, chunk)
6764 {
6766
6767 /*
6768 * Exit loop when we reach events that aren't AS triggers for
6769 * the target relation.
6770 */
6771 if (evtshared->ats_relid != relid)
6772 goto done;
6773 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6774 goto done;
6775 if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6776 goto done;
6777 if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6778 goto done;
6779 /* OK, mark it DONE */
6780 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6781 event->ate_flags |= AFTER_TRIGGER_DONE;
6782 }
6783 /* signal we must reinitialize event ptr for next chunk */
6784 event = NULL;
6785 }
6786 }
6787done:
6788
6789 /* In any case, save current insertion point for next time */
6790 table->after_trig_done = true;
6791 table->after_trig_events = qs->events;
6792}

References AFTER_TRIGGER_DONE, afterTriggers, CHUNK_DATA_START, fb(), for_each_chunk_from, for_each_event_from, GetAfterTriggersTableData(), GetTriggerSharedData, AfterTriggersData::query_depth, AfterTriggersData::query_stack, table, TRIGGER_EVENT_OPMASK, TRIGGER_FIRED_AFTER, and TRIGGER_FIRED_FOR_STATEMENT.

Referenced by AfterTriggerSaveEvent().

◆ check_modified_virtual_generated()

static HeapTuple check_modified_virtual_generated ( TupleDesc  tupdesc,
HeapTuple  tuple 
)
static

Definition at line 6828 of file trigger.c.

6829{
6830 if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
6831 return tuple;
6832
6833 for (int i = 0; i < tupdesc->natts; i++)
6834 {
6835 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6836 {
6837 if (!heap_attisnull(tuple, i + 1, tupdesc))
6838 {
6839 int replCol = i + 1;
6840 Datum replValue = 0;
6841 bool replIsnull = true;
6842
6843 tuple = heap_modify_tuple_by_cols(tuple, tupdesc, 1, &replCol, &replValue, &replIsnull);
6844 }
6845 }
6846 }
6847
6848 return tuple;
6849}

References TupleDescData::constr, fb(), TupleConstr::has_generated_virtual, heap_attisnull(), heap_modify_tuple_by_cols(), i, TupleDescData::natts, and TupleDescAttr().

Referenced by ExecBRInsertTriggers(), and ExecBRUpdateTriggers().

◆ CopyTriggerDesc()

TriggerDesc * CopyTriggerDesc ( TriggerDesc trigdesc)

Definition at line 2117 of file trigger.c.

2118{
2121 int i;
2122
2123 if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2124 return NULL;
2125
2127 memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2128
2129 trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2130 memcpy(trigger, trigdesc->triggers,
2131 trigdesc->numtriggers * sizeof(Trigger));
2132 newdesc->triggers = trigger;
2133
2134 for (i = 0; i < trigdesc->numtriggers; i++)
2135 {
2136 trigger->tgname = pstrdup(trigger->tgname);
2137 if (trigger->tgnattr > 0)
2138 {
2139 int16 *newattr;
2140
2141 newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2142 memcpy(newattr, trigger->tgattr,
2143 trigger->tgnattr * sizeof(int16));
2144 trigger->tgattr = newattr;
2145 }
2146 if (trigger->tgnargs > 0)
2147 {
2148 char **newargs;
2149 int16 j;
2150
2151 newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2152 for (j = 0; j < trigger->tgnargs; j++)
2153 newargs[j] = pstrdup(trigger->tgargs[j]);
2154 trigger->tgargs = newargs;
2155 }
2156 if (trigger->tgqual)
2157 trigger->tgqual = pstrdup(trigger->tgqual);
2158 if (trigger->tgoldtable)
2159 trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2160 if (trigger->tgnewtable)
2161 trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2162 trigger++;
2163 }
2164
2165 return newdesc;
2166}
int16_t int16
Definition c.h:678
#define palloc_object(type)
Definition fe_memutils.h:89
int j
Definition isn.c:78
char * pstrdup(const char *in)
Definition mcxt.c:1910

References fb(), i, j, memcpy(), TriggerDesc::numtriggers, palloc(), palloc_object, pstrdup(), and TriggerDesc::triggers.

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

◆ CreateTrigger()

ObjectAddress CreateTrigger ( const CreateTrigStmt stmt,
const char queryString,
Oid  relOid,
Oid  refRelOid,
Oid  constraintOid,
Oid  indexOid,
Oid  funcoid,
Oid  parentTriggerOid,
Node whenClause,
bool  isInternal,
bool  in_partition 
)

Definition at line 162 of file trigger.c.

166{
167 return
168 CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
169 constraintOid, indexOid, funcoid,
170 parentTriggerOid, whenClause, isInternal,
172}
ObjectAddress CreateTriggerFiringOn(const CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition trigger.c:179
#define TRIGGER_FIRES_ON_ORIGIN
Definition trigger.h:151

References CreateTriggerFiringOn(), fb(), stmt, and TRIGGER_FIRES_ON_ORIGIN.

Referenced by CreateFKCheckTrigger(), createForeignKeyActionTriggers(), index_constraint_create(), and ProcessUtilitySlow().

◆ CreateTriggerFiringOn()

ObjectAddress CreateTriggerFiringOn ( const CreateTrigStmt stmt,
const char queryString,
Oid  relOid,
Oid  refRelOid,
Oid  constraintOid,
Oid  indexOid,
Oid  funcoid,
Oid  parentTriggerOid,
Node whenClause,
bool  isInternal,
bool  in_partition,
char  trigger_fires_when 
)

Definition at line 179 of file trigger.c.

184{
185 int16 tgtype;
186 int ncolumns;
187 int16 *columns;
188 int2vector *tgattr;
190 char *qual;
192 bool nulls[Natts_pg_trigger];
193 Relation rel;
197 HeapTuple tuple = NULL;
201 char *trigname;
205 char *oldtablename = NULL;
206 char *newtablename = NULL;
208 bool trigger_exists = false;
210 bool existing_isInternal = false;
211 bool existing_isClone = false;
212
213 if (OidIsValid(relOid))
214 rel = table_open(relOid, ShareRowExclusiveLock);
215 else
216 rel = table_openrv(stmt->relation, ShareRowExclusiveLock);
217
218 /*
219 * Triggers must be on tables or views, and there are additional
220 * relation-type-specific restrictions.
221 */
222 if (rel->rd_rel->relkind == RELKIND_RELATION)
223 {
224 /* Tables can't have INSTEAD OF triggers */
225 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
226 stmt->timing != TRIGGER_TYPE_AFTER)
229 errmsg("\"%s\" is a table",
231 errdetail("Tables cannot have INSTEAD OF triggers.")));
232 }
233 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
234 {
235 /* Partitioned tables can't have INSTEAD OF triggers */
236 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
237 stmt->timing != TRIGGER_TYPE_AFTER)
240 errmsg("\"%s\" is a table",
242 errdetail("Tables cannot have INSTEAD OF triggers.")));
243
244 /*
245 * FOR EACH ROW triggers have further restrictions
246 */
247 if (stmt->row)
248 {
249 /*
250 * Disallow use of transition tables.
251 *
252 * Note that we have another restriction about transition tables
253 * in partitions; search for 'has_superclass' below for an
254 * explanation. The check here is just to protect from the fact
255 * that if we allowed it here, the creation would succeed for a
256 * partitioned table with no partitions, but would be blocked by
257 * the other restriction when the first partition was created,
258 * which is very unfriendly behavior.
259 */
260 if (stmt->transitionRels != NIL)
263 errmsg("\"%s\" is a partitioned table",
265 errdetail("ROW triggers with transition tables are not supported on partitioned tables.")));
266 }
267 }
268 else if (rel->rd_rel->relkind == RELKIND_VIEW)
269 {
270 /*
271 * Views can have INSTEAD OF triggers (which we check below are
272 * row-level), or statement-level BEFORE/AFTER triggers.
273 */
274 if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row)
277 errmsg("\"%s\" is a view",
279 errdetail("Views cannot have row-level BEFORE or AFTER triggers.")));
280 /* Disallow TRUNCATE triggers on VIEWs */
281 if (TRIGGER_FOR_TRUNCATE(stmt->events))
284 errmsg("\"%s\" is a view",
286 errdetail("Views cannot have TRUNCATE triggers.")));
287 }
288 else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
289 {
290 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
291 stmt->timing != TRIGGER_TYPE_AFTER)
294 errmsg("\"%s\" is a foreign table",
296 errdetail("Foreign tables cannot have INSTEAD OF triggers.")));
297
298 /*
299 * We disallow constraint triggers to protect the assumption that
300 * triggers on FKs can't be deferred. See notes with AfterTriggers
301 * data structures, below.
302 */
303 if (stmt->isconstraint)
306 errmsg("\"%s\" is a foreign table",
308 errdetail("Foreign tables cannot have constraint triggers.")));
309 }
310 else
313 errmsg("relation \"%s\" cannot have triggers",
316
317 /*
318 * Conflict log tables are used internally for logical replication
319 * conflict logging and should not have triggers, as it could disrupt
320 * conflict logging.
321 */
325 errmsg("cannot create trigger on conflict log table \"%s\"",
327 errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
328
332 errmsg("permission denied: \"%s\" is a system catalog",
334
335 if (stmt->isconstraint)
336 {
337 /*
338 * We must take a lock on the target relation to protect against
339 * concurrent drop. It's not clear that AccessShareLock is strong
340 * enough, but we certainly need at least that much... otherwise, we
341 * might end up creating a pg_constraint entry referencing a
342 * nonexistent table.
343 */
345 {
348 }
349 else if (stmt->constrrel != NULL)
351 false);
352 }
353
354 /* permission checks */
355 if (!isInternal)
356 {
359 if (aclresult != ACLCHECK_OK)
362
364 {
367 if (aclresult != ACLCHECK_OK)
370 }
371 }
372
373 /*
374 * When called on a partitioned table to create a FOR EACH ROW trigger
375 * that's not internal, we create one trigger for each partition, too.
376 *
377 * For that, we'd better hold lock on all of them ahead of time.
378 */
379 partition_recurse = !isInternal && stmt->row &&
380 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
384
385 /* Compute tgtype */
386 TRIGGER_CLEAR_TYPE(tgtype);
387 if (stmt->row)
388 TRIGGER_SETT_ROW(tgtype);
389 tgtype |= stmt->timing;
390 tgtype |= stmt->events;
391
392 /* Disallow ROW-level TRUNCATE triggers */
393 if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
396 errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
397
398 /* INSTEAD triggers must be row-level, and can't have WHEN or columns */
399 if (TRIGGER_FOR_INSTEAD(tgtype))
400 {
401 if (!TRIGGER_FOR_ROW(tgtype))
404 errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
405 if (stmt->whenClause)
408 errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
409 if (stmt->columns != NIL)
412 errmsg("INSTEAD OF triggers cannot have column lists")));
413 }
414
415 /*
416 * We don't yet support naming ROW transition variables, but the parser
417 * recognizes the syntax so we can give a nicer message here.
418 *
419 * Per standard, REFERENCING TABLE names are only allowed on AFTER
420 * triggers. Per standard, REFERENCING ROW names are not allowed with FOR
421 * EACH STATEMENT. Per standard, each OLD/NEW, ROW/TABLE permutation is
422 * only allowed once. Per standard, OLD may not be specified when
423 * creating a trigger only for INSERT, and NEW may not be specified when
424 * creating a trigger only for DELETE.
425 *
426 * Notice that the standard allows an AFTER ... FOR EACH ROW trigger to
427 * reference both ROW and TABLE transition data.
428 */
429 if (stmt->transitionRels != NIL)
430 {
431 List *varList = stmt->transitionRels;
432 ListCell *lc;
433
434 foreach(lc, varList)
435 {
437
438 if (!(tt->isTable))
441 errmsg("ROW variable naming in the REFERENCING clause is not supported"),
442 errhint("Use OLD TABLE or NEW TABLE for naming transition tables.")));
443
444 /*
445 * Because of the above test, we omit further ROW-related testing
446 * below. If we later allow naming OLD and NEW ROW variables,
447 * adjustments will be needed below.
448 */
449
450 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
453 errmsg("\"%s\" is a foreign table",
455 errdetail("Triggers on foreign tables cannot have transition tables.")));
456
457 if (rel->rd_rel->relkind == RELKIND_VIEW)
460 errmsg("\"%s\" is a view",
462 errdetail("Triggers on views cannot have transition tables.")));
463
464 /*
465 * We currently don't allow row-level triggers with transition
466 * tables on partition or inheritance children. Such triggers
467 * would somehow need to see tuples converted to the format of the
468 * table they're attached to, and it's not clear which subset of
469 * tuples each child should see. See also the prohibitions in
470 * ATExecAttachPartition() and ATExecAddInherit().
471 */
472 if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id))
473 {
474 /* Use appropriate error message. */
475 if (rel->rd_rel->relispartition)
478 errmsg("ROW triggers with transition tables are not supported on partitions")));
479 else
482 errmsg("ROW triggers with transition tables are not supported on inheritance children")));
483 }
484
485 if (stmt->timing != TRIGGER_TYPE_AFTER)
488 errmsg("transition table name can only be specified for an AFTER trigger")));
489
490 if (TRIGGER_FOR_TRUNCATE(tgtype))
493 errmsg("TRUNCATE triggers with transition tables are not supported")));
494
495 /*
496 * We currently don't allow multi-event triggers ("INSERT OR
497 * UPDATE") with transition tables, because it's not clear how to
498 * handle INSERT ... ON CONFLICT statements which can fire both
499 * INSERT and UPDATE triggers. We show the inserted tuples to
500 * INSERT triggers and the updated tuples to UPDATE triggers, but
501 * it's not yet clear what INSERT OR UPDATE trigger should see.
502 * This restriction could be lifted if we can decide on the right
503 * semantics in a later release.
504 */
505 if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) +
506 (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) +
507 (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1)
510 errmsg("transition tables cannot be specified for triggers with more than one event")));
511
512 /*
513 * We currently don't allow column-specific triggers with
514 * transition tables. Per spec, that seems to require
515 * accumulating separate transition tables for each combination of
516 * columns, which is a lot of work for a rather marginal feature.
517 */
518 if (stmt->columns != NIL)
521 errmsg("transition tables cannot be specified for triggers with column lists")));
522
523 /*
524 * We disallow constraint triggers with transition tables, to
525 * protect the assumption that such triggers can't be deferred.
526 * See notes with AfterTriggers data structures, below.
527 *
528 * Currently this is enforced by the grammar, so just Assert here.
529 */
530 Assert(!stmt->isconstraint);
531
532 if (tt->isNew)
533 {
534 if (!(TRIGGER_FOR_INSERT(tgtype) ||
535 TRIGGER_FOR_UPDATE(tgtype)))
538 errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger")));
539
540 if (newtablename != NULL)
543 errmsg("NEW TABLE cannot be specified multiple times")));
544
545 newtablename = tt->name;
546 }
547 else
548 {
549 if (!(TRIGGER_FOR_DELETE(tgtype) ||
550 TRIGGER_FOR_UPDATE(tgtype)))
553 errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger")));
554
555 if (oldtablename != NULL)
558 errmsg("OLD TABLE cannot be specified multiple times")));
559
560 oldtablename = tt->name;
561 }
562 }
563
564 if (newtablename != NULL && oldtablename != NULL &&
568 errmsg("OLD TABLE name and NEW TABLE name cannot be the same")));
569 }
570
571 /*
572 * Parse the WHEN clause, if any and we weren't passed an already
573 * transformed one.
574 *
575 * Note that as a side effect, we fill whenRtable when parsing. If we got
576 * an already parsed clause, this does not occur, which is what we want --
577 * no point in adding redundant dependencies below.
578 */
579 if (!whenClause && stmt->whenClause)
580 {
581 ParseState *pstate;
583 List *varList;
584 ListCell *lc;
585
586 /* Set up a pstate to parse with */
587 pstate = make_parsestate(NULL);
588 pstate->p_sourcetext = queryString;
589
590 /*
591 * Set up nsitems for OLD and NEW references.
592 *
593 * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
594 */
597 makeAlias("old", NIL),
598 false, false);
599 addNSItemToQuery(pstate, nsitem, false, true, true);
602 makeAlias("new", NIL),
603 false, false);
604 addNSItemToQuery(pstate, nsitem, false, true, true);
605
606 /* Transform expression. Copy to be sure we don't modify original */
607 whenClause = transformWhereClause(pstate,
608 copyObject(stmt->whenClause),
610 "WHEN");
611 /* we have to fix its collations too */
612 assign_expr_collations(pstate, whenClause);
613
614 /*
615 * Check for disallowed references to OLD/NEW.
616 *
617 * NB: pull_var_clause is okay here only because we don't allow
618 * subselects in WHEN clauses; it would fail to examine the contents
619 * of subselects.
620 */
621 varList = pull_var_clause(whenClause, 0);
622 foreach(lc, varList)
623 {
624 Var *var = (Var *) lfirst(lc);
625
626 switch (var->varno)
627 {
628 case PRS2_OLD_VARNO:
629 if (!TRIGGER_FOR_ROW(tgtype))
632 errmsg("statement trigger's WHEN condition cannot reference column values"),
633 parser_errposition(pstate, var->location)));
634 if (TRIGGER_FOR_INSERT(tgtype))
637 errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
638 parser_errposition(pstate, var->location)));
639 /* system columns are okay here */
640 break;
641 case PRS2_NEW_VARNO:
642 if (!TRIGGER_FOR_ROW(tgtype))
645 errmsg("statement trigger's WHEN condition cannot reference column values"),
646 parser_errposition(pstate, var->location)));
647 if (TRIGGER_FOR_DELETE(tgtype))
650 errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
651 parser_errposition(pstate, var->location)));
652 if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
655 errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
656 parser_errposition(pstate, var->location)));
657 if (TRIGGER_FOR_BEFORE(tgtype) &&
658 var->varattno == 0 &&
659 RelationGetDescr(rel)->constr &&
660 (RelationGetDescr(rel)->constr->has_generated_stored ||
661 RelationGetDescr(rel)->constr->has_generated_virtual))
664 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
665 errdetail("A whole-row reference is used and the table contains generated columns."),
666 parser_errposition(pstate, var->location)));
667 if (TRIGGER_FOR_BEFORE(tgtype) &&
668 var->varattno > 0 &&
669 TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attgenerated)
672 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
673 errdetail("Column \"%s\" is a generated column.",
674 NameStr(TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attname)),
675 parser_errposition(pstate, var->location)));
676 break;
677 default:
678 /* can't happen without add_missing_from, so just elog */
679 elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
680 break;
681 }
682 }
683
684 /* we'll need the rtable for recordDependencyOnExpr */
685 whenRtable = pstate->p_rtable;
686
687 qual = nodeToString(whenClause);
688
689 free_parsestate(pstate);
690 }
691 else if (!whenClause)
692 {
693 whenClause = NULL;
694 whenRtable = NIL;
695 qual = NULL;
696 }
697 else
698 {
699 qual = nodeToString(whenClause);
700 whenRtable = NIL;
701 }
702
703 /*
704 * Find and validate the trigger function.
705 */
706 if (!OidIsValid(funcoid))
707 funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
708 if (!isInternal)
709 {
711 if (aclresult != ACLCHECK_OK)
713 NameListToString(stmt->funcname));
714 }
716 if (funcrettype != TRIGGEROID)
719 errmsg("function %s must return type %s",
720 NameListToString(stmt->funcname), "trigger")));
721
722 /*
723 * Scan pg_trigger to see if there is already a trigger of the same name.
724 * Skip this for internally generated triggers, since we'll modify the
725 * name to be unique below.
726 *
727 * NOTE that this is cool only because we have ShareRowExclusiveLock on
728 * the relation, so the trigger set won't be changing underneath us.
729 */
731 if (!isInternal)
732 {
735
736 ScanKeyInit(&skeys[0],
740
741 ScanKeyInit(&skeys[1],
744 CStringGetDatum(stmt->trigname));
745
747 NULL, 2, skeys);
748
749 /* There should be at most one matching tuple */
751 {
753
754 trigoid = oldtrigger->oid;
755 existing_constraint_oid = oldtrigger->tgconstraint;
756 existing_isInternal = oldtrigger->tgisinternal;
758 trigger_exists = true;
759 /* copy the tuple to use in CatalogTupleUpdate() */
760 tuple = heap_copytuple(tuple);
761 }
763 }
764
765 if (!trigger_exists)
766 {
767 /* Generate the OID for the new trigger. */
770 }
771 else
772 {
773 /*
774 * If OR REPLACE was specified, we'll replace the old trigger;
775 * otherwise complain about the duplicate name.
776 */
777 if (!stmt->replace)
780 errmsg("trigger \"%s\" for relation \"%s\" already exists",
781 stmt->trigname, RelationGetRelationName(rel))));
782
783 /*
784 * An internal trigger or a child trigger (isClone) cannot be replaced
785 * by a user-defined trigger. However, skip this test when
786 * in_partition, because then we're recursing from a partitioned table
787 * and the check was made at the parent level.
788 */
793 errmsg("trigger \"%s\" for relation \"%s\" is an internal or a child trigger",
794 stmt->trigname, RelationGetRelationName(rel))));
795
796 /*
797 * It is not allowed to replace with a constraint trigger; gram.y
798 * should have enforced this already.
799 */
800 Assert(!stmt->isconstraint);
801
802 /*
803 * It is not allowed to replace an existing constraint trigger,
804 * either. (The reason for these restrictions is partly that it seems
805 * difficult to deal with pending trigger events in such cases, and
806 * partly that the command might imply changing the constraint's
807 * properties as well, which doesn't seem nice.)
808 */
812 errmsg("trigger \"%s\" for relation \"%s\" is a constraint trigger",
813 stmt->trigname, RelationGetRelationName(rel))));
814 }
815
816 /*
817 * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
818 * corresponding pg_constraint entry.
819 */
820 if (stmt->isconstraint && !OidIsValid(constraintOid))
821 {
822 /* Internal callers should have made their own constraints */
827 stmt->deferrable,
828 stmt->initdeferred,
829 true, /* Is Enforced */
830 true,
831 InvalidOid, /* no parent */
832 RelationGetRelid(rel),
833 NULL, /* no conkey */
834 0,
835 0,
836 InvalidOid, /* no domain */
837 InvalidOid, /* no index */
838 InvalidOid, /* no foreign key */
839 NULL,
840 NULL,
841 NULL,
842 NULL,
843 0,
844 ' ',
845 ' ',
846 NULL,
847 0,
848 ' ',
849 NULL, /* no exclusion */
850 NULL, /* no check constraint */
851 NULL,
852 true, /* islocal */
853 0, /* inhcount */
854 true, /* noinherit */
855 false, /* conperiod */
856 isInternal); /* is_internal */
857 }
858
859 /*
860 * If trigger is internally generated, modify the provided trigger name to
861 * ensure uniqueness by appending the trigger OID. (Callers will usually
862 * supply a simple constant trigger name in these cases.)
863 */
864 if (isInternal)
865 {
867 "%s_%u", stmt->trigname, trigoid);
868 trigname = internaltrigname;
869 }
870 else
871 {
872 /* user-defined trigger; use the specified trigger name as-is */
873 trigname = stmt->trigname;
874 }
875
876 /*
877 * Build the new pg_trigger tuple.
878 */
879 memset(nulls, false, sizeof(nulls));
880
885 CStringGetDatum(trigname));
895
896 if (stmt->args)
897 {
898 ListCell *le;
899 char *args;
900 int16 nargs = list_length(stmt->args);
901 int len = 0;
902
903 foreach(le, stmt->args)
904 {
905 char *ar = strVal(lfirst(le));
906
907 len += strlen(ar) + 4;
908 for (; *ar; ar++)
909 {
910 if (*ar == '\\')
911 len++;
912 }
913 }
914 args = (char *) palloc(len + 1);
915 args[0] = '\0';
916 foreach(le, stmt->args)
917 {
918 char *s = strVal(lfirst(le));
919 char *d = args + strlen(args);
920
921 while (*s)
922 {
923 if (*s == '\\')
924 *d++ = '\\';
925 *d++ = *s++;
926 }
927 strcpy(d, "\\000");
928 }
931 CStringGetDatum(args));
932 }
933 else
934 {
937 CStringGetDatum(""));
938 }
939
940 /* build column number array if it's a column-specific trigger */
941 ncolumns = list_length(stmt->columns);
942 if (ncolumns == 0)
943 columns = NULL;
944 else
945 {
946 ListCell *cell;
947 int i = 0;
948
949 columns = (int16 *) palloc(ncolumns * sizeof(int16));
950 foreach(cell, stmt->columns)
951 {
952 char *name = strVal(lfirst(cell));
954 int j;
955
956 /* Lookup column name. System columns are not allowed */
957 attnum = attnameAttNum(rel, name, false);
961 errmsg("column \"%s\" of relation \"%s\" does not exist",
963
964 /* Check for duplicates */
965 for (j = i - 1; j >= 0; j--)
966 {
967 if (columns[j] == attnum)
970 errmsg("column \"%s\" specified more than once",
971 name)));
972 }
973
974 columns[i++] = attnum;
975 }
976 }
977 tgattr = buildint2vector(columns, ncolumns);
979
980 /* set tgqual if trigger has WHEN clause */
981 if (qual)
983 else
984 nulls[Anum_pg_trigger_tgqual - 1] = true;
985
986 if (oldtablename)
989 else
990 nulls[Anum_pg_trigger_tgoldtable - 1] = true;
991 if (newtablename)
994 else
995 nulls[Anum_pg_trigger_tgnewtable - 1] = true;
996
997 /*
998 * Insert or replace tuple in pg_trigger.
999 */
1000 if (!trigger_exists)
1001 {
1002 tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
1003 CatalogTupleInsert(tgrel, tuple);
1004 }
1005 else
1006 {
1008
1009 newtup = heap_form_tuple(tgrel->rd_att, values, nulls);
1012 }
1013
1014 heap_freetuple(tuple); /* free either original or new tuple */
1016
1020 if (oldtablename)
1022 if (newtablename)
1024
1025 /*
1026 * Update relation's pg_class entry; if necessary; and if not, send an SI
1027 * message to make other backends (and this one) rebuild relcache entries.
1028 */
1032 if (!HeapTupleIsValid(tuple))
1033 elog(ERROR, "cache lookup failed for relation %u",
1034 RelationGetRelid(rel));
1035 if (!((Form_pg_class) GETSTRUCT(tuple))->relhastriggers)
1036 {
1037 ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
1038
1039 CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
1040
1042 }
1043 else
1045
1046 heap_freetuple(tuple);
1048
1049 /*
1050 * If we're replacing a trigger, flush all the old dependencies before
1051 * recording new ones.
1052 */
1053 if (trigger_exists)
1055
1056 /*
1057 * Record dependencies for trigger. Always place a normal dependency on
1058 * the function.
1059 */
1060 myself.classId = TriggerRelationId;
1061 myself.objectId = trigoid;
1062 myself.objectSubId = 0;
1063
1065 referenced.objectId = funcoid;
1066 referenced.objectSubId = 0;
1068
1070 {
1071 /*
1072 * Internally-generated trigger for a constraint, so make it an
1073 * internal dependency of the constraint. We can skip depending on
1074 * the relation(s), as there'll be an indirect dependency via the
1075 * constraint.
1076 */
1078 referenced.objectId = constraintOid;
1079 referenced.objectSubId = 0;
1081 }
1082 else
1083 {
1084 /*
1085 * User CREATE TRIGGER, so place dependencies. We make trigger be
1086 * auto-dropped if its relation is dropped or if the FK relation is
1087 * dropped. (Auto drop is compatible with our pre-7.3 behavior.)
1088 */
1090 referenced.objectId = RelationGetRelid(rel);
1091 referenced.objectSubId = 0;
1093
1095 {
1097 referenced.objectId = constrrelid;
1098 referenced.objectSubId = 0;
1100 }
1101 /* Not possible to have an index dependency in this case */
1102 Assert(!OidIsValid(indexOid));
1103
1104 /*
1105 * If it's a user-specified constraint trigger, make the constraint
1106 * internally dependent on the trigger instead of vice versa.
1107 */
1109 {
1111 referenced.objectId = constraintOid;
1112 referenced.objectSubId = 0;
1114 }
1115
1116 /*
1117 * If it's a partition trigger, create the partition dependencies.
1118 */
1120 {
1125 }
1126 }
1127
1128 /* If column-specific trigger, add normal dependencies on columns */
1129 if (columns != NULL)
1130 {
1131 int i;
1132
1134 referenced.objectId = RelationGetRelid(rel);
1135 for (i = 0; i < ncolumns; i++)
1136 {
1137 referenced.objectSubId = columns[i];
1139 }
1140 }
1141
1142 /*
1143 * If it has a WHEN clause, add dependencies on objects mentioned in the
1144 * expression (eg, functions, as well as any columns used).
1145 */
1146 if (whenRtable != NIL)
1149
1150 /* Post creation hook for new trigger */
1152 isInternal);
1153
1154 /*
1155 * Lastly, create the trigger on child relations, if needed.
1156 */
1158 {
1159 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1160 int i;
1163
1165 "part trig clone",
1167
1168 /*
1169 * We don't currently expect to be called with a valid indexOid. If
1170 * that ever changes then we'll need to write code here to find the
1171 * corresponding child index.
1172 */
1173 Assert(!OidIsValid(indexOid));
1174
1176
1177 /* Iterate to create the trigger on each existing partition */
1178 for (i = 0; i < partdesc->nparts; i++)
1179 {
1182 Node *qual;
1183
1185
1186 /*
1187 * Initialize our fabricated parse node by copying the original
1188 * one, then resetting fields that we pass separately.
1189 */
1191 childStmt->funcname = NIL;
1192 childStmt->whenClause = NULL;
1193
1194 /* If there is a WHEN clause, create a modified copy of it */
1195 qual = copyObject(whenClause);
1196 qual = (Node *)
1198 childTbl, rel);
1199 qual = (Node *)
1201 childTbl, rel);
1202
1203 CreateTriggerFiringOn(childStmt, queryString,
1204 partdesc->oids[i], refRelOid,
1206 funcoid, trigoid, qual,
1208
1210
1212 }
1213
1216 }
1217
1218 /* Keep lock on target rel until end of xact */
1219 table_close(rel, NoLock);
1220
1221 return myself;
1222}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2672
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3902
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4105
#define InvalidAttrNumber
Definition attnum.h:23
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define CStringGetTextDatum(s)
Definition builtins.h:98
Datum byteain(PG_FUNCTION_ARGS)
Definition bytea.c:201
#define NameStr(name)
Definition c.h:894
bool IsConflictLogTableClass(Form_pg_class reltuple)
Definition catalog.c:242
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:475
void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior)
@ DEPENDENCY_AUTO
Definition dependency.h:34
@ DEPENDENCY_INTERNAL
Definition dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition dependency.h:37
@ DEPENDENCY_NORMAL
Definition dependency.h:33
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:688
bool allowSystemTableMods
Definition globals.c:132
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:686
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition indexing.c:233
int2vector * buildint2vector(const int16 *int2s, int n)
Definition int.c:114
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition inval.c:1666
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:107
#define NoLock
Definition lockdefs.h:34
#define ShareRowExclusiveLock
Definition lockdefs.h:41
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_rel_name(Oid relid)
Definition lsyscache.c:2242
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2317
Oid get_func_rettype(Oid funcid)
Definition lsyscache.c:1969
Alias * makeAlias(const char *aliasname, List *colnames)
Definition makefuncs.c:438
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
Datum namein(PG_FUNCTION_ARGS)
Definition name.c:48
char * NameListToString(const List *names)
Definition namespace.c:3666
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition namespace.h:98
#define copyObject(obj)
Definition nodes.h:230
#define InvokeObjectPostCreateHookArg(classId, objectId, subId, is_internal)
ObjectType get_relkind_objtype(char relkind)
#define ObjectAddressSet(addr, class_id, object_id)
char * nodeToString(const void *obj)
Definition outfuncs.c:811
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
void assign_expr_collations(ParseState *pstate, Node *expr)
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
void free_parsestate(ParseState *pstate)
Definition parse_node.c:72
int parser_errposition(ParseState *pstate, int location)
Definition parse_node.c:106
ParseState * make_parsestate(ParseState *parentParseState)
Definition parse_node.c:39
@ EXPR_KIND_TRIGGER_WHEN
Definition parse_node.h:78
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, LOCKMODE lockmode, Alias *alias, bool inh, bool inFromCl)
int attnameAttNum(Relation rd, const char *attname, bool sysColOK)
@ OBJECT_FUNCTION
#define ACL_EXECUTE
Definition parsenodes.h:83
#define ACL_TRIGGER
Definition parsenodes.h:82
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition partdesc.c:71
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition partition.c:222
int16 attnum
int errdetail_relkind_not_supported(char relkind)
Definition pg_class.c:24
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
#define NAMEDATALEN
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
const void size_t len
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:51
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition pg_depend.c:314
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
bool has_superclass(Oid relationId)
#define lfirst_node(type, lc)
Definition pg_list.h:176
static int list_length(const List *l)
Definition pg_list.h:152
#define snprintf
Definition port.h:261
static Datum Int16GetDatum(int16 X)
Definition postgres.h:172
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
#define PointerGetDatum(X)
Definition postgres.h:354
static Datum CharGetDatum(char X)
Definition postgres.h:132
#define InvalidOid
#define PRS2_OLD_VARNO
Definition primnodes.h:251
#define PRS2_NEW_VARNO
Definition primnodes.h:252
#define RelationGetDescr(relation)
Definition rel.h:542
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationGetNamespace(relation)
Definition rel.h:557
#define ERRCODE_DUPLICATE_OBJECT
Definition streamutil.c:30
ItemPointerData t_self
Definition htup.h:65
Definition nodes.h:133
const char * p_sourcetext
Definition parse_node.h:214
List * p_rtable
Definition parse_node.h:215
Oid rd_id
Definition rel.h:113
ParseLoc location
Definition primnodes.h:311
AttrNumber varattno
Definition primnodes.h:275
int varno
Definition primnodes.h:270
#define SearchSysCacheCopy1(cacheId, key1)
Definition syscache.h:91
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition table.c:83
#define strVal(v)
Definition value.h:82
List * pull_var_clause(Node *node, int flags)
Definition var.c:653
const char * name
void CommandCounterIncrement(void)
Definition xact.c:1130

References AccessShareLock, ACL_EXECUTE, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addNSItemToQuery(), addRangeTableEntryForRelation(), ALLOCSET_SMALL_SIZES, AllocSetContextCreate, allowSystemTableMods, Assert, assign_expr_collations(), attnameAttNum(), attnum, BoolGetDatum(), BTEqualStrategyNumber, buildint2vector(), byteain(), CacheInvalidateRelcacheByTuple(), CatalogTupleInsert(), CatalogTupleUpdate(), CharGetDatum(), CommandCounterIncrement(), copyObject, CreateConstraintEntry(), CreateTriggerFiringOn(), CStringGetDatum(), CStringGetTextDatum, CurrentMemoryContext, DatumGetPointer(), deleteDependencyRecordsFor(), DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, DirectFunctionCall1, elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errdetail_relkind_not_supported(), errhint(), errmsg, ERROR, EXPR_KIND_TRIGGER_WHEN, fb(), find_all_inheritors(), Form_pg_trigger, free_parsestate(), get_func_rettype(), get_rel_name(), get_rel_relkind(), get_relkind_objtype(), GetNewOidWithIndex(), GETSTRUCT(), GetUserId(), has_superclass(), heap_copytuple(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, Int16GetDatum(), InvalidAttrNumber, InvalidOid, InvokeObjectPostCreateHookArg, IsConflictLogTableClass(), IsSystemRelation(), j, len, lfirst, lfirst_node, list_free(), list_length(), Var::location, LockRelationOid(), LookupFuncName(), make_parsestate(), makeAlias(), map_partition_varattnos(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), name, NAMEDATALEN, namein(), NameListToString(), NameStr, NIL, nodeToString(), NoLock, PartitionDescData::nparts, object_aclcheck(), OBJECT_FUNCTION, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, ParseState::p_rtable, ParseState::p_sourcetext, palloc(), parser_errposition(), pfree(), pg_class_aclcheck(), PointerGetDatum, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pull_var_clause(), RangeVarGetRelid, RelationData::rd_id, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnExpr(), RelationGetDescr, RelationGetNamespace, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, ShareRowExclusiveLock, snprintf, stmt, strVal, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), table_openrv(), transformWhereClause(), TupleDescAttr(), values, Var::varattno, and Var::varno.

Referenced by CloneRowTriggersToPartition(), CreateTrigger(), and CreateTriggerFiringOn().

◆ EnableDisableTrigger()

void EnableDisableTrigger ( Relation  rel,
const char tgname,
Oid  tgparent,
char  fires_when,
bool  skip_system,
bool  recurse,
LOCKMODE  lockmode 
)

Definition at line 1753 of file trigger.c.

1756{
1758 int nkeys;
1759 ScanKeyData keys[2];
1761 HeapTuple tuple;
1762 bool found;
1763 bool changed;
1764
1765 /* Scan the relevant entries in pg_triggers */
1767
1768 ScanKeyInit(&keys[0],
1772 if (tgname)
1773 {
1774 ScanKeyInit(&keys[1],
1777 CStringGetDatum(tgname));
1778 nkeys = 2;
1779 }
1780 else
1781 nkeys = 1;
1782
1784 NULL, nkeys, keys);
1785
1786 found = changed = false;
1787
1788 while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1789 {
1791
1792 if (OidIsValid(tgparent) && tgparent != oldtrig->tgparentid)
1793 continue;
1794
1795 if (oldtrig->tgisinternal)
1796 {
1797 /* system trigger ... ok to process? */
1798 if (skip_system)
1799 continue;
1800 if (!superuser())
1801 ereport(ERROR,
1803 errmsg("permission denied: \"%s\" is a system trigger",
1804 NameStr(oldtrig->tgname))));
1805 }
1806
1807 found = true;
1808
1809 if (oldtrig->tgenabled != fires_when)
1810 {
1811 /* need to change this one ... make a copy to scribble on */
1814
1815 newtrig->tgenabled = fires_when;
1816
1818
1820
1821 changed = true;
1822 }
1823
1824 /*
1825 * When altering FOR EACH ROW triggers on a partitioned table, do the
1826 * same on the partitions as well, unless ONLY is specified.
1827 *
1828 * Note that we recurse even if we didn't change the trigger above,
1829 * because the partitions' copy of the trigger may have a different
1830 * value of tgenabled than the parent's trigger and thus might need to
1831 * be changed.
1832 */
1833 if (recurse &&
1834 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
1835 (TRIGGER_FOR_ROW(oldtrig->tgtype)))
1836 {
1837 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1838 int i;
1839
1840 for (i = 0; i < partdesc->nparts; i++)
1841 {
1842 Relation part;
1843
1844 part = relation_open(partdesc->oids[i], lockmode);
1845 /* Match on child triggers' tgparentid, not their name */
1847 fires_when, skip_system, recurse,
1848 lockmode);
1849 table_close(part, NoLock); /* keep lock till commit */
1850 }
1851 }
1852
1854 oldtrig->oid, 0);
1855 }
1856
1858
1860
1861 if (tgname && !found)
1862 ereport(ERROR,
1864 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1865 tgname, RelationGetRelationName(rel))));
1866
1867 /*
1868 * If we changed anything, broadcast a SI inval message to force each
1869 * backend (including our own!) to rebuild relation's relcache entry.
1870 * Otherwise they will fail to apply the change promptly.
1871 */
1872 if (changed)
1874}
void CacheInvalidateRelcache(Relation relation)
Definition inval.c:1632
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
bool superuser(void)
Definition superuser.c:47
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition trigger.c:1753

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), CStringGetDatum(), EnableDisableTrigger(), ereport, errcode(), errmsg, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, i, InvokeObjectPostAlterHook, NameStr, NoLock, PartitionDescData::nparts, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, RelationData::rd_rel, relation_open(), RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), superuser(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecEnableDisableTrigger(), and EnableDisableTrigger().

◆ ExecARDeleteTriggers()

void ExecARDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TransitionCaptureState transition_capture,
bool  is_crosspart_update 
)

Definition at line 2828 of file trigger.c.

2834{
2835 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2836
2837 if (relinfo->ri_FdwRoutine && transition_capture &&
2838 transition_capture->tcs_delete_old_table)
2839 {
2840 Assert(relinfo->ri_RootResultRelInfo);
2841 ereport(ERROR,
2843 errmsg("cannot collect transition tuples from child foreign tables")));
2844 }
2845
2846 if ((trigdesc && trigdesc->trig_delete_after_row) ||
2847 (transition_capture && transition_capture->tcs_delete_old_table))
2848 {
2850
2852 if (fdw_trigtuple == NULL)
2853 GetTupleForTrigger(estate,
2854 NULL,
2855 relinfo,
2856 tupleid,
2858 slot,
2859 false,
2860 NULL,
2861 NULL,
2862 NULL);
2863 else
2865
2868 true, slot, NULL, NIL, NULL,
2869 transition_capture,
2871 }
2872}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
@ LockTupleExclusive
Definition lockoptions.h:59
static bool GetTupleForTrigger(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, bool do_epq_recheck, TupleTableSlot **epqslot, TM_Result *tmresultp, TM_FailureData *tmfdp)
Definition trigger.c:3371

References AfterTriggerSaveEvent(), Assert, ereport, errcode(), errmsg, ERROR, ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), fb(), GetTupleForTrigger(), HeapTupleIsValid, ItemPointerIsValid(), LockTupleExclusive, NIL, TransitionCaptureState::tcs_delete_old_table, TriggerDesc::trig_delete_after_row, and TRIGGER_EVENT_DELETE.

Referenced by ExecDeleteEpilogue(), and ExecSimpleRelationDelete().

◆ ExecARInsertTriggers()

void ExecARInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot,
List recheckIndexes,
TransitionCaptureState transition_capture 
)

Definition at line 2570 of file trigger.c.

2573{
2574 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2575
2576 if (relinfo->ri_FdwRoutine && transition_capture &&
2577 transition_capture->tcs_insert_new_table)
2578 {
2579 Assert(relinfo->ri_RootResultRelInfo);
2580 ereport(ERROR,
2582 errmsg("cannot collect transition tuples from child foreign tables")));
2583 }
2584
2585 if ((trigdesc && trigdesc->trig_insert_after_row) ||
2586 (transition_capture && transition_capture->tcs_insert_new_table))
2589 true, NULL, slot,
2591 transition_capture,
2592 false);
2593}

References AfterTriggerSaveEvent(), Assert, ereport, errcode(), errmsg, ERROR, fb(), TransitionCaptureState::tcs_insert_new_table, TriggerDesc::trig_insert_after_row, and TRIGGER_EVENT_INSERT.

Referenced by CopyFrom(), CopyMultiInsertBufferFlush(), ExecBatchInsert(), ExecInsert(), and ExecSimpleRelationInsert().

◆ ExecARUpdateTriggers()

void ExecARUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
ResultRelInfo src_partinfo,
ResultRelInfo dst_partinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot newslot,
List recheckIndexes,
TransitionCaptureState transition_capture,
bool  is_crosspart_update 
)

Definition at line 3171 of file trigger.c.

3180{
3181 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3182
3183 if (relinfo->ri_FdwRoutine && transition_capture &&
3184 (transition_capture->tcs_update_old_table ||
3185 transition_capture->tcs_update_new_table))
3186 {
3187 Assert(relinfo->ri_RootResultRelInfo);
3188 ereport(ERROR,
3190 errmsg("cannot collect transition tuples from child foreign tables")));
3191 }
3192
3193 if ((trigdesc && trigdesc->trig_update_after_row) ||
3194 (transition_capture &&
3195 (transition_capture->tcs_update_old_table ||
3196 transition_capture->tcs_update_new_table)))
3197 {
3198 /*
3199 * Note: if the UPDATE is converted into a DELETE+INSERT as part of
3200 * update-partition-key operation, then this function is also called
3201 * separately for DELETE and INSERT to capture transition table rows.
3202 * In such case, either old tuple or new tuple can be NULL.
3203 */
3206
3209
3212
3214 GetTupleForTrigger(estate,
3215 NULL,
3216 tupsrc,
3217 tupleid,
3219 oldslot,
3220 false,
3221 NULL,
3222 NULL,
3223 NULL);
3224 else if (fdw_trigtuple != NULL)
3226 else
3228
3232 true,
3235 transition_capture,
3237 }
3238}
Bitmapset * ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition execUtils.c:1444

References AfterTriggerSaveEvent(), Assert, ereport, errcode(), errmsg, ERROR, ExecClearTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), fb(), GetTupleForTrigger(), ItemPointerIsValid(), LockTupleExclusive, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, TriggerDesc::trig_update_after_row, and TRIGGER_EVENT_UPDATE.

Referenced by ExecCrossPartitionUpdateForeignKey(), ExecDeleteEpilogue(), ExecInsert(), ExecSimpleRelationUpdate(), and ExecUpdateEpilogue().

◆ ExecASDeleteTriggers()

void ExecASDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)

Definition at line 2708 of file trigger.c.

2710{
2711 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2712
2713 if (trigdesc && trigdesc->trig_delete_after_statement)
2716 false, NULL, NULL, NIL, NULL, transition_capture,
2717 false);
2718}
bool trig_delete_after_statement
Definition reltrigger.h:70

References AfterTriggerSaveEvent(), fb(), NIL, TriggerDesc::trig_delete_after_statement, and TRIGGER_EVENT_DELETE.

Referenced by fireASTriggers().

◆ ExecASInsertTriggers()

void ExecASInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)

Definition at line 2479 of file trigger.c.

2481{
2482 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2483
2484 if (trigdesc && trigdesc->trig_insert_after_statement)
2487 false, NULL, NULL, NIL, NULL, transition_capture,
2488 false);
2489}
bool trig_insert_after_statement
Definition reltrigger.h:60

References AfterTriggerSaveEvent(), fb(), NIL, TriggerDesc::trig_insert_after_statement, and TRIGGER_EVENT_INSERT.

Referenced by CopyFrom(), and fireASTriggers().

◆ ExecASTruncateTriggers()

void ExecASTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 3354 of file trigger.c.

3355{
3356 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3357
3358 if (trigdesc && trigdesc->trig_truncate_after_statement)
3360 NULL, NULL,
3362 false, NULL, NULL, NIL, NULL, NULL,
3363 false);
3364}
bool trig_truncate_after_statement
Definition reltrigger.h:73

References AfterTriggerSaveEvent(), fb(), NIL, TriggerDesc::trig_truncate_after_statement, and TRIGGER_EVENT_TRUNCATE.

Referenced by ExecuteTruncateGuts().

◆ ExecASUpdateTriggers()

void ExecASUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)

Definition at line 2980 of file trigger.c.

2982{
2983 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2984
2985 /* statement-level triggers operate on the parent table */
2986 Assert(relinfo->ri_RootResultRelInfo == NULL);
2987
2988 if (trigdesc && trigdesc->trig_update_after_statement)
2991 false, NULL, NULL, NIL,
2993 transition_capture,
2994 false);
2995}
bool trig_update_after_statement
Definition reltrigger.h:65

References AfterTriggerSaveEvent(), Assert, ExecGetAllUpdatedCols(), fb(), NIL, TriggerDesc::trig_update_after_statement, and TRIGGER_EVENT_UPDATE.

Referenced by fireASTriggers().

◆ ExecBRDeleteTriggers()

bool ExecBRDeleteTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot **  epqslot,
TM_Result tmresult,
TM_FailureData tmfd,
bool  is_merge_delete 
)

Definition at line 2728 of file trigger.c.

2736{
2738 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2739 bool result = true;
2742 bool should_free = false;
2743 int i;
2744
2746 if (fdw_trigtuple == NULL)
2747 {
2749
2750 /*
2751 * Get a copy of the on-disk tuple we are planning to delete. In
2752 * general, if the tuple has been concurrently updated, we should
2753 * recheck it using EPQ. However, if this is a MERGE DELETE action,
2754 * we skip this EPQ recheck and leave it to the caller (it must do
2755 * additional rechecking, and might end up executing a different
2756 * action entirely).
2757 */
2758 if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2760 &epqslot_candidate, tmresult, tmfd))
2761 return false;
2762
2763 /*
2764 * If the tuple was concurrently updated and the caller of this
2765 * function requested for the updated tuple, skip the trigger
2766 * execution.
2767 */
2768 if (epqslot_candidate != NULL && epqslot != NULL)
2769 {
2771 return false;
2772 }
2773
2775 }
2776 else
2777 {
2779 ExecForceStoreHeapTuple(trigtuple, slot, false);
2780 }
2781
2786 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2787 for (i = 0; i < trigdesc->numtriggers; i++)
2788 {
2789 HeapTuple newtuple;
2790 Trigger *trigger = &trigdesc->triggers[i];
2791
2792 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2796 continue;
2797 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2798 NULL, slot, NULL))
2799 continue;
2800
2801 LocTriggerData.tg_trigslot = slot;
2802 LocTriggerData.tg_trigtuple = trigtuple;
2803 LocTriggerData.tg_trigger = trigger;
2805 i,
2806 relinfo->ri_TrigFunctions,
2807 relinfo->ri_TrigInstrument,
2808 GetPerTupleMemoryContext(estate));
2809 if (newtuple == NULL)
2810 {
2811 result = false; /* tell caller to suppress delete */
2812 break;
2813 }
2814 if (newtuple != trigtuple)
2815 heap_freetuple(newtuple);
2816 }
2817 if (should_free)
2819
2820 return result;
2821}
#define GetPerTupleMemoryContext(estate)
Definition executor.h:670
#define TRIGGER_EVENT_BEFORE
Definition trigger.h:102

References Assert, ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), fb(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), HeapTupleIsValid, i, ItemPointerIsValid(), LockTupleExclusive, TriggerDesc::numtriggers, result, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_ROW, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by ExecDeletePrologue(), and ExecSimpleRelationDelete().

◆ ExecBRInsertTriggers()

bool ExecBRInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot 
)

Definition at line 2492 of file trigger.c.

2494{
2495 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2496 HeapTuple newtuple = NULL;
2497 bool should_free;
2499 int i;
2500
2505 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2506 for (i = 0; i < trigdesc->numtriggers; i++)
2507 {
2508 Trigger *trigger = &trigdesc->triggers[i];
2509 HeapTuple oldtuple;
2510
2511 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2515 continue;
2516 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2517 NULL, NULL, slot))
2518 continue;
2519
2520 if (!newtuple)
2521 newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2522
2523 LocTriggerData.tg_trigslot = slot;
2524 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2525 LocTriggerData.tg_trigger = trigger;
2527 i,
2528 relinfo->ri_TrigFunctions,
2529 relinfo->ri_TrigInstrument,
2530 GetPerTupleMemoryContext(estate));
2531 if (newtuple == NULL)
2532 {
2533 if (should_free)
2534 heap_freetuple(oldtuple);
2535 return false; /* "do nothing" */
2536 }
2537 else if (newtuple != oldtuple)
2538 {
2539 newtuple = check_modified_virtual_generated(RelationGetDescr(relinfo->ri_RelationDesc), newtuple);
2540
2541 ExecForceStoreHeapTuple(newtuple, slot, false);
2542
2543 /*
2544 * After a tuple in a partition goes through a trigger, the user
2545 * could have changed the partition key enough that the tuple no
2546 * longer fits the partition. Verify that.
2547 */
2548 if (trigger->tgisclone &&
2549 !ExecPartitionCheck(relinfo, slot, estate, false))
2550 ereport(ERROR,
2552 errmsg("moving row to another partition during a BEFORE FOR EACH ROW trigger is not supported"),
2553 errdetail("Before executing trigger \"%s\", the row was to be in partition \"%s.%s\".",
2554 trigger->tgname,
2556 RelationGetRelationName(relinfo->ri_RelationDesc))));
2557
2558 if (should_free)
2559 heap_freetuple(oldtuple);
2560
2561 /* signal tuple should be re-fetched if used */
2562 newtuple = NULL;
2563 }
2564 }
2565
2566 return true;
2567}
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition execMain.c:1922
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
NodeTag type
Definition trigger.h:33

References check_modified_virtual_generated(), ereport, errcode(), errdetail(), errmsg, ERROR, ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecPartitionCheck(), fb(), get_namespace_name(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, RelationGetDescr, RelationGetNamespace, RelationGetRelationName, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by CopyFrom(), ExecInsert(), and ExecSimpleRelationInsert().

◆ ExecBRUpdateTriggers()

bool ExecBRUpdateTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot newslot,
TM_Result tmresult,
TM_FailureData tmfd,
bool  is_merge_update 
)

Definition at line 2998 of file trigger.c.

3006{
3007 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3009 HeapTuple newtuple = NULL;
3011 bool should_free_trig = false;
3012 bool should_free_new = false;
3014 int i;
3015 Bitmapset *updatedCols;
3016 LockTupleMode lockmode;
3017
3018 /* Determine lock mode to use */
3019 lockmode = ExecUpdateLockMode(estate, relinfo);
3020
3022 if (fdw_trigtuple == NULL)
3023 {
3025
3026 /*
3027 * Get a copy of the on-disk tuple we are planning to update. In
3028 * general, if the tuple has been concurrently updated, we should
3029 * recheck it using EPQ. However, if this is a MERGE UPDATE action,
3030 * we skip this EPQ recheck and leave it to the caller (it must do
3031 * additional rechecking, and might end up executing a different
3032 * action entirely).
3033 */
3034 if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
3035 lockmode, oldslot, !is_merge_update,
3036 &epqslot_candidate, tmresult, tmfd))
3037 return false; /* cancel the update action */
3038
3039 /*
3040 * In READ COMMITTED isolation level it's possible that target tuple
3041 * was changed due to concurrent update. In that case we have a raw
3042 * subplan output tuple in epqslot_candidate, and need to form a new
3043 * insertable tuple using ExecGetUpdateNewTuple to replace the one we
3044 * received in newslot. Neither we nor our callers have any further
3045 * interest in the passed-in tuple, so it's okay to overwrite newslot
3046 * with the newer data.
3047 */
3048 if (epqslot_candidate != NULL)
3049 {
3051
3053 oldslot);
3054
3055 /*
3056 * Typically, the caller's newslot was also generated by
3057 * ExecGetUpdateNewTuple, so that epqslot_clean will be the same
3058 * slot and copying is not needed. But do the right thing if it
3059 * isn't.
3060 */
3063
3064 /*
3065 * At this point newslot contains a virtual tuple that may
3066 * reference some fields of oldslot's tuple in some disk buffer.
3067 * If that tuple is in a different page than the original target
3068 * tuple, then our only pin on that buffer is oldslot's, and we're
3069 * about to release it. Hence we'd better materialize newslot to
3070 * ensure it doesn't contain references into an unpinned buffer.
3071 * (We'd materialize it below anyway, but too late for safety.)
3072 */
3074 }
3075
3076 /*
3077 * Here we convert oldslot to a materialized slot holding trigtuple.
3078 * Neither slot passed to the triggers will hold any buffer pin.
3079 */
3081 }
3082 else
3083 {
3084 /* Put the FDW-supplied tuple into oldslot to unify the cases */
3087 }
3088
3093 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3094 updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
3095 LocTriggerData.tg_updatedcols = updatedCols;
3096 for (i = 0; i < trigdesc->numtriggers; i++)
3097 {
3098 Trigger *trigger = &trigdesc->triggers[i];
3099 HeapTuple oldtuple;
3100
3101 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3105 continue;
3106 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3107 updatedCols, oldslot, newslot))
3108 continue;
3109
3110 if (!newtuple)
3112
3113 LocTriggerData.tg_trigslot = oldslot;
3114 LocTriggerData.tg_trigtuple = trigtuple;
3115 LocTriggerData.tg_newtuple = oldtuple = newtuple;
3116 LocTriggerData.tg_newslot = newslot;
3117 LocTriggerData.tg_trigger = trigger;
3119 i,
3120 relinfo->ri_TrigFunctions,
3121 relinfo->ri_TrigInstrument,
3122 GetPerTupleMemoryContext(estate));
3123
3124 if (newtuple == NULL)
3125 {
3126 if (should_free_trig)
3128 if (should_free_new)
3129 heap_freetuple(oldtuple);
3130 return false; /* "do nothing" */
3131 }
3132 else if (newtuple != oldtuple)
3133 {
3134 newtuple = check_modified_virtual_generated(RelationGetDescr(relinfo->ri_RelationDesc), newtuple);
3135
3136 ExecForceStoreHeapTuple(newtuple, newslot, false);
3137
3138 /*
3139 * If the tuple returned by the trigger / being stored, is the old
3140 * row version, and the heap tuple passed to the trigger was
3141 * allocated locally, materialize the slot. Otherwise we might
3142 * free it while still referenced by the slot.
3143 */
3144 if (should_free_trig && newtuple == trigtuple)
3146
3147 if (should_free_new)
3148 heap_freetuple(oldtuple);
3149
3150 /* signal tuple should be re-fetched if used */
3151 newtuple = NULL;
3152 }
3153 }
3154 if (should_free_trig)
3156
3157 return true;
3158}
#define unlikely(x)
Definition c.h:497
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition execMain.c:2596
LockTupleMode
Definition lockoptions.h:51
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition tuptable.h:495

References Assert, check_modified_virtual_generated(), ExecCallTriggerFunc(), ExecCopySlot(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), ExecGetUpdateNewTuple(), ExecMaterializeSlot(), ExecUpdateLockMode(), fb(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), HeapTupleIsValid, i, ItemPointerIsValid(), TriggerDesc::numtriggers, RelationGetDescr, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and unlikely.

Referenced by ExecSimpleRelationUpdate(), and ExecUpdatePrologue().

◆ ExecBSDeleteTriggers()

void ExecBSDeleteTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2657 of file trigger.c.

2658{
2659 TriggerDesc *trigdesc;
2660 int i;
2662
2663 trigdesc = relinfo->ri_TrigDesc;
2664
2665 if (trigdesc == NULL)
2666 return;
2667 if (!trigdesc->trig_delete_before_statement)
2668 return;
2669
2670 /* no-op if we already fired BS triggers in this context */
2672 CMD_DELETE))
2673 return;
2674
2678 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2679 for (i = 0; i < trigdesc->numtriggers; i++)
2680 {
2681 Trigger *trigger = &trigdesc->triggers[i];
2682 HeapTuple newtuple;
2683
2684 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2688 continue;
2689 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2690 NULL, NULL, NULL))
2691 continue;
2692
2693 LocTriggerData.tg_trigger = trigger;
2695 i,
2696 relinfo->ri_TrigFunctions,
2697 relinfo->ri_TrigInstrument,
2698 GetPerTupleMemoryContext(estate));
2699
2700 if (newtuple)
2701 ereport(ERROR,
2703 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2704 }
2705}
bool trig_delete_before_statement
Definition reltrigger.h:69

References before_stmt_triggers_fired(), CMD_DELETE, ereport, errcode(), errmsg, ERROR, ExecCallTriggerFunc(), fb(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, TriggerDesc::trig_delete_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_DELETE, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by fireBSTriggers().

◆ ExecBSInsertTriggers()

void ExecBSInsertTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2428 of file trigger.c.

2429{
2430 TriggerDesc *trigdesc;
2431 int i;
2433
2434 trigdesc = relinfo->ri_TrigDesc;
2435
2436 if (trigdesc == NULL)
2437 return;
2438 if (!trigdesc->trig_insert_before_statement)
2439 return;
2440
2441 /* no-op if we already fired BS triggers in this context */
2443 CMD_INSERT))
2444 return;
2445
2449 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2450 for (i = 0; i < trigdesc->numtriggers; i++)
2451 {
2452 Trigger *trigger = &trigdesc->triggers[i];
2453 HeapTuple newtuple;
2454
2455 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2459 continue;
2460 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2461 NULL, NULL, NULL))
2462 continue;
2463
2464 LocTriggerData.tg_trigger = trigger;
2466 i,
2467 relinfo->ri_TrigFunctions,
2468 relinfo->ri_TrigInstrument,
2469 GetPerTupleMemoryContext(estate));
2470
2471 if (newtuple)
2472 ereport(ERROR,
2474 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2475 }
2476}
bool trig_insert_before_statement
Definition reltrigger.h:59

References before_stmt_triggers_fired(), CMD_INSERT, ereport, errcode(), errmsg, ERROR, ExecCallTriggerFunc(), fb(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, TriggerDesc::trig_insert_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_INSERT, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by CopyFrom(), and fireBSTriggers().

◆ ExecBSTruncateTriggers()

void ExecBSTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 3307 of file trigger.c.

3308{
3309 TriggerDesc *trigdesc;
3310 int i;
3312
3313 trigdesc = relinfo->ri_TrigDesc;
3314
3315 if (trigdesc == NULL)
3316 return;
3317 if (!trigdesc->trig_truncate_before_statement)
3318 return;
3319
3323 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3324
3325 for (i = 0; i < trigdesc->numtriggers; i++)
3326 {
3327 Trigger *trigger = &trigdesc->triggers[i];
3328 HeapTuple newtuple;
3329
3330 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3334 continue;
3335 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3336 NULL, NULL, NULL))
3337 continue;
3338
3339 LocTriggerData.tg_trigger = trigger;
3341 i,
3342 relinfo->ri_TrigFunctions,
3343 relinfo->ri_TrigInstrument,
3344 GetPerTupleMemoryContext(estate));
3345
3346 if (newtuple)
3347 ereport(ERROR,
3349 errmsg("BEFORE STATEMENT trigger cannot return a value")));
3350 }
3351}
bool trig_truncate_before_statement
Definition reltrigger.h:72

References ereport, errcode(), errmsg, ERROR, ExecCallTriggerFunc(), fb(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, TriggerDesc::trig_truncate_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_TRUNCATE, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by ExecuteTruncateGuts().

◆ ExecBSUpdateTriggers()

void ExecBSUpdateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2922 of file trigger.c.

2923{
2924 TriggerDesc *trigdesc;
2925 int i;
2927 Bitmapset *updatedCols;
2928
2929 trigdesc = relinfo->ri_TrigDesc;
2930
2931 if (trigdesc == NULL)
2932 return;
2933 if (!trigdesc->trig_update_before_statement)
2934 return;
2935
2936 /* no-op if we already fired BS triggers in this context */
2938 CMD_UPDATE))
2939 return;
2940
2941 /* statement-level triggers operate on the parent table */
2942 Assert(relinfo->ri_RootResultRelInfo == NULL);
2943
2944 updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
2945
2949 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2950 LocTriggerData.tg_updatedcols = updatedCols;
2951 for (i = 0; i < trigdesc->numtriggers; i++)
2952 {
2953 Trigger *trigger = &trigdesc->triggers[i];
2954 HeapTuple newtuple;
2955
2956 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2960 continue;
2961 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2962 updatedCols, NULL, NULL))
2963 continue;
2964
2965 LocTriggerData.tg_trigger = trigger;
2967 i,
2968 relinfo->ri_TrigFunctions,
2969 relinfo->ri_TrigInstrument,
2970 GetPerTupleMemoryContext(estate));
2971
2972 if (newtuple)
2973 ereport(ERROR,
2975 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2976 }
2977}
bool trig_update_before_statement
Definition reltrigger.h:64

References Assert, before_stmt_triggers_fired(), CMD_UPDATE, ereport, errcode(), errmsg, ERROR, ExecCallTriggerFunc(), ExecGetAllUpdatedCols(), fb(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, TriggerDesc::trig_update_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_UPDATE, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by fireBSTriggers().

◆ ExecCallTriggerFunc()

static HeapTuple ExecCallTriggerFunc ( TriggerData trigdata,
int  tgindx,
FmgrInfo finfo,
TriggerInstrumentation instr,
MemoryContext  per_tuple_context 
)
static

Definition at line 2336 of file trigger.c.

2341{
2342 LOCAL_FCINFO(fcinfo, 0);
2344 Datum result;
2346
2347 /*
2348 * Protect against code paths that may fail to initialize transition table
2349 * info.
2350 */
2352 TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event) ||
2353 TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) &&
2354 TRIGGER_FIRED_AFTER(trigdata->tg_event) &&
2355 !(trigdata->tg_event & AFTER_TRIGGER_DEFERRABLE) &&
2356 !(trigdata->tg_event & AFTER_TRIGGER_INITDEFERRED)) ||
2357 (trigdata->tg_oldtable == NULL && trigdata->tg_newtable == NULL));
2358
2359 finfo += tgindx;
2360
2361 /*
2362 * We cache fmgr lookup info, to avoid making the lookup again on each
2363 * call.
2364 */
2365 if (finfo->fn_oid == InvalidOid)
2366 fmgr_info(trigdata->tg_trigger->tgfoid, finfo);
2367
2368 Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid);
2369
2370 /*
2371 * If doing EXPLAIN ANALYZE, start charging time to this trigger.
2372 */
2373 if (instr)
2374 InstrStartTrigger(instr + tgindx);
2375
2376 /*
2377 * Do the function evaluation in the per-tuple memory context, so that
2378 * leaked memory will be reclaimed once per tuple. Note in particular that
2379 * any new tuple created by the trigger function will live till the end of
2380 * the tuple cycle.
2381 */
2383
2384 /*
2385 * Call the function, passing no arguments but setting a context.
2386 */
2387 InitFunctionCallInfoData(*fcinfo, finfo, 0,
2388 InvalidOid, (Node *) trigdata, NULL);
2389
2391
2393 PG_TRY();
2394 {
2395 result = FunctionCallInvoke(fcinfo);
2396 }
2397 PG_FINALLY();
2398 {
2400 }
2401 PG_END_TRY();
2402
2404
2406
2407 /*
2408 * Trigger protocol allows function to return a null pointer, but NOT to
2409 * set the isnull result flag.
2410 */
2411 if (fcinfo->isnull)
2412 ereport(ERROR,
2414 errmsg("trigger function %u returned null value",
2415 fcinfo->flinfo->fn_oid)));
2416
2417 /*
2418 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
2419 * the firing of the trigger.
2420 */
2421 if (instr)
2422 InstrStopTrigger(instr + tgindx, 1);
2423
2425}
#define PG_TRY(...)
Definition elog.h:374
#define PG_END_TRY(...)
Definition elog.h:399
#define PG_FINALLY(...)
Definition elog.h:391
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition fmgr.c:129
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition fmgr.h:150
#define LOCAL_FCINFO(name, nargs)
Definition fmgr.h:110
#define FunctionCallInvoke(fcinfo)
Definition fmgr.h:172
void pgstat_init_function_usage(FunctionCallInfo fcinfo, PgStat_FunctionCallUsage *fcu)
void pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
Oid fn_oid
Definition fmgr.h:59
Tuplestorestate * tg_oldtable
Definition trigger.h:41
Tuplestorestate * tg_newtable
Definition trigger.h:42
TriggerEvent tg_event
Definition trigger.h:34
Trigger * tg_trigger
Definition trigger.h:38
Oid tgfoid
Definition reltrigger.h:28
#define TRIGGER_FIRED_BY_INSERT(event)
Definition trigger.h:112

References AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_INITDEFERRED, Assert, DatumGetPointer(), ereport, errcode(), errmsg, ERROR, fb(), fmgr_info(), FmgrInfo::fn_oid, FunctionCallInvoke, InitFunctionCallInfoData, InstrStartTrigger(), InstrStopTrigger(), InvalidOid, LOCAL_FCINFO, MemoryContextSwitchTo(), MyTriggerDepth, PG_END_TRY, PG_FINALLY, PG_TRY, pgstat_end_function_usage(), pgstat_init_function_usage(), result, TriggerData::tg_event, TriggerData::tg_newtable, TriggerData::tg_oldtable, TriggerData::tg_trigger, Trigger::tgfoid, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, and TRIGGER_FIRED_BY_UPDATE.

Referenced by AfterTriggerExecute(), ExecBRDeleteTriggers(), ExecBRInsertTriggers(), ExecBRUpdateTriggers(), ExecBSDeleteTriggers(), ExecBSInsertTriggers(), ExecBSTruncateTriggers(), ExecBSUpdateTriggers(), ExecIRDeleteTriggers(), ExecIRInsertTriggers(), and ExecIRUpdateTriggers().

◆ ExecIRDeleteTriggers()

bool ExecIRDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
HeapTuple  trigtuple 
)

Definition at line 2875 of file trigger.c.

2877{
2878 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2881 int i;
2882
2887 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2888
2889 ExecForceStoreHeapTuple(trigtuple, slot, false);
2890
2891 for (i = 0; i < trigdesc->numtriggers; i++)
2892 {
2894 Trigger *trigger = &trigdesc->triggers[i];
2895
2896 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2900 continue;
2901 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2902 NULL, slot, NULL))
2903 continue;
2904
2905 LocTriggerData.tg_trigslot = slot;
2906 LocTriggerData.tg_trigtuple = trigtuple;
2907 LocTriggerData.tg_trigger = trigger;
2909 i,
2910 relinfo->ri_TrigFunctions,
2911 relinfo->ri_TrigInstrument,
2912 GetPerTupleMemoryContext(estate));
2913 if (rettuple == NULL)
2914 return false; /* Delete was suppressed */
2915 if (rettuple != trigtuple)
2917 }
2918 return true;
2919}
#define TRIGGER_EVENT_INSTEAD
Definition trigger.h:104

References ExecCallTriggerFunc(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), fb(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSTEAD, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by ExecDelete(), and ExecMergeMatched().

◆ ExecIRInsertTriggers()

bool ExecIRInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot 
)

Definition at line 2596 of file trigger.c.

2598{
2599 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2600 HeapTuple newtuple = NULL;
2601 bool should_free;
2603 int i;
2604
2609 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2610 for (i = 0; i < trigdesc->numtriggers; i++)
2611 {
2612 Trigger *trigger = &trigdesc->triggers[i];
2613 HeapTuple oldtuple;
2614
2615 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2619 continue;
2620 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2621 NULL, NULL, slot))
2622 continue;
2623
2624 if (!newtuple)
2625 newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2626
2627 LocTriggerData.tg_trigslot = slot;
2628 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2629 LocTriggerData.tg_trigger = trigger;
2631 i,
2632 relinfo->ri_TrigFunctions,
2633 relinfo->ri_TrigInstrument,
2634 GetPerTupleMemoryContext(estate));
2635 if (newtuple == NULL)
2636 {
2637 if (should_free)
2638 heap_freetuple(oldtuple);
2639 return false; /* "do nothing" */
2640 }
2641 else if (newtuple != oldtuple)
2642 {
2643 ExecForceStoreHeapTuple(newtuple, slot, false);
2644
2645 if (should_free)
2646 heap_freetuple(oldtuple);
2647
2648 /* signal tuple should be re-fetched if used */
2649 newtuple = NULL;
2650 }
2651 }
2652
2653 return true;
2654}

References ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), fb(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_INSTEAD, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by CopyFrom(), and ExecInsert().

◆ ExecIRUpdateTriggers()

bool ExecIRUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
HeapTuple  trigtuple,
TupleTableSlot newslot 
)

Definition at line 3241 of file trigger.c.

3243{
3244 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3246 HeapTuple newtuple = NULL;
3247 bool should_free;
3249 int i;
3250
3255 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3256
3258
3259 for (i = 0; i < trigdesc->numtriggers; i++)
3260 {
3261 Trigger *trigger = &trigdesc->triggers[i];
3262 HeapTuple oldtuple;
3263
3264 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3268 continue;
3269 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3270 NULL, oldslot, newslot))
3271 continue;
3272
3273 if (!newtuple)
3274 newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free);
3275
3276 LocTriggerData.tg_trigslot = oldslot;
3277 LocTriggerData.tg_trigtuple = trigtuple;
3278 LocTriggerData.tg_newslot = newslot;
3279 LocTriggerData.tg_newtuple = oldtuple = newtuple;
3280
3281 LocTriggerData.tg_trigger = trigger;
3283 i,
3284 relinfo->ri_TrigFunctions,
3285 relinfo->ri_TrigInstrument,
3286 GetPerTupleMemoryContext(estate));
3287 if (newtuple == NULL)
3288 {
3289 return false; /* "do nothing" */
3290 }
3291 else if (newtuple != oldtuple)
3292 {
3293 ExecForceStoreHeapTuple(newtuple, newslot, false);
3294
3295 if (should_free)
3296 heap_freetuple(oldtuple);
3297
3298 /* signal tuple should be re-fetched if used */
3299 newtuple = NULL;
3300 }
3301 }
3302
3303 return true;
3304}

References ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), fb(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, TRIGGER_EVENT_INSTEAD, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ FindTriggerIncompatibleWithInheritance()

const char * FindTriggerIncompatibleWithInheritance ( TriggerDesc trigdesc)

Definition at line 2304 of file trigger.c.

2305{
2306 if (trigdesc != NULL)
2307 {
2308 int i;
2309
2310 for (i = 0; i < trigdesc->numtriggers; ++i)
2311 {
2312 Trigger *trigger = &trigdesc->triggers[i];
2313
2314 if (!TRIGGER_FOR_ROW(trigger->tgtype))
2315 continue;
2316 if (trigger->tgoldtable != NULL || trigger->tgnewtable != NULL)
2317 return trigger->tgname;
2318 }
2319 }
2320
2321 return NULL;
2322}
char * tgname
Definition reltrigger.h:27

References fb(), i, TriggerDesc::numtriggers, Trigger::tgname, and TriggerDesc::triggers.

Referenced by ATExecAddInherit(), and ATExecAttachPartition().

◆ FireAfterTriggerBatchCallbacks()

static void FireAfterTriggerBatchCallbacks ( List callbacks)
static

◆ FreeTriggerDesc()

void FreeTriggerDesc ( TriggerDesc trigdesc)

Definition at line 2172 of file trigger.c.

2173{
2175 int i;
2176
2177 if (trigdesc == NULL)
2178 return;
2179
2180 trigger = trigdesc->triggers;
2181 for (i = 0; i < trigdesc->numtriggers; i++)
2182 {
2183 pfree(trigger->tgname);
2184 if (trigger->tgnattr > 0)
2185 pfree(trigger->tgattr);
2186 if (trigger->tgnargs > 0)
2187 {
2188 while (--(trigger->tgnargs) >= 0)
2189 pfree(trigger->tgargs[trigger->tgnargs]);
2190 pfree(trigger->tgargs);
2191 }
2192 if (trigger->tgqual)
2193 pfree(trigger->tgqual);
2194 if (trigger->tgoldtable)
2195 pfree(trigger->tgoldtable);
2196 if (trigger->tgnewtable)
2197 pfree(trigger->tgnewtable);
2198 trigger++;
2199 }
2200 pfree(trigdesc->triggers);
2201 pfree(trigdesc);
2202}

References fb(), i, TriggerDesc::numtriggers, pfree(), and TriggerDesc::triggers.

Referenced by RelationBuildTriggers(), and RelationDestroyRelation().

◆ get_trigger_oid()

Oid get_trigger_oid ( Oid  relid,
const char trigname,
bool  missing_ok 
)

Definition at line 1384 of file trigger.c.

1385{
1387 ScanKeyData skey[2];
1389 HeapTuple tup;
1390 Oid oid;
1391
1392 /*
1393 * Find the trigger, verify permissions, set up object address
1394 */
1396
1397 ScanKeyInit(&skey[0],
1400 ObjectIdGetDatum(relid));
1401 ScanKeyInit(&skey[1],
1404 CStringGetDatum(trigname));
1405
1407 NULL, 2, skey);
1408
1410
1411 if (!HeapTupleIsValid(tup))
1412 {
1413 if (!missing_ok)
1414 ereport(ERROR,
1416 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1417 trigname, get_rel_name(relid))));
1418 oid = InvalidOid;
1419 }
1420 else
1421 {
1422 oid = ((Form_pg_trigger) GETSTRUCT(tup))->oid;
1423 }
1424
1427 return oid;
1428}

References AccessShareLock, BTEqualStrategyNumber, CStringGetDatum(), ereport, errcode(), errmsg, ERROR, fb(), Form_pg_trigger, get_rel_name(), GETSTRUCT(), HeapTupleIsValid, InvalidOid, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by get_object_address_relobject().

◆ GetAfterTriggersStoreSlot()

static TupleTableSlot * GetAfterTriggersStoreSlot ( AfterTriggersTableData table,
TupleDesc  tupdesc 
)
static

Definition at line 4956 of file trigger.c.

4958{
4959 /* Create it if not already done. */
4960 if (!table->storeslot)
4961 {
4963
4964 /*
4965 * We need this slot only until AfterTriggerEndQuery, but making it
4966 * last till end-of-subxact is good enough. It'll be freed by
4967 * AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4968 * a different lifespan, so we'd better make a copy of that.
4969 */
4971 tupdesc = CreateTupleDescCopy(tupdesc);
4972 table->storeslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
4974 }
4975
4976 return table->storeslot;
4977}

References CreateTupleDescCopy(), CurTransactionContext, fb(), MakeSingleTupleTableSlot(), MemoryContextSwitchTo(), table, and TTSOpsVirtual.

Referenced by TransitionTableAddTuple().

◆ GetAfterTriggersTableData()

static AfterTriggersTableData * GetAfterTriggersTableData ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 4914 of file trigger.c.

4915{
4919 ListCell *lc;
4920
4921 /* At this level, cmdType should not be, eg, CMD_MERGE */
4922 Assert(cmdType == CMD_INSERT ||
4923 cmdType == CMD_UPDATE ||
4924 cmdType == CMD_DELETE);
4925
4926 /* Caller should have ensured query_depth is OK. */
4930
4931 foreach(lc, qs->tables)
4932 {
4934 if (table->relid == relid && table->cmdType == cmdType &&
4935 !table->closed)
4936 return table;
4937 }
4938
4940
4942 table->relid = relid;
4943 table->cmdType = cmdType;
4944 qs->tables = lappend(qs->tables, table);
4945
4947
4948 return table;
4949}

References afterTriggers, Assert, CMD_DELETE, CMD_INSERT, CMD_UPDATE, CurTransactionContext, fb(), lappend(), lfirst, AfterTriggersData::maxquerydepth, MemoryContextSwitchTo(), palloc0_object, AfterTriggersData::query_depth, AfterTriggersData::query_stack, and table.

Referenced by before_stmt_triggers_fired(), cancel_prior_stmt_triggers(), and MakeTransitionCaptureState().

◆ GetAfterTriggersTransitionTable()

static Tuplestorestate * GetAfterTriggersTransitionTable ( int  event,
TupleTableSlot oldslot,
TupleTableSlot newslot,
TransitionCaptureState transition_capture 
)
static

Definition at line 5619 of file trigger.c.

5623{
5624 Tuplestorestate *tuplestore = NULL;
5625 bool delete_old_table = transition_capture->tcs_delete_old_table;
5626 bool update_old_table = transition_capture->tcs_update_old_table;
5627 bool update_new_table = transition_capture->tcs_update_new_table;
5628 bool insert_new_table = transition_capture->tcs_insert_new_table;
5629
5630 /*
5631 * For INSERT events NEW should be non-NULL, for DELETE events OLD should
5632 * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
5633 * non-NULL. But for UPDATE events fired for capturing transition tuples
5634 * during UPDATE partition-key row movement, OLD is NULL when the event is
5635 * for a row being inserted, whereas NEW is NULL when the event is for a
5636 * row being deleted.
5637 */
5639 TupIsNull(oldslot)));
5641 TupIsNull(newslot)));
5642
5643 if (!TupIsNull(oldslot))
5644 {
5646 if (event == TRIGGER_EVENT_DELETE && delete_old_table)
5647 tuplestore = transition_capture->tcs_delete_private->old_tuplestore;
5648 else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
5649 tuplestore = transition_capture->tcs_update_private->old_tuplestore;
5650 }
5651 else if (!TupIsNull(newslot))
5652 {
5654 if (event == TRIGGER_EVENT_INSERT && insert_new_table)
5655 tuplestore = transition_capture->tcs_insert_private->new_tuplestore;
5656 else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
5657 tuplestore = transition_capture->tcs_update_private->new_tuplestore;
5658 }
5659
5660 return tuplestore;
5661}

References Assert, fb(), AfterTriggersTableData::new_tuplestore, AfterTriggersTableData::old_tuplestore, TransitionCaptureState::tcs_delete_old_table, TransitionCaptureState::tcs_delete_private, TransitionCaptureState::tcs_insert_new_table, TransitionCaptureState::tcs_insert_private, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, TransitionCaptureState::tcs_update_private, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_UPDATE, and TupIsNull.

Referenced by AfterTriggerSaveEvent().

◆ GetCurrentFDWTuplestore()

static Tuplestorestate * GetCurrentFDWTuplestore ( void  )
static

◆ GetTupleForTrigger()

static bool GetTupleForTrigger ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tid,
LockTupleMode  lockmode,
TupleTableSlot oldslot,
bool  do_epq_recheck,
TupleTableSlot **  epqslot,
TM_Result tmresultp,
TM_FailureData tmfdp 
)
static

Definition at line 3371 of file trigger.c.

3381{
3382 Relation relation = relinfo->ri_RelationDesc;
3383
3384 if (epqslot != NULL)
3385 {
3387 TM_FailureData tmfd;
3388 int lockflags = 0;
3389
3390 *epqslot = NULL;
3391
3392 /* caller must pass an epqstate if EvalPlanQual is possible */
3393 Assert(epqstate != NULL);
3394
3395 /*
3396 * lock tuple for update
3397 */
3400 test = table_tuple_lock(relation, tid, estate->es_snapshot, oldslot,
3401 estate->es_output_cid,
3402 lockmode, LockWaitBlock,
3403 lockflags,
3404 &tmfd);
3405
3406 /* Let the caller know about the status of this operation */
3407 if (tmresultp)
3408 *tmresultp = test;
3409 if (tmfdp)
3410 *tmfdp = tmfd;
3411
3412 switch (test)
3413 {
3414 case TM_SelfModified:
3415
3416 /*
3417 * The target tuple was already updated or deleted by the
3418 * current command, or by a later command in the current
3419 * transaction. We ignore the tuple in the former case, and
3420 * throw error in the latter case, for the same reasons
3421 * enumerated in ExecUpdate and ExecDelete in
3422 * nodeModifyTable.c.
3423 */
3424 if (tmfd.cmax != estate->es_output_cid)
3425 ereport(ERROR,
3427 errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
3428 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3429
3430 /* treat it as deleted; do not process */
3431 return false;
3432
3433 case TM_Ok:
3434 if (tmfd.traversed)
3435 {
3436 /*
3437 * Recheck the tuple using EPQ, if requested. Otherwise,
3438 * just return that it was concurrently updated.
3439 */
3440 if (do_epq_recheck)
3441 {
3442 *epqslot = EvalPlanQual(epqstate,
3443 relation,
3444 relinfo->ri_RangeTableIndex,
3445 oldslot);
3446
3447 /*
3448 * If PlanQual failed for updated tuple - we must not
3449 * process this tuple!
3450 */
3451 if (TupIsNull(*epqslot))
3452 {
3453 *epqslot = NULL;
3454 return false;
3455 }
3456 }
3457 else
3458 {
3459 if (tmresultp)
3461 return false;
3462 }
3463 }
3464 break;
3465
3466 case TM_Updated:
3468 ereport(ERROR,
3470 errmsg("could not serialize access due to concurrent update")));
3471 elog(ERROR, "unexpected table_tuple_lock status: %u", test);
3472 break;
3473
3474 case TM_Deleted:
3476 ereport(ERROR,
3478 errmsg("could not serialize access due to concurrent delete")));
3479 /* tuple was deleted */
3480 return false;
3481
3482 case TM_Invisible:
3483 elog(ERROR, "attempted to lock invisible tuple");
3484 break;
3485
3486 default:
3487 elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
3488 return false; /* keep compiler quiet */
3489 }
3490 }
3491 else
3492 {
3493 /*
3494 * We expect the tuple to be present, thus very simple error handling
3495 * suffices.
3496 */
3497 if (!table_tuple_fetch_row_version(relation, tid, SnapshotAny,
3498 oldslot))
3499 elog(ERROR, "failed to fetch tuple for trigger");
3500 }
3501
3502 return true;
3503}
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition execMain.c:2715
@ LockWaitBlock
Definition lockoptions.h:40
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition pgbench.c:77
static void test(void)
CommandId es_output_cid
Definition execnodes.h:719
Snapshot es_snapshot
Definition execnodes.h:697
CommandId cmax
Definition tableam.h:173
TM_Result
Definition tableam.h:95
@ TM_Ok
Definition tableam.h:100
@ TM_Deleted
Definition tableam.h:115
@ TM_Updated
Definition tableam.h:112
@ TM_SelfModified
Definition tableam.h:106
@ TM_Invisible
Definition tableam.h:103
static TM_Result table_tuple_lock(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
Definition tableam.h:1646
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition tableam.h:299
#define IsolationUsesXactSnapshot()
Definition xact.h:52

References Assert, TM_FailureData::cmax, elog, ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errhint(), errmsg, ERROR, EState::es_output_cid, EState::es_snapshot, EvalPlanQual(), fb(), IsolationUsesXactSnapshot, LockWaitBlock, SnapshotAny, table_tuple_fetch_row_version(), table_tuple_lock(), test(), TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TM_FailureData::traversed, TupIsNull, and TUPLE_LOCK_FLAG_FIND_LAST_VERSION.

Referenced by ExecARDeleteTriggers(), ExecARUpdateTriggers(), ExecBRDeleteTriggers(), and ExecBRUpdateTriggers().

◆ MakeTransitionCaptureState()

TransitionCaptureState * MakeTransitionCaptureState ( TriggerDesc trigdesc,
Oid  relid,
CmdType  cmdType 
)

Definition at line 5005 of file trigger.c.

5006{
5008 bool need_old_upd,
5017
5018 if (trigdesc == NULL)
5019 return NULL;
5020
5021 /* Detect which table(s) we need. */
5022 switch (cmdType)
5023 {
5024 case CMD_INSERT:
5027 break;
5028 case CMD_UPDATE:
5031 need_old_del = need_new_ins = false;
5032 break;
5033 case CMD_DELETE:
5036 break;
5037 case CMD_MERGE:
5042 break;
5043 default:
5044 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
5045 /* keep compiler quiet */
5047 break;
5048 }
5050 return NULL;
5051
5052 /* Check state, like AfterTriggerSaveEvent. */
5053 if (afterTriggers.query_depth < 0)
5054 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
5055
5056 /* Be sure we have enough space to record events at this query depth. */
5059
5060 /*
5061 * Find or create AfterTriggersTableData struct(s) to hold the
5062 * tuplestore(s). If there's a matching struct but it's marked closed,
5063 * ignore it; we need a newer one.
5064 *
5065 * Note: MERGE must use the same AfterTriggersTableData structs as INSERT,
5066 * UPDATE, and DELETE, so that any MERGE'd tuples are added to the same
5067 * tuplestores as tuples from any INSERT, UPDATE, or DELETE commands
5068 * running in the same top-level command (e.g., in a writable CTE).
5069 *
5070 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
5071 * allocated in the current (sub)transaction's CurTransactionContext, and
5072 * the tuplestores are managed by the (sub)transaction's resource owner.
5073 * This is sufficient lifespan because we do not allow triggers using
5074 * transition tables to be deferrable; they will be fired during
5075 * AfterTriggerEndQuery, after which it's okay to delete the data.
5076 */
5077 if (need_new_ins)
5079 else
5080 ins_table = NULL;
5081
5084 else
5085 upd_table = NULL;
5086
5087 if (need_old_del)
5089 else
5090 del_table = NULL;
5091
5092 /* Now create required tuplestore(s), if we don't have them already. */
5096
5097 if (need_old_upd && upd_table->old_tuplestore == NULL)
5098 upd_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5099 if (need_new_upd && upd_table->new_tuplestore == NULL)
5100 upd_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5101 if (need_old_del && del_table->old_tuplestore == NULL)
5102 del_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5103 if (need_new_ins && ins_table->new_tuplestore == NULL)
5104 ins_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5105
5108
5109 /* Now build the TransitionCaptureState struct, in caller's context */
5111 state->tcs_delete_old_table = need_old_del;
5112 state->tcs_update_old_table = need_old_upd;
5113 state->tcs_update_new_table = need_new_upd;
5114 state->tcs_insert_new_table = need_new_ins;
5115 state->tcs_insert_private = ins_table;
5116 state->tcs_update_private = upd_table;
5117 state->tcs_delete_private = del_table;
5118
5119 return state;
5120}

References AfterTriggerEnlargeQueryState(), afterTriggers, CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, CurrentResourceOwner, CurTransactionContext, CurTransactionResourceOwner, elog, ERROR, fb(), GetAfterTriggersTableData(), AfterTriggersData::maxquerydepth, MemoryContextSwitchTo(), palloc0_object, AfterTriggersData::query_depth, TriggerDesc::trig_delete_old_table, TriggerDesc::trig_insert_new_table, TriggerDesc::trig_update_new_table, TriggerDesc::trig_update_old_table, tuplestore_begin_heap(), and work_mem.

Referenced by CopyFrom(), and ExecSetupTransitionCaptureState().

◆ pg_trigger_depth()

Datum pg_trigger_depth ( PG_FUNCTION_ARGS  )

Definition at line 6812 of file trigger.c.

6813{
6815}

References MyTriggerDepth, and PG_RETURN_INT32.

◆ RangeVarCallbackForRenameTrigger()

static void RangeVarCallbackForRenameTrigger ( const RangeVar rv,
Oid  relid,
Oid  oldrelid,
void arg 
)
static

Definition at line 1434 of file trigger.c.

1436{
1437 HeapTuple tuple;
1439
1440 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1441 if (!HeapTupleIsValid(tuple))
1442 return; /* concurrently dropped */
1443 form = (Form_pg_class) GETSTRUCT(tuple);
1444
1445 /* only tables and views can have triggers */
1446 if (form->relkind != RELKIND_RELATION && form->relkind != RELKIND_VIEW &&
1447 form->relkind != RELKIND_FOREIGN_TABLE &&
1448 form->relkind != RELKIND_PARTITIONED_TABLE)
1449 ereport(ERROR,
1451 errmsg("relation \"%s\" cannot have triggers",
1452 rv->relname),
1454
1455 /* you must own the table to rename one of its triggers */
1458
1459 /*
1460 * Conflict log tables are used internally for logical replication
1461 * conflict logging and should not have triggers, as it could disrupt
1462 * conflict logging.
1463 */
1465 ereport(ERROR,
1467 errmsg("cannot rename trigger on conflict log table \"%s\"",
1468 rv->relname),
1469 errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
1470
1471 if (!allowSystemTableMods && IsSystemClass(relid, form))
1472 ereport(ERROR,
1474 errmsg("permission denied: \"%s\" is a system catalog",
1475 rv->relname)));
1476
1477 ReleaseSysCache(tuple);
1478}
@ ACLCHECK_NOT_OWNER
Definition acl.h:186
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4156
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition catalog.c:86
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, ereport, errcode(), errdetail(), errdetail_relkind_not_supported(), errmsg, ERROR, fb(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IsConflictLogTableClass(), IsSystemClass(), object_ownercheck(), ObjectIdGetDatum(), ReleaseSysCache(), RangeVar::relname, and SearchSysCache1().

Referenced by renametrig().

◆ RegisterAfterTriggerBatchCallback()

void RegisterAfterTriggerBatchCallback ( AfterTriggerBatchCallback  callback,
void arg 
)

Definition at line 6863 of file trigger.c.

6865{
6868
6869 /*
6870 * Allocate in TopTransactionContext so the item survives for the duration
6871 * of the batch, which may span multiple trigger invocations.
6872 *
6873 * Must be called while afterTriggers is active; callbacks registered
6874 * outside a trigger-firing context would never fire.
6875 */
6879 item = palloc(sizeof(AfterTriggerCallbackItem));
6880 item->callback = callback;
6881 item->arg = arg;
6882 if (afterTriggers.query_depth >= 0)
6883 {
6886
6887 qs->batch_callbacks = lappend(qs->batch_callbacks, item);
6888 }
6889 else
6893}

References afterTriggers, AfterTriggerCallbackItem::arg, arg, Assert, AfterTriggersData::batch_callbacks, AfterTriggersQueryData::batch_callbacks, AfterTriggerCallbackItem::callback, callback(), fb(), AfterTriggersData::firing_batch_callbacks, AfterTriggersData::firing_depth, lappend(), MemoryContextSwitchTo(), palloc(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, and TopTransactionContext.

Referenced by ri_FastPathGetEntry().

◆ RelationBuildTriggers()

void RelationBuildTriggers ( Relation  relation)

Definition at line 1888 of file trigger.c.

1889{
1890 TriggerDesc *trigdesc;
1891 int numtrigs;
1892 int maxtrigs;
1893 Trigger *triggers;
1897 HeapTuple htup;
1899 int i;
1900
1901 /*
1902 * Allocate a working array to hold the triggers (the array is extended if
1903 * necessary)
1904 */
1905 maxtrigs = 16;
1906 triggers = (Trigger *) palloc(maxtrigs * sizeof(Trigger));
1907 numtrigs = 0;
1908
1909 /*
1910 * Note: since we scan the triggers using TriggerRelidNameIndexId, we will
1911 * be reading the triggers in name order, except possibly during
1912 * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn
1913 * ensures that triggers will be fired in name order.
1914 */
1919
1922 NULL, 1, &skey);
1923
1925 {
1927 Trigger *build;
1928 Datum datum;
1929 bool isnull;
1930
1931 if (numtrigs >= maxtrigs)
1932 {
1933 maxtrigs *= 2;
1934 triggers = (Trigger *) repalloc(triggers, maxtrigs * sizeof(Trigger));
1935 }
1936 build = &(triggers[numtrigs]);
1937
1938 build->tgoid = pg_trigger->oid;
1940 NameGetDatum(&pg_trigger->tgname)));
1941 build->tgfoid = pg_trigger->tgfoid;
1942 build->tgtype = pg_trigger->tgtype;
1943 build->tgenabled = pg_trigger->tgenabled;
1944 build->tgisinternal = pg_trigger->tgisinternal;
1945 build->tgisclone = OidIsValid(pg_trigger->tgparentid);
1946 build->tgconstrrelid = pg_trigger->tgconstrrelid;
1947 build->tgconstrindid = pg_trigger->tgconstrindid;
1948 build->tgconstraint = pg_trigger->tgconstraint;
1949 build->tgdeferrable = pg_trigger->tgdeferrable;
1950 build->tginitdeferred = pg_trigger->tginitdeferred;
1951 build->tgnargs = pg_trigger->tgnargs;
1952 /* tgattr is first var-width field, so OK to access directly */
1953 build->tgnattr = pg_trigger->tgattr.dim1;
1954 if (build->tgnattr > 0)
1955 {
1956 build->tgattr = (int16 *) palloc(build->tgnattr * sizeof(int16));
1957 memcpy(build->tgattr, &(pg_trigger->tgattr.values),
1958 build->tgnattr * sizeof(int16));
1959 }
1960 else
1961 build->tgattr = NULL;
1962 if (build->tgnargs > 0)
1963 {
1964 bytea *val;
1965 char *p;
1966
1969 tgrel->rd_att, &isnull));
1970 if (isnull)
1971 elog(ERROR, "tgargs is null in trigger for relation \"%s\"",
1972 RelationGetRelationName(relation));
1973 p = (char *) VARDATA_ANY(val);
1974 build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
1975 for (i = 0; i < build->tgnargs; i++)
1976 {
1977 build->tgargs[i] = pstrdup(p);
1978 p += strlen(p) + 1;
1979 }
1980 }
1981 else
1982 build->tgargs = NULL;
1983
1985 tgrel->rd_att, &isnull);
1986 if (!isnull)
1987 build->tgoldtable =
1989 else
1990 build->tgoldtable = NULL;
1991
1993 tgrel->rd_att, &isnull);
1994 if (!isnull)
1995 build->tgnewtable =
1997 else
1998 build->tgnewtable = NULL;
1999
2000 datum = fastgetattr(htup, Anum_pg_trigger_tgqual,
2001 tgrel->rd_att, &isnull);
2002 if (!isnull)
2003 build->tgqual = TextDatumGetCString(datum);
2004 else
2005 build->tgqual = NULL;
2006
2007 numtrigs++;
2008 }
2009
2012
2013 /* There might not be any triggers */
2014 if (numtrigs == 0)
2015 {
2016 pfree(triggers);
2017 return;
2018 }
2019
2020 /* Build trigdesc */
2021 trigdesc = palloc0_object(TriggerDesc);
2022 trigdesc->triggers = triggers;
2023 trigdesc->numtriggers = numtrigs;
2024 for (i = 0; i < numtrigs; i++)
2025 SetTriggerFlags(trigdesc, &(triggers[i]));
2026
2027 /* Copy completed trigdesc into cache storage */
2029 relation->trigdesc = CopyTriggerDesc(trigdesc);
2031
2032 /* Release working memory */
2033 FreeTriggerDesc(trigdesc);
2034}
#define TextDatumGetCString(d)
Definition builtins.h:99
#define DatumGetByteaPP(X)
Definition fmgr.h:292
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
long val
Definition informix.c:689
MemoryContext CacheMemoryContext
Definition mcxt.c:170
Datum nameout(PG_FUNCTION_ARGS)
Definition name.c:71
static char * DatumGetCString(Datum X)
Definition postgres.h:365
static Datum NameGetDatum(const NameData *X)
Definition postgres.h:406
TriggerDesc * trigdesc
Definition rel.h:117
Definition c.h:835
void FreeTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2172
static void SetTriggerFlags(TriggerDesc *trigdesc, Trigger *trigger)
Definition trigger.c:2040
TriggerDesc * CopyTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2117
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486

References AccessShareLock, BTEqualStrategyNumber, CacheMemoryContext, CopyTriggerDesc(), DatumGetByteaPP, DatumGetCString(), DirectFunctionCall1, elog, ERROR, fastgetattr(), fb(), Form_pg_trigger, FreeTriggerDesc(), GETSTRUCT(), HeapTupleIsValid, i, memcpy(), MemoryContextSwitchTo(), NameGetDatum(), nameout(), TriggerDesc::numtriggers, ObjectIdGetDatum(), OidIsValid, palloc(), palloc0_object, pfree(), pstrdup(), RelationGetRelationName, RelationGetRelid, repalloc(), ScanKeyInit(), SetTriggerFlags(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TextDatumGetCString, Trigger::tgoid, RelationData::trigdesc, TriggerDesc::triggers, val, and VARDATA_ANY().

Referenced by RelationBuildDesc(), and RelationCacheInitializePhase3().

◆ RemoveTriggerById()

void RemoveTriggerById ( Oid  trigOid)

Definition at line 1305 of file trigger.c.

1306{
1309 ScanKeyData skey[1];
1310 HeapTuple tup;
1311 Oid relid;
1312 Relation rel;
1313
1315
1316 /*
1317 * Find the trigger to delete.
1318 */
1319 ScanKeyInit(&skey[0],
1322 ObjectIdGetDatum(trigOid));
1323
1325 NULL, 1, skey);
1326
1328 if (!HeapTupleIsValid(tup))
1329 elog(ERROR, "could not find tuple for trigger %u", trigOid);
1330
1331 /*
1332 * Open and exclusive-lock the relation the trigger belongs to.
1333 */
1334 relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
1335
1336 rel = table_open(relid, AccessExclusiveLock);
1337
1338 if (rel->rd_rel->relkind != RELKIND_RELATION &&
1339 rel->rd_rel->relkind != RELKIND_VIEW &&
1340 rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
1341 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1342 ereport(ERROR,
1344 errmsg("relation \"%s\" cannot have triggers",
1346 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
1347
1349 ereport(ERROR,
1351 errmsg("permission denied: \"%s\" is a system catalog",
1353
1354 /*
1355 * Delete the pg_trigger tuple.
1356 */
1357 CatalogTupleDelete(tgrel, &tup->t_self);
1358
1361
1362 /*
1363 * We do not bother to try to determine whether any other triggers remain,
1364 * which would be needed in order to decide whether it's safe to clear the
1365 * relation's relhastriggers. (In any case, there might be a concurrent
1366 * process adding new triggers.) Instead, just force a relcache inval to
1367 * make other backends (and this one too!) rebuild their relcache entries.
1368 * There's no great harm in leaving relhastriggers true even if there are
1369 * no triggers left.
1370 */
1372
1373 /* Keep lock on trigger's rel until end of xact */
1374 table_close(rel, NoLock);
1375}
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
Definition indexing.c:365
#define AccessExclusiveLock
Definition lockdefs.h:43

References AccessExclusiveLock, allowSystemTableMods, BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleDelete(), elog, ereport, errcode(), errdetail_relkind_not_supported(), errmsg, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), HeapTupleIsValid, IsSystemRelation(), NoLock, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelationName, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by doDeletion().

◆ renametrig()

ObjectAddress renametrig ( RenameStmt stmt)

Definition at line 1494 of file trigger.c.

1495{
1496 Oid tgoid;
1499 HeapTuple tuple;
1501 ScanKeyData key[2];
1502 Oid relid;
1503 ObjectAddress address;
1504
1505 /*
1506 * Look up name, check permissions, and acquire lock (which we will NOT
1507 * release until end of transaction).
1508 */
1510 0,
1512 NULL);
1513
1514 /* Have lock already, so just need to build relcache entry. */
1515 targetrel = relation_open(relid, NoLock);
1516
1517 /*
1518 * On partitioned tables, this operation recurses to partitions. Lock all
1519 * tables upfront.
1520 */
1521 if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1523
1525
1526 /*
1527 * Search for the trigger to modify.
1528 */
1529 ScanKeyInit(&key[0],
1532 ObjectIdGetDatum(relid));
1533 ScanKeyInit(&key[1],
1536 PointerGetDatum(stmt->subname));
1538 NULL, 2, key);
1540 {
1542
1544 tgoid = trigform->oid;
1545
1546 /*
1547 * If the trigger descends from a trigger on a parent partitioned
1548 * table, reject the rename. We don't allow a trigger in a partition
1549 * to differ in name from that of its parent: that would lead to an
1550 * inconsistency that pg_dump would not reproduce.
1551 */
1552 if (OidIsValid(trigform->tgparentid))
1553 ereport(ERROR,
1555 errmsg("cannot rename trigger \"%s\" on table \"%s\"",
1557 errhint("Rename the trigger on the partitioned table \"%s\" instead.",
1558 get_rel_name(get_partition_parent(relid, false))));
1559
1560
1561 /* Rename the trigger on this relation ... */
1562 renametrig_internal(tgrel, targetrel, tuple, stmt->newname,
1563 stmt->subname);
1564
1565 /* ... and if it is partitioned, recurse to its partitions */
1566 if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1567 {
1569
1570 for (int i = 0; i < partdesc->nparts; i++)
1571 {
1572 Oid partitionId = partdesc->oids[i];
1573
1575 stmt->newname, stmt->subname);
1576 }
1577 }
1578 }
1579 else
1580 {
1581 ereport(ERROR,
1583 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1585 }
1586
1587 ObjectAddressSet(address, TriggerRelationId, tgoid);
1588
1590
1592
1593 /*
1594 * Close rel, but keep exclusive lock!
1595 */
1597
1598 return address;
1599}
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition namespace.c:442
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition partition.c:53
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
static void renametrig_internal(Relation tgrel, Relation targetrel, HeapTuple trigtup, const char *newname, const char *expected_name)
Definition trigger.c:1609
static void RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition trigger.c:1434
static void renametrig_partition(Relation tgrel, Oid partitionId, Oid parentTriggerOid, const char *newname, const char *expected_name)
Definition trigger.c:1680

References AccessExclusiveLock, BTEqualStrategyNumber, ereport, errcode(), errhint(), errmsg, ERROR, fb(), find_all_inheritors(), Form_pg_trigger, get_partition_parent(), get_rel_name(), GETSTRUCT(), HeapTupleIsValid, i, NoLock, PartitionDescData::nparts, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, PointerGetDatum, RangeVarCallbackForRenameTrigger(), RangeVarGetRelidExtended(), relation_close(), relation_open(), RelationGetPartitionDesc(), RelationGetRelationName, renametrig_internal(), renametrig_partition(), RowExclusiveLock, ScanKeyInit(), stmt, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ExecRenameStmt().

◆ renametrig_internal()

static void renametrig_internal ( Relation  tgrel,
Relation  targetrel,
HeapTuple  trigtup,
const char newname,
const char expected_name 
)
static

Definition at line 1609 of file trigger.c.

1611{
1612 HeapTuple tuple;
1614 ScanKeyData key[2];
1616
1617 /* If the trigger already has the new name, nothing to do. */
1619 if (strcmp(NameStr(tgform->tgname), newname) == 0)
1620 return;
1621
1622 /*
1623 * Before actually trying the rename, search for triggers with the same
1624 * name. The update would fail with an ugly message in that case, and it
1625 * is better to throw a nicer error.
1626 */
1627 ScanKeyInit(&key[0],
1631 ScanKeyInit(&key[1],
1634 PointerGetDatum(newname));
1636 NULL, 2, key);
1638 ereport(ERROR,
1640 errmsg("trigger \"%s\" for relation \"%s\" already exists",
1643
1644 /*
1645 * The target name is free; update the existing pg_trigger tuple with it.
1646 */
1647 tuple = heap_copytuple(trigtup); /* need a modifiable copy */
1648 tgform = (Form_pg_trigger) GETSTRUCT(tuple);
1649
1650 /*
1651 * If the trigger has a name different from what we expected, let the user
1652 * know. (We can proceed anyway, since we must have reached here following
1653 * a tgparentid link.)
1654 */
1655 if (strcmp(NameStr(tgform->tgname), expected_name) != 0)
1657 errmsg("renamed trigger \"%s\" on relation \"%s\"",
1658 NameStr(tgform->tgname),
1660
1661 namestrcpy(&tgform->tgname, newname);
1662
1663 CatalogTupleUpdate(tgrel, &tuple->t_self, tuple);
1664
1666
1667 /*
1668 * Invalidate relation's relcache entry so that other backends (and this
1669 * one too!) are sent SI message to make them rebuild relcache entries.
1670 * (Ideally this should happen automatically...)
1671 */
1673}
#define NOTICE
Definition elog.h:36
void namestrcpy(Name name, const char *str)
Definition name.c:233

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), heap_copytuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, NameStr, namestrcpy(), NOTICE, ObjectIdGetDatum(), PointerGetDatum, RelationGetRelationName, RelationGetRelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.

Referenced by renametrig(), and renametrig_partition().

◆ renametrig_partition()

static void renametrig_partition ( Relation  tgrel,
Oid  partitionId,
Oid  parentTriggerOid,
const char newname,
const char expected_name 
)
static

Definition at line 1680 of file trigger.c.

1682{
1685 HeapTuple tuple;
1686
1687 /*
1688 * Given a relation and the OID of a trigger on parent relation, find the
1689 * corresponding trigger in the child and rename that trigger to the given
1690 * name.
1691 */
1692 ScanKeyInit(&key,
1697 NULL, 1, &key);
1698 while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1699 {
1702
1703 if (tgform->tgparentid != parentTriggerOid)
1704 continue; /* not our trigger */
1705
1707
1708 /* Rename the trigger on this partition */
1710
1711 /* And if this relation is partitioned, recurse to its partitions */
1712 if (partitionRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1713 {
1715 true);
1716
1717 for (int i = 0; i < partdesc->nparts; i++)
1718 {
1719 Oid partoid = partdesc->oids[i];
1720
1721 renametrig_partition(tgrel, partoid, tgform->oid, newname,
1722 NameStr(tgform->tgname));
1723 }
1724 }
1726
1727 /* There should be at most one matching tuple */
1728 break;
1729 }
1731}

References BTEqualStrategyNumber, fb(), Form_pg_trigger, GETSTRUCT(), HeapTupleIsValid, i, NameStr, NoLock, PartitionDescData::nparts, ObjectIdGetDatum(), PartitionDescData::oids, RelationGetPartitionDesc(), renametrig_internal(), renametrig_partition(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by renametrig(), and renametrig_partition().

◆ SetConstraintStateAddItem()

static SetConstraintState SetConstraintStateAddItem ( SetConstraintState  state,
Oid  tgoid,
bool  tgisdeferred 
)
static

Definition at line 5821 of file trigger.c.

5823{
5824 if (state->numstates >= state->numalloc)
5825 {
5826 int newalloc = state->numalloc * 2;
5827
5828 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5831 offsetof(SetConstraintStateData, trigstates) +
5833 state->numalloc = newalloc;
5834 Assert(state->numstates < state->numalloc);
5835 }
5836
5837 state->trigstates[state->numstates].sct_tgoid = tgoid;
5838 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5839 state->numstates++;
5840
5841 return state;
5842}

References Assert, fb(), Max, and repalloc().

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCopy()

static SetConstraintState SetConstraintStateCopy ( SetConstraintState  origstate)
static

Definition at line 5801 of file trigger.c.

5802{
5804
5806
5807 state->all_isset = origstate->all_isset;
5808 state->all_isdeferred = origstate->all_isdeferred;
5809 state->numstates = origstate->numstates;
5810 memcpy(state->trigstates, origstate->trigstates,
5811 origstate->numstates * sizeof(SetConstraintTriggerData));
5812
5813 return state;
5814}

References fb(), memcpy(), and SetConstraintStateCreate().

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCreate()

static SetConstraintState SetConstraintStateCreate ( int  numalloc)
static

Definition at line 5776 of file trigger.c.

5777{
5779
5780 /* Behave sanely with numalloc == 0 */
5781 if (numalloc <= 0)
5782 numalloc = 1;
5783
5784 /*
5785 * We assume that zeroing will correctly initialize the state values.
5786 */
5789 offsetof(SetConstraintStateData, trigstates) +
5790 numalloc * sizeof(SetConstraintTriggerData));
5791
5792 state->numalloc = numalloc;
5793
5794 return state;
5795}

References fb(), MemoryContextAllocZero(), and TopTransactionContext.

Referenced by AfterTriggerSetState(), and SetConstraintStateCopy().

◆ SetTriggerFlags()

static void SetTriggerFlags ( TriggerDesc trigdesc,
Trigger trigger 
)
static

Definition at line 2040 of file trigger.c.

2041{
2042 int16 tgtype = trigger->tgtype;
2043
2044 trigdesc->trig_insert_before_row |=
2047 trigdesc->trig_insert_after_row |=
2050 trigdesc->trig_insert_instead_row |=
2053 trigdesc->trig_insert_before_statement |=
2056 trigdesc->trig_insert_after_statement |=
2059 trigdesc->trig_update_before_row |=
2062 trigdesc->trig_update_after_row |=
2065 trigdesc->trig_update_instead_row |=
2068 trigdesc->trig_update_before_statement |=
2071 trigdesc->trig_update_after_statement |=
2074 trigdesc->trig_delete_before_row |=
2077 trigdesc->trig_delete_after_row |=
2080 trigdesc->trig_delete_instead_row |=
2083 trigdesc->trig_delete_before_statement |=
2086 trigdesc->trig_delete_after_statement |=
2089 /* there are no row-level truncate triggers */
2096
2097 trigdesc->trig_insert_new_table |=
2098 (TRIGGER_FOR_INSERT(tgtype) &&
2100 trigdesc->trig_update_old_table |=
2101 (TRIGGER_FOR_UPDATE(tgtype) &&
2103 trigdesc->trig_update_new_table |=
2104 (TRIGGER_FOR_UPDATE(tgtype) &&
2106 trigdesc->trig_delete_old_table |=
2107 (TRIGGER_FOR_DELETE(tgtype) &&
2109}
bool trig_delete_before_row
Definition reltrigger.h:66
bool trig_update_instead_row
Definition reltrigger.h:63
bool trig_delete_instead_row
Definition reltrigger.h:68
bool trig_insert_instead_row
Definition reltrigger.h:58
bool trig_update_before_row
Definition reltrigger.h:61
bool trig_insert_before_row
Definition reltrigger.h:56

References fb(), TriggerDesc::trig_delete_after_row, TriggerDesc::trig_delete_after_statement, TriggerDesc::trig_delete_before_row, TriggerDesc::trig_delete_before_statement, TriggerDesc::trig_delete_instead_row, TriggerDesc::trig_delete_old_table, TriggerDesc::trig_insert_after_row, TriggerDesc::trig_insert_after_statement, TriggerDesc::trig_insert_before_row, TriggerDesc::trig_insert_before_statement, TriggerDesc::trig_insert_instead_row, TriggerDesc::trig_insert_new_table, TriggerDesc::trig_truncate_after_statement, TriggerDesc::trig_truncate_before_statement, TriggerDesc::trig_update_after_row, TriggerDesc::trig_update_after_statement, TriggerDesc::trig_update_before_row, TriggerDesc::trig_update_before_statement, TriggerDesc::trig_update_instead_row, TriggerDesc::trig_update_new_table, and TriggerDesc::trig_update_old_table.

Referenced by RelationBuildTriggers().

◆ TransitionTableAddTuple()

static void TransitionTableAddTuple ( EState estate,
int  event,
TransitionCaptureState transition_capture,
ResultRelInfo relinfo,
TupleTableSlot slot,
TupleTableSlot original_insert_tuple,
Tuplestorestate tuplestore 
)
static

Definition at line 5670 of file trigger.c.

5677{
5678 TupleConversionMap *map;
5679
5680 /*
5681 * Nothing needs to be done if we don't have a tuplestore.
5682 */
5683 if (tuplestore == NULL)
5684 return;
5685
5688 else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5689 {
5691 TupleTableSlot *storeslot;
5692
5693 switch (event)
5694 {
5696 table = transition_capture->tcs_insert_private;
5697 break;
5699 table = transition_capture->tcs_update_private;
5700 break;
5702 table = transition_capture->tcs_delete_private;
5703 break;
5704 default:
5705 elog(ERROR, "invalid after-trigger event code: %d", event);
5706 table = NULL; /* keep compiler quiet */
5707 break;
5708 }
5709
5710 storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5711 execute_attr_map_slot(map->attrMap, slot, storeslot);
5712 tuplestore_puttupleslot(tuplestore, storeslot);
5713 }
5714 else
5715 tuplestore_puttupleslot(tuplestore, slot);
5716}

References TupleConversionMap::attrMap, elog, ERROR, ExecGetChildToRootMap(), execute_attr_map_slot(), fb(), GetAfterTriggersStoreSlot(), TupleConversionMap::outdesc, table, TransitionCaptureState::tcs_delete_private, TransitionCaptureState::tcs_insert_private, TransitionCaptureState::tcs_update_private, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_UPDATE, and tuplestore_puttupleslot().

Referenced by AfterTriggerSaveEvent().

◆ TriggerEnabled()

static bool TriggerEnabled ( EState estate,
ResultRelInfo relinfo,
Trigger trigger,
TriggerEvent  event,
Bitmapset modifiedCols,
TupleTableSlot oldslot,
TupleTableSlot newslot 
)
static

Definition at line 3509 of file trigger.c.

3513{
3514 /* Check replication-role-dependent enable state */
3516 {
3517 if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN ||
3518 trigger->tgenabled == TRIGGER_DISABLED)
3519 return false;
3520 }
3521 else /* ORIGIN or LOCAL role */
3522 {
3523 if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA ||
3524 trigger->tgenabled == TRIGGER_DISABLED)
3525 return false;
3526 }
3527
3528 /*
3529 * Check for column-specific trigger (only possible for UPDATE, and in
3530 * fact we *must* ignore tgattr for other event types)
3531 */
3532 if (trigger->tgnattr > 0 && TRIGGER_FIRED_BY_UPDATE(event))
3533 {
3534 int i;
3535 bool modified;
3536
3537 modified = false;
3538 for (i = 0; i < trigger->tgnattr; i++)
3539 {
3541 modifiedCols))
3542 {
3543 modified = true;
3544 break;
3545 }
3546 }
3547 if (!modified)
3548 return false;
3549 }
3550
3551 /* Check for WHEN clause */
3552 if (trigger->tgqual)
3553 {
3555 ExprContext *econtext;
3557 int i;
3558
3559 Assert(estate != NULL);
3560
3561 /*
3562 * trigger is an element of relinfo->ri_TrigDesc->triggers[]; find the
3563 * matching element of relinfo->ri_TrigWhenExprs[]
3564 */
3565 i = trigger - relinfo->ri_TrigDesc->triggers;
3566 predicate = &relinfo->ri_TrigWhenExprs[i];
3567
3568 /*
3569 * If first time through for this WHEN expression, build expression
3570 * nodetrees for it. Keep them in the per-query memory context so
3571 * they'll survive throughout the query.
3572 */
3573 if (*predicate == NULL)
3574 {
3575 Node *tgqual;
3576
3578 tgqual = stringToNode(trigger->tgqual);
3579 tgqual = expand_generated_columns_in_expr(tgqual, relinfo->ri_RelationDesc, PRS2_OLD_VARNO);
3580 tgqual = expand_generated_columns_in_expr(tgqual, relinfo->ri_RelationDesc, PRS2_NEW_VARNO);
3581 /* Change references to OLD and NEW to INNER_VAR and OUTER_VAR */
3584 /* ExecPrepareQual wants implicit-AND form */
3585 tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
3586 *predicate = ExecPrepareQual((List *) tgqual, estate);
3588 }
3589
3590 /*
3591 * We will use the EState's per-tuple context for evaluating WHEN
3592 * expressions (creating it if it's not already there).
3593 */
3594 econtext = GetPerTupleExprContext(estate);
3595
3596 /*
3597 * Finally evaluate the expression, making the old and/or new tuples
3598 * available as INNER_VAR/OUTER_VAR respectively.
3599 */
3600 econtext->ecxt_innertuple = oldslot;
3601 econtext->ecxt_outertuple = newslot;
3602 if (!ExecQual(*predicate, econtext))
3603 return false;
3604 }
3605
3606 return true;
3607}
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:645
ExprState * ExecPrepareQual(List *qual, EState *estate)
Definition execExpr.c:793
#define GetPerTupleExprContext(estate)
Definition executor.h:665
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition executor.h:527
List * make_ands_implicit(Expr *clause)
Definition makefuncs.c:810
#define OUTER_VAR
Definition primnodes.h:244
#define INNER_VAR
Definition primnodes.h:243
void * stringToNode(const char *str)
Definition read.c:90
Node * expand_generated_columns_in_expr(Node *node, Relation rel, int rt_index)
void ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
MemoryContext es_query_cxt
Definition execnodes.h:747
TupleTableSlot * ecxt_innertuple
Definition execnodes.h:289
TupleTableSlot * ecxt_outertuple
Definition execnodes.h:291
#define FirstLowInvalidHeapAttributeNumber
Definition sysattr.h:27
#define SESSION_REPLICATION_ROLE_REPLICA
Definition trigger.h:143
#define TRIGGER_DISABLED
Definition trigger.h:154
#define TRIGGER_FIRES_ON_REPLICA
Definition trigger.h:153

References Assert, bms_is_member(), ChangeVarNodes(), ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, EState::es_query_cxt, ExecPrepareQual(), ExecQual(), expand_generated_columns_in_expr(), fb(), FirstLowInvalidHeapAttributeNumber, GetPerTupleExprContext, i, INNER_VAR, make_ands_implicit(), MemoryContextSwitchTo(), OUTER_VAR, PRS2_NEW_VARNO, PRS2_OLD_VARNO, SESSION_REPLICATION_ROLE_REPLICA, SessionReplicationRole, stringToNode(), TRIGGER_DISABLED, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRES_ON_ORIGIN, and TRIGGER_FIRES_ON_REPLICA.

Referenced by AfterTriggerSaveEvent(), ExecBRDeleteTriggers(), ExecBRInsertTriggers(), ExecBRUpdateTriggers(), ExecBSDeleteTriggers(), ExecBSInsertTriggers(), ExecBSTruncateTriggers(), ExecBSUpdateTriggers(), ExecIRDeleteTriggers(), ExecIRInsertTriggers(), and ExecIRUpdateTriggers().

◆ TriggerSetParentTrigger()

void TriggerSetParentTrigger ( Relation  trigRel,
Oid  childTrigId,
Oid  parentTrigId,
Oid  childTableId 
)

Definition at line 1234 of file trigger.c.

1238{
1240 ScanKeyData skey[1];
1242 HeapTuple tuple,
1243 newtup;
1246
1247 /*
1248 * Find the trigger to delete.
1249 */
1250 ScanKeyInit(&skey[0],
1254
1256 NULL, 1, skey);
1257
1258 tuple = systable_getnext(tgscan);
1259 if (!HeapTupleIsValid(tuple))
1260 elog(ERROR, "could not find tuple for trigger %u", childTrigId);
1261 newtup = heap_copytuple(tuple);
1264 {
1265 /* don't allow setting parent for a constraint that already has one */
1266 if (OidIsValid(trigForm->tgparentid))
1267 elog(ERROR, "trigger %u already has a parent trigger",
1268 childTrigId);
1269
1270 trigForm->tgparentid = parentTrigId;
1271
1273
1275
1278
1281 }
1282 else
1283 {
1284 trigForm->tgparentid = InvalidOid;
1285
1287
1294 }
1295
1298}
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:364

References BTEqualStrategyNumber, CatalogTupleUpdate(), deleteDependencyRecordsForClass(), DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, elog, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvalidOid, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, recordDependencyOn(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.

Referenced by AttachPartitionForeignKey(), and DetachPartitionFinalize().

Variable Documentation

◆ afterTriggers

◆ MyTriggerDepth

int MyTriggerDepth = 0
static

Definition at line 68 of file trigger.c.

Referenced by ExecCallTriggerFunc(), and pg_trigger_depth().

◆ SessionReplicationRole