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 3689 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3690 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3691 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3684 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3688 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3687 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3685 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3683 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3692 of file trigger.c.

◆ CHUNK_DATA_START

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

Definition at line 3772 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 3783 of file trigger.c.

3882{
3883 CommandId firing_counter; /* next firing ID to assign */
3884 SetConstraintState state; /* the active S C state */
3885 AfterTriggerEventList events; /* deferred-event list */
3886 MemoryContext event_cxt; /* memory context for events, if any */
3887
3888 /* per-query-level data: */
3889 AfterTriggersQueryData *query_stack; /* array of structs shown below */
3890 int query_depth; /* current index in above array */
3891 int maxquerydepth; /* allocated len of above array */
3892
3893 /* per-subtransaction-level data: */
3894 AfterTriggersTransData *trans_stack; /* array of structs shown below */
3895 int maxtransdepth; /* allocated len of above array */
3896
3897 List *batch_callbacks; /* List of AfterTriggerCallbackItem; for
3898 * deferred constraints */
3899 bool firing_batch_callbacks; /* true when in
3900 * FireAfterTriggerBatchCallbacks() */
3901
3902 /*
3903 * Incremented around the trigger-firing loops in AfterTriggerEndQuery,
3904 * AfterTriggerFireDeferred, and AfterTriggerSetState. Used by
3905 * AfterTriggerIsActive() to signal that after-trigger firing is active.
3906 */
3907 int firing_depth;
3909
3911{
3912 AfterTriggerEventList events; /* events pending from this query */
3913 Tuplestorestate *fdw_tuplestore; /* foreign tuples for said events */
3914 List *tables; /* list of AfterTriggersTableData, see below */
3915 List *batch_callbacks; /* List of AfterTriggerCallbackItem */
3916};
3917
3919{
3920 /* these fields are just for resetting at subtrans abort: */
3921 SetConstraintState state; /* saved S C state, or NULL if not yet saved */
3922 AfterTriggerEventList events; /* saved list pointer */
3923 int query_depth; /* saved query_depth */
3924 CommandId firing_counter; /* saved firing_counter */
3925};
3926
3928{
3929 /* relid + cmdType form the lookup key for these structs: */
3930 Oid relid; /* target table's OID */
3931 CmdType cmdType; /* event type, CMD_INSERT/UPDATE/DELETE */
3932 bool closed; /* true when no longer OK to add tuples */
3933 bool before_trig_done; /* did we already queue BS triggers? */
3934 bool after_trig_done; /* did we already queue AS triggers? */
3935 AfterTriggerEventList after_trig_events; /* if so, saved list pointer */
3936
3937 /* "old" transition table for UPDATE/DELETE, if any */
3939 /* "new" transition table for INSERT/UPDATE, if any */
3941
3942 TupleTableSlot *storeslot; /* for converting to tuplestore's format */
3943};
3944
3945/* Entry in afterTriggers.batch_callbacks */
3946typedef struct AfterTriggerCallbackItem
3947{
3949 void *arg;
3951
3953
3954static void AfterTriggerExecute(EState *estate,
3955 AfterTriggerEvent event,
3959 TriggerDesc *trigdesc,
3960 FmgrInfo *finfo,
3966 CmdType cmdType);
3968 TupleDesc tupdesc);
3972 TransitionCaptureState *transition_capture);
3973static void TransitionTableAddTuple(EState *estate,
3974 int event,
3975 TransitionCaptureState *transition_capture,
3977 TupleTableSlot *slot,
3979 Tuplestorestate *tuplestore);
3981static SetConstraintState SetConstraintStateCreate(int numalloc);
3984 Oid tgoid, bool tgisdeferred);
3985static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent);
3986
3987static void FireAfterTriggerBatchCallbacks(List *callbacks);
3988
3989/*
3990 * Get the FDW tuplestore for the current trigger query level, creating it
3991 * if necessary.
3992 */
3993static Tuplestorestate *
3995{
3996 Tuplestorestate *ret;
3997
3999 if (ret == NULL)
4000 {
4003
4004 /*
4005 * Make the tuplestore valid until end of subtransaction. We really
4006 * only need it until AfterTriggerEndQuery().
4007 */
4011
4012 ret = tuplestore_begin_heap(false, false, work_mem);
4013
4016
4018 }
4019
4020 return ret;
4021}
4022
4023/* ----------
4024 * afterTriggerCheckState()
4025 *
4026 * Returns true if the trigger event is actually in state DEFERRED.
4027 * ----------
4028 */
4029static bool
4031{
4032 Oid tgoid = evtshared->ats_tgoid;
4034 int i;
4035
4036 /*
4037 * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
4038 * constraints declared NOT DEFERRABLE), the state is always false.
4039 */
4040 if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
4041 return false;
4042
4043 /*
4044 * If constraint state exists, SET CONSTRAINTS might have been executed
4045 * either for this trigger or for all triggers.
4046 */
4047 if (state != NULL)
4048 {
4049 /* Check for SET CONSTRAINTS for this specific trigger. */
4050 for (i = 0; i < state->numstates; i++)
4051 {
4052 if (state->trigstates[i].sct_tgoid == tgoid)
4053 return state->trigstates[i].sct_tgisdeferred;
4054 }
4055
4056 /* Check for SET CONSTRAINTS ALL. */
4057 if (state->all_isset)
4058 return state->all_isdeferred;
4059 }
4060
4061 /*
4062 * Otherwise return the default state for the trigger.
4063 */
4064 return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
4065}
4066
4067/* ----------
4068 * afterTriggerCopyBitmap()
4069 *
4070 * Copy bitmap into AfterTriggerEvents memory context, which is where the after
4071 * trigger events are kept.
4072 * ----------
4073 */
4074static Bitmapset *
4076{
4077 Bitmapset *dst;
4079
4080 if (src == NULL)
4081 return NULL;
4082
4084
4085 dst = bms_copy(src);
4086
4088
4089 return dst;
4090}
4091
4092/* ----------
4093 * afterTriggerAddEvent()
4094 *
4095 * Add a new trigger event to the specified queue.
4096 * The passed-in event data is copied.
4097 * ----------
4098 */
4099static void
4102{
4108
4109 /*
4110 * If empty list or not enough room in the tail chunk, make a new chunk.
4111 * We assume here that a new shared record will always be needed.
4112 */
4113 chunk = events->tail;
4114 if (chunk == NULL ||
4115 chunk->endfree - chunk->freeptr < needed)
4116 {
4118
4119 /* Create event context if we didn't already */
4123 "AfterTriggerEvents",
4125
4126 /*
4127 * Chunk size starts at 1KB and is allowed to increase up to 1MB.
4128 * These numbers are fairly arbitrary, though there is a hard limit at
4129 * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
4130 * shared records using the available space in ate_flags. Another
4131 * constraint is that if the chunk size gets too huge, the search loop
4132 * below would get slow given a (not too common) usage pattern with
4133 * many distinct event types in a chunk. Therefore, we double the
4134 * preceding chunk size only if there weren't too many shared records
4135 * in the preceding chunk; otherwise we halve it. This gives us some
4136 * ability to adapt to the actual usage pattern of the current query
4137 * while still having large chunk sizes in typical usage. All chunk
4138 * sizes used should be MAXALIGN multiples, to ensure that the shared
4139 * records will be aligned safely.
4140 */
4141#define MIN_CHUNK_SIZE 1024
4142#define MAX_CHUNK_SIZE (1024*1024)
4143
4144#if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
4145#error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
4146#endif
4147
4148 if (chunk == NULL)
4150 else
4151 {
4152 /* preceding chunk size... */
4153 chunksize = chunk->endptr - (char *) chunk;
4154 /* check number of shared records in preceding chunk */
4155 if ((chunk->endptr - chunk->endfree) <=
4156 (100 * sizeof(AfterTriggerSharedData)))
4157 chunksize *= 2; /* okay, double it */
4158 else
4159 chunksize /= 2; /* too many shared records */
4161 }
4163 chunk->next = NULL;
4164 chunk->freeptr = CHUNK_DATA_START(chunk);
4165 chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
4166 Assert(chunk->endfree - chunk->freeptr >= needed);
4167
4168 if (events->tail == NULL)
4169 {
4170 Assert(events->head == NULL);
4171 events->head = chunk;
4172 }
4173 else
4174 events->tail->next = chunk;
4175 events->tail = chunk;
4176 /* events->tailfree is now out of sync, but we'll fix it below */
4177 }
4178
4179 /*
4180 * Try to locate a matching shared-data record already in the chunk. If
4181 * none, make a new one. The search begins with the most recently added
4182 * record, since newer ones are most likely to match.
4183 */
4184 for (newshared = (AfterTriggerShared) chunk->endfree;
4185 (char *) newshared < chunk->endptr;
4186 newshared++)
4187 {
4188 /* compare fields roughly by probability of them being different */
4189 if (newshared->ats_tgoid == evtshared->ats_tgoid &&
4190 newshared->ats_event == evtshared->ats_event &&
4191 newshared->ats_firing_id == 0 &&
4192 newshared->ats_table == evtshared->ats_table &&
4193 newshared->ats_relid == evtshared->ats_relid &&
4194 newshared->ats_rolid == evtshared->ats_rolid &&
4195 bms_equal(newshared->ats_modifiedcols,
4196 evtshared->ats_modifiedcols))
4197 break;
4198 }
4199 if ((char *) newshared >= chunk->endptr)
4200 {
4201 newshared = ((AfterTriggerShared) chunk->endfree) - 1;
4202 *newshared = *evtshared;
4203 /* now we must make a suitably-long-lived copy of the bitmap */
4204 newshared->ats_modifiedcols = afterTriggerCopyBitmap(evtshared->ats_modifiedcols);
4205 newshared->ats_firing_id = 0; /* just to be sure */
4206 chunk->endfree = (char *) newshared;
4207 }
4208
4209 /* Insert the data */
4210 newevent = (AfterTriggerEvent) chunk->freeptr;
4211 memcpy(newevent, event, eventsize);
4212 /* ... and link the new event to its shared record */
4213 newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
4214 newevent->ate_flags |= (char *) newshared - (char *) newevent;
4215
4216 chunk->freeptr += eventsize;
4217 events->tailfree = chunk->freeptr;
4218}
4219
4220/* ----------
4221 * afterTriggerFreeEventList()
4222 *
4223 * Free all the event storage in the given list.
4224 * ----------
4225 */
4226static void
4228{
4230
4231 while ((chunk = events->head) != NULL)
4232 {
4233 events->head = chunk->next;
4234 pfree(chunk);
4235 }
4236 events->tail = NULL;
4237 events->tailfree = NULL;
4238}
4239
4240/* ----------
4241 * afterTriggerRestoreEventList()
4242 *
4243 * Restore an event list to its prior length, removing all the events
4244 * added since it had the value old_events.
4245 * ----------
4246 */
4247static void
4250{
4253
4254 if (old_events->tail == NULL)
4255 {
4256 /* restoring to a completely empty state, so free everything */
4258 }
4259 else
4260 {
4261 *events = *old_events;
4262 /* free any chunks after the last one we want to keep */
4263 for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4264 {
4265 next_chunk = chunk->next;
4266 pfree(chunk);
4267 }
4268 /* and clean up the tail chunk to be the right length */
4269 events->tail->next = NULL;
4270 events->tail->freeptr = events->tailfree;
4271
4272 /*
4273 * We don't make any effort to remove now-unused shared data records.
4274 * They might still be useful, anyway.
4275 */
4276 }
4277}
4278
4279/* ----------
4280 * afterTriggerDeleteHeadEventChunk()
4281 *
4282 * Remove the first chunk of events from the query level's event list.
4283 * Keep any event list pointers elsewhere in the query level's data
4284 * structures in sync.
4285 * ----------
4286 */
4287static void
4289{
4290 AfterTriggerEventChunk *target = qs->events.head;
4291 ListCell *lc;
4292
4293 Assert(target && target->next);
4294
4295 /*
4296 * First, update any pointers in the per-table data, so that they won't be
4297 * dangling. Resetting obsoleted pointers to NULL will make
4298 * cancel_prior_stmt_triggers start from the list head, which is fine.
4299 */
4300 foreach(lc, qs->tables)
4301 {
4303
4304 if (table->after_trig_done &&
4305 table->after_trig_events.tail == target)
4306 {
4307 table->after_trig_events.head = NULL;
4308 table->after_trig_events.tail = NULL;
4309 table->after_trig_events.tailfree = NULL;
4310 }
4311 }
4312
4313 /* Now we can flush the head chunk */
4314 qs->events.head = target->next;
4315 pfree(target);
4316}
4317
4318
4319/* ----------
4320 * AfterTriggerExecute()
4321 *
4322 * Fetch the required tuples back from the heap and fire one
4323 * single trigger function.
4324 *
4325 * Frequently, this will be fired many times in a row for triggers of
4326 * a single relation. Therefore, we cache the open relation and provide
4327 * fmgr lookup cache space at the caller level. (For triggers fired at
4328 * the end of a query, we can even piggyback on the executor's state.)
4329 *
4330 * When fired for a cross-partition update of a partitioned table, the old
4331 * tuple is fetched using 'src_relInfo' (the source leaf partition) and
4332 * the new tuple using 'dst_relInfo' (the destination leaf partition), though
4333 * both are converted into the root partitioned table's format before passing
4334 * to the trigger function.
4335 *
4336 * event: event currently being fired.
4337 * relInfo: result relation for event.
4338 * src_relInfo: source partition of a cross-partition update
4339 * dst_relInfo: its destination partition
4340 * trigdesc: working copy of rel's trigger info.
4341 * finfo: array of fmgr lookup cache entries (one per trigger in trigdesc).
4342 * instr: array of EXPLAIN ANALYZE instrumentation nodes (one per trigger),
4343 * or NULL if no instrumentation is wanted.
4344 * per_tuple_context: memory context to call trigger function in.
4345 * trig_tuple_slot1: scratch slot for tg_trigtuple (foreign tables only)
4346 * trig_tuple_slot2: scratch slot for tg_newtuple (foreign tables only)
4347 * ----------
4348 */
4349static void
4351 AfterTriggerEvent event,
4355 TriggerDesc *trigdesc,
4356 FmgrInfo *finfo, TriggerInstrumentation *instr,
4360{
4361 Relation rel = relInfo->ri_RelationDesc;
4362 Relation src_rel = src_relInfo->ri_RelationDesc;
4363 Relation dst_rel = dst_relInfo->ri_RelationDesc;
4365 Oid tgoid = evtshared->ats_tgoid;
4368 int save_sec_context;
4370 int tgindx;
4371 bool should_free_trig = false;
4372 bool should_free_new = false;
4373
4374 /*
4375 * Locate trigger in trigdesc. It might not be present, and in fact the
4376 * trigdesc could be NULL, if the trigger was dropped since the event was
4377 * queued. In that case, silently do nothing.
4378 */
4379 if (trigdesc == NULL)
4380 return;
4381 for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4382 {
4383 if (trigdesc->triggers[tgindx].tgoid == tgoid)
4384 {
4385 LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4386 break;
4387 }
4388 }
4389 if (LocTriggerData.tg_trigger == NULL)
4390 return;
4391
4392 /*
4393 * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4394 * to include time spent re-fetching tuples in the trigger cost.
4395 */
4396 if (instr)
4397 InstrStartTrigger(instr + tgindx);
4398
4399 /*
4400 * Fetch the required tuple(s).
4401 */
4402 switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4403 {
4405 {
4406 Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4407
4408 if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4410 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4411
4412 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4414 !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4416 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4417 }
4420
4421 /*
4422 * Store tuple in the slot so that tg_trigtuple does not reference
4423 * tuplestore memory. (It is formally possible for the trigger
4424 * function to queue trigger events that add to the same
4425 * tuplestore, which can push other tuples out of memory.) The
4426 * distinction is academic, because we start with a minimal tuple
4427 * that is stored as a heap tuple, constructed in different memory
4428 * context, in the slot anyway.
4429 */
4430 LocTriggerData.tg_trigslot = trig_tuple_slot1;
4431 LocTriggerData.tg_trigtuple =
4433
4434 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4436 {
4437 LocTriggerData.tg_newslot = trig_tuple_slot2;
4438 LocTriggerData.tg_newtuple =
4440 }
4441 else
4442 {
4443 LocTriggerData.tg_newtuple = NULL;
4444 }
4445 break;
4446
4447 default:
4448 if (ItemPointerIsValid(&(event->ate_ctid1)))
4449 {
4451 src_relInfo);
4452
4454 &(event->ate_ctid1),
4456 src_slot))
4457 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4458
4459 /*
4460 * Store the tuple fetched from the source partition into the
4461 * target (root partitioned) table slot, converting if needed.
4462 */
4463 if (src_relInfo != relInfo)
4464 {
4466
4467 LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4468 if (map)
4469 {
4471 src_slot,
4472 LocTriggerData.tg_trigslot);
4473 }
4474 else
4475 ExecCopySlot(LocTriggerData.tg_trigslot, src_slot);
4476 }
4477 else
4478 LocTriggerData.tg_trigslot = src_slot;
4479 LocTriggerData.tg_trigtuple =
4481 }
4482 else
4483 {
4484 LocTriggerData.tg_trigtuple = NULL;
4485 }
4486
4487 /* don't touch ctid2 if not there */
4489 (event->ate_flags & AFTER_TRIGGER_CP_UPDATE)) &&
4490 ItemPointerIsValid(&(event->ate_ctid2)))
4491 {
4493 dst_relInfo);
4494
4496 &(event->ate_ctid2),
4498 dst_slot))
4499 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4500
4501 /*
4502 * Store the tuple fetched from the destination partition into
4503 * the target (root partitioned) table slot, converting if
4504 * needed.
4505 */
4506 if (dst_relInfo != relInfo)
4507 {
4509
4510 LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4511 if (map)
4512 {
4514 dst_slot,
4515 LocTriggerData.tg_newslot);
4516 }
4517 else
4519 }
4520 else
4521 LocTriggerData.tg_newslot = dst_slot;
4522 LocTriggerData.tg_newtuple =
4524 }
4525 else
4526 {
4527 LocTriggerData.tg_newtuple = NULL;
4528 }
4529 }
4530
4531 /*
4532 * Set up the tuplestore information to let the trigger have access to
4533 * transition tables. When we first make a transition table available to
4534 * a trigger, mark it "closed" so that it cannot change anymore. If any
4535 * additional events of the same type get queued in the current trigger
4536 * query level, they'll go into new transition tables.
4537 */
4538 LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4539 if (evtshared->ats_table)
4540 {
4541 if (LocTriggerData.tg_trigger->tgoldtable)
4542 {
4543 LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4544 evtshared->ats_table->closed = true;
4545 }
4546
4547 if (LocTriggerData.tg_trigger->tgnewtable)
4548 {
4549 LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4550 evtshared->ats_table->closed = true;
4551 }
4552 }
4553
4554 /*
4555 * Setup the remaining trigger information
4556 */
4558 LocTriggerData.tg_event =
4560 LocTriggerData.tg_relation = rel;
4561 if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4562 LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4563
4565
4566 /*
4567 * If necessary, become the role that was active when the trigger got
4568 * queued. Note that the role might have been dropped since the trigger
4569 * was queued, but if that is a problem, we will get an error later.
4570 * Checking here would still leave a race condition.
4571 */
4572 GetUserIdAndSecContext(&save_rolid, &save_sec_context);
4573 if (save_rolid != evtshared->ats_rolid)
4575 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
4576
4577 /*
4578 * Call the trigger and throw away any possibly returned updated tuple.
4579 * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4580 */
4582 tgindx,
4583 finfo,
4584 NULL,
4586 if (rettuple != NULL &&
4587 rettuple != LocTriggerData.tg_trigtuple &&
4588 rettuple != LocTriggerData.tg_newtuple)
4590
4591 /* Restore the current role if necessary */
4592 if (save_rolid != evtshared->ats_rolid)
4593 SetUserIdAndSecContext(save_rolid, save_sec_context);
4594
4595 /*
4596 * Release resources
4597 */
4598 if (should_free_trig)
4599 heap_freetuple(LocTriggerData.tg_trigtuple);
4600 if (should_free_new)
4601 heap_freetuple(LocTriggerData.tg_newtuple);
4602
4603 /* don't clear slots' contents if foreign table */
4604 if (trig_tuple_slot1 == NULL)
4605 {
4606 if (LocTriggerData.tg_trigslot)
4607 ExecClearTuple(LocTriggerData.tg_trigslot);
4608 if (LocTriggerData.tg_newslot)
4609 ExecClearTuple(LocTriggerData.tg_newslot);
4610 }
4611
4612 /*
4613 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4614 * the firing of the trigger.
4615 */
4616 if (instr)
4617 InstrStopTrigger(instr + tgindx, 1);
4618}
4619
4620
4621/*
4622 * afterTriggerMarkEvents()
4623 *
4624 * Scan the given event list for not yet invoked events. Mark the ones
4625 * that can be invoked now with the current firing ID.
4626 *
4627 * If move_list isn't NULL, events that are not to be invoked now are
4628 * transferred to move_list.
4629 *
4630 * When immediate_only is true, do not invoke currently-deferred triggers.
4631 * (This will be false only at main transaction exit.)
4632 *
4633 * Returns true if any invokable events were found.
4634 */
4635static bool
4638 bool immediate_only)
4639{
4640 bool found = false;
4641 bool deferred_found = false;
4642 AfterTriggerEvent event;
4644
4645 for_each_event_chunk(event, chunk, *events)
4646 {
4648 bool defer_it = false;
4649
4650 if (!(event->ate_flags &
4652 {
4653 /*
4654 * This trigger hasn't been called or scheduled yet. Check if we
4655 * should call it now.
4656 */
4658 {
4659 defer_it = true;
4660 }
4661 else
4662 {
4663 /*
4664 * Mark it as to be fired in this firing cycle.
4665 */
4666 evtshared->ats_firing_id = afterTriggers.firing_counter;
4667 event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4668 found = true;
4669 }
4670 }
4671
4672 /*
4673 * If it's deferred, move it to move_list, if requested.
4674 */
4675 if (defer_it && move_list != NULL)
4676 {
4677 deferred_found = true;
4678 /* add it to move_list */
4680 /* mark original copy "done" so we don't do it again */
4681 event->ate_flags |= AFTER_TRIGGER_DONE;
4682 }
4683 }
4684
4685 /*
4686 * We could allow deferred triggers if, before the end of the
4687 * security-restricted operation, we were to verify that a SET CONSTRAINTS
4688 * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4689 */
4691 ereport(ERROR,
4693 errmsg("cannot fire deferred trigger within security-restricted operation")));
4694
4695 return found;
4696}
4697
4698/*
4699 * afterTriggerInvokeEvents()
4700 *
4701 * Scan the given event list for events that are marked as to be fired
4702 * in the current firing cycle, and fire them.
4703 *
4704 * If estate isn't NULL, we use its result relation info to avoid repeated
4705 * openings and closing of trigger target relations. If it is NULL, we
4706 * make one locally to cache the info in case there are multiple trigger
4707 * events per rel.
4708 *
4709 * When delete_ok is true, it's safe to delete fully-processed events.
4710 * (We are not very tense about that: we simply reset a chunk to be empty
4711 * if all its events got fired. The objective here is just to avoid useless
4712 * rescanning of events when a trigger queues new events during transaction
4713 * end, so it's not necessary to worry much about the case where only
4714 * some events are fired.)
4715 *
4716 * Returns true if no unfired events remain in the list (this allows us
4717 * to avoid repeating afterTriggerMarkEvents).
4718 */
4719static bool
4722 EState *estate,
4723 bool delete_ok)
4724{
4725 bool all_fired = true;
4728 bool local_estate = false;
4730 Relation rel = NULL;
4731 TriggerDesc *trigdesc = NULL;
4732 FmgrInfo *finfo = NULL;
4735 *slot2 = NULL;
4736
4737 /* Make a local EState if need be */
4738 if (estate == NULL)
4739 {
4740 estate = CreateExecutorState();
4741 local_estate = true;
4742 }
4743
4744 /* Make a per-tuple memory context for trigger function calls */
4747 "AfterTriggerTupleContext",
4749
4750 for_each_chunk(chunk, *events)
4751 {
4752 AfterTriggerEvent event;
4753 bool all_fired_in_chunk = true;
4754
4755 for_each_event(event, chunk)
4756 {
4758
4759 /*
4760 * Is it one for me to fire?
4761 */
4762 if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4763 evtshared->ats_firing_id == firing_id)
4764 {
4766 *dst_rInfo;
4767
4768 /*
4769 * So let's fire it... but first, find the correct relation if
4770 * this is not the same relation as before.
4771 */
4772 if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4773 {
4774 rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4775 NULL);
4776 rel = rInfo->ri_RelationDesc;
4777 /* Catch calls with insufficient relcache refcounting */
4779 trigdesc = rInfo->ri_TrigDesc;
4780 /* caution: trigdesc could be NULL here */
4781 finfo = rInfo->ri_TrigFunctions;
4782 instr = rInfo->ri_TrigInstrument;
4783 if (slot1 != NULL)
4784 {
4787 slot1 = slot2 = NULL;
4788 }
4789 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4790 {
4795 }
4796 }
4797
4798 /*
4799 * Look up source and destination partition result rels of a
4800 * cross-partition update event.
4801 */
4802 if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4804 {
4805 Assert(OidIsValid(event->ate_src_part) &&
4806 OidIsValid(event->ate_dst_part));
4808 event->ate_src_part,
4809 rInfo);
4811 event->ate_dst_part,
4812 rInfo);
4813 }
4814 else
4816
4817 /*
4818 * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4819 * still set, so recursive examinations of the event list
4820 * won't try to re-fire it.
4821 */
4822 AfterTriggerExecute(estate, event, rInfo,
4824 trigdesc, finfo, instr,
4826
4827 /*
4828 * Mark the event as done.
4829 */
4830 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4831 event->ate_flags |= AFTER_TRIGGER_DONE;
4832 }
4833 else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4834 {
4835 /* something remains to be done */
4836 all_fired = all_fired_in_chunk = false;
4837 }
4838 }
4839
4840 /* Clear the chunk if delete_ok and nothing left of interest */
4842 {
4843 chunk->freeptr = CHUNK_DATA_START(chunk);
4844 chunk->endfree = chunk->endptr;
4845
4846 /*
4847 * If it's last chunk, must sync event list's tailfree too. Note
4848 * that delete_ok must NOT be passed as true if there could be
4849 * additional AfterTriggerEventList values pointing at this event
4850 * list, since we'd fail to fix their copies of tailfree.
4851 */
4852 if (chunk == events->tail)
4853 events->tailfree = chunk->freeptr;
4854 }
4855 }
4856 if (slot1 != NULL)
4857 {
4860 }
4861
4862 /* Release working resources */
4864
4865 if (local_estate)
4866 {
4868 ExecResetTupleTable(estate->es_tupleTable, false);
4869 FreeExecutorState(estate);
4870 }
4871
4872 return all_fired;
4873}
4874
4875
4876/*
4877 * GetAfterTriggersTableData
4878 *
4879 * Find or create an AfterTriggersTableData struct for the specified
4880 * trigger event (relation + operation type). Ignore existing structs
4881 * marked "closed"; we don't want to put any additional tuples into them,
4882 * nor change their stmt-triggers-fired state.
4883 *
4884 * Note: the AfterTriggersTableData list is allocated in the current
4885 * (sub)transaction's CurTransactionContext. This is OK because
4886 * we don't need it to live past AfterTriggerEndQuery.
4887 */
4890{
4894 ListCell *lc;
4895
4896 /* At this level, cmdType should not be, eg, CMD_MERGE */
4897 Assert(cmdType == CMD_INSERT ||
4898 cmdType == CMD_UPDATE ||
4899 cmdType == CMD_DELETE);
4900
4901 /* Caller should have ensured query_depth is OK. */
4905
4906 foreach(lc, qs->tables)
4907 {
4909 if (table->relid == relid && table->cmdType == cmdType &&
4910 !table->closed)
4911 return table;
4912 }
4913
4915
4917 table->relid = relid;
4918 table->cmdType = cmdType;
4919 qs->tables = lappend(qs->tables, table);
4920
4922
4923 return table;
4924}
4925
4926/*
4927 * Returns a TupleTableSlot suitable for holding the tuples to be put
4928 * into AfterTriggersTableData's transition table tuplestores.
4929 */
4930static TupleTableSlot *
4932 TupleDesc tupdesc)
4933{
4934 /* Create it if not already done. */
4935 if (!table->storeslot)
4936 {
4938
4939 /*
4940 * We need this slot only until AfterTriggerEndQuery, but making it
4941 * last till end-of-subxact is good enough. It'll be freed by
4942 * AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4943 * a different lifespan, so we'd better make a copy of that.
4944 */
4946 tupdesc = CreateTupleDescCopy(tupdesc);
4947 table->storeslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
4949 }
4950
4951 return table->storeslot;
4952}
4953
4954/*
4955 * MakeTransitionCaptureState
4956 *
4957 * Make a TransitionCaptureState object for the given TriggerDesc, target
4958 * relation, and operation type. The TCS object holds all the state needed
4959 * to decide whether to capture tuples in transition tables.
4960 *
4961 * If there are no triggers in 'trigdesc' that request relevant transition
4962 * tables, then return NULL.
4963 *
4964 * The resulting object can be passed to the ExecAR* functions. When
4965 * dealing with child tables, the caller can set tcs_original_insert_tuple
4966 * to avoid having to reconstruct the original tuple in the root table's
4967 * format.
4968 *
4969 * Note that we copy the flags from a parent table into this struct (rather
4970 * than subsequently using the relation's TriggerDesc directly) so that we can
4971 * use it to control collection of transition tuples from child tables.
4972 *
4973 * Per SQL spec, all operations of the same kind (INSERT/UPDATE/DELETE)
4974 * on the same table during one query should share one transition table.
4975 * Therefore, the Tuplestores are owned by an AfterTriggersTableData struct
4976 * looked up using the table OID + CmdType, and are merely referenced by
4977 * the TransitionCaptureState objects we hand out to callers.
4978 */
4980MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
4981{
4983 bool need_old_upd,
4992
4993 if (trigdesc == NULL)
4994 return NULL;
4995
4996 /* Detect which table(s) we need. */
4997 switch (cmdType)
4998 {
4999 case CMD_INSERT:
5002 break;
5003 case CMD_UPDATE:
5006 need_old_del = need_new_ins = false;
5007 break;
5008 case CMD_DELETE:
5011 break;
5012 case CMD_MERGE:
5017 break;
5018 default:
5019 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
5020 /* keep compiler quiet */
5022 break;
5023 }
5025 return NULL;
5026
5027 /* Check state, like AfterTriggerSaveEvent. */
5028 if (afterTriggers.query_depth < 0)
5029 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
5030
5031 /* Be sure we have enough space to record events at this query depth. */
5034
5035 /*
5036 * Find or create AfterTriggersTableData struct(s) to hold the
5037 * tuplestore(s). If there's a matching struct but it's marked closed,
5038 * ignore it; we need a newer one.
5039 *
5040 * Note: MERGE must use the same AfterTriggersTableData structs as INSERT,
5041 * UPDATE, and DELETE, so that any MERGE'd tuples are added to the same
5042 * tuplestores as tuples from any INSERT, UPDATE, or DELETE commands
5043 * running in the same top-level command (e.g., in a writable CTE).
5044 *
5045 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
5046 * allocated in the current (sub)transaction's CurTransactionContext, and
5047 * the tuplestores are managed by the (sub)transaction's resource owner.
5048 * This is sufficient lifespan because we do not allow triggers using
5049 * transition tables to be deferrable; they will be fired during
5050 * AfterTriggerEndQuery, after which it's okay to delete the data.
5051 */
5052 if (need_new_ins)
5054 else
5055 ins_table = NULL;
5056
5059 else
5060 upd_table = NULL;
5061
5062 if (need_old_del)
5064 else
5065 del_table = NULL;
5066
5067 /* Now create required tuplestore(s), if we don't have them already. */
5071
5072 if (need_old_upd && upd_table->old_tuplestore == NULL)
5073 upd_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5074 if (need_new_upd && upd_table->new_tuplestore == NULL)
5075 upd_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5076 if (need_old_del && del_table->old_tuplestore == NULL)
5077 del_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5078 if (need_new_ins && ins_table->new_tuplestore == NULL)
5079 ins_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5080
5083
5084 /* Now build the TransitionCaptureState struct, in caller's context */
5086 state->tcs_delete_old_table = need_old_del;
5087 state->tcs_update_old_table = need_old_upd;
5088 state->tcs_update_new_table = need_new_upd;
5089 state->tcs_insert_new_table = need_new_ins;
5090 state->tcs_insert_private = ins_table;
5091 state->tcs_update_private = upd_table;
5092 state->tcs_delete_private = del_table;
5093
5094 return state;
5095}
5096
5097
5098/* ----------
5099 * AfterTriggerBeginXact()
5100 *
5101 * Called at transaction start (either BEGIN or implicit for single
5102 * statement outside of transaction block).
5103 * ----------
5104 */
5105void
5107{
5108 /*
5109 * Initialize after-trigger state structure to empty
5110 */
5111 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5116
5117 /*
5118 * Verify that there is no leftover state remaining. If these assertions
5119 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5120 * up properly.
5121 */
5129}
5130
5131
5132/* ----------
5133 * AfterTriggerBeginQuery()
5134 *
5135 * Called just before we start processing a single query within a
5136 * transaction (or subtransaction). Most of the real work gets deferred
5137 * until somebody actually tries to queue a trigger event.
5138 * ----------
5139 */
5140void
5142{
5143 /* Increase the query stack depth */
5145}
5146
5147
5148/* ----------
5149 * AfterTriggerEndQuery()
5150 *
5151 * Called after one query has been completely processed. At this time
5152 * we invoke all AFTER IMMEDIATE trigger events queued by the query, and
5153 * transfer deferred trigger events to the global deferred-trigger list.
5154 *
5155 * Note that this must be called BEFORE closing down the executor
5156 * with ExecutorEnd, because we make use of the EState's info about
5157 * target relations. Normally it is called from ExecutorFinish.
5158 * ----------
5159 */
5160void
5162{
5164
5165 /* Must be inside a query, too */
5167
5168 /*
5169 * If we never even got as far as initializing the event stack, there
5170 * certainly won't be any events, so exit quickly.
5171 */
5173 {
5175 return;
5176 }
5177
5178 /*
5179 * Process all immediate-mode triggers queued by the query, and move the
5180 * deferred ones to the main list of deferred events.
5181 *
5182 * Notice that we decide which ones will be fired, and put the deferred
5183 * ones on the main list, before anything is actually fired. This ensures
5184 * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5185 * IMMEDIATE: all events we have decided to defer will be available for it
5186 * to fire.
5187 *
5188 * We loop in case a trigger queues more events at the same query level.
5189 * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5190 * will instead fire any triggers in a dedicated query level. Foreign key
5191 * enforcement triggers do add to the current query level, thanks to their
5192 * passing fire_triggers = false to SPI_execute_snapshot(). Other
5193 * C-language triggers might do likewise.
5194 *
5195 * If we find no firable events, we don't have to increment
5196 * firing_counter.
5197 */
5199
5201 for (;;)
5202 {
5203 if (afterTriggerMarkEvents(&qs->events, &afterTriggers.events, true))
5204 {
5206 AfterTriggerEventChunk *oldtail = qs->events.tail;
5207
5208 if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5209 break; /* all fired */
5210
5211 /*
5212 * Firing a trigger could result in query_stack being repalloc'd,
5213 * so we must recalculate qs after each afterTriggerInvokeEvents
5214 * call. Furthermore, it's unsafe to pass delete_ok = true here,
5215 * because that could cause afterTriggerInvokeEvents to try to
5216 * access qs->events after the stack has been repalloc'd.
5217 */
5219
5220 /*
5221 * We'll need to scan the events list again. To reduce the cost
5222 * of doing so, get rid of completely-fired chunks. We know that
5223 * all events were marked IN_PROGRESS or DONE at the conclusion of
5224 * afterTriggerMarkEvents, so any still-interesting events must
5225 * have been added after that, and so must be in the chunk that
5226 * was then the tail chunk, or in later chunks. So, zap all
5227 * chunks before oldtail. This is approximately the same set of
5228 * events we would have gotten rid of by passing delete_ok = true.
5229 */
5230 Assert(oldtail != NULL);
5231 while (qs->events.head != oldtail)
5233 }
5234 else
5235 break;
5236 }
5237
5238 /*
5239 * Fire batch callbacks before releasing query-level storage and before
5240 * decrementing query_depth. Callbacks may do real work (index probes,
5241 * error reporting).
5242 */
5243 FireAfterTriggerBatchCallbacks(qs->batch_callbacks);
5244
5245 /* Release query-level-local storage, including tuplestores if any */
5247
5250}
5251
5252
5253/*
5254 * AfterTriggerFreeQuery
5255 * Release subsidiary storage for a trigger query level.
5256 * This includes closing down tuplestores.
5257 * Note: it's important for this to be safe if interrupted by an error
5258 * and then called again for the same query level.
5259 */
5260static void
5262{
5263 Tuplestorestate *ts;
5264 List *tables;
5265 ListCell *lc;
5266
5267 /* Drop the trigger events */
5268 afterTriggerFreeEventList(&qs->events);
5269
5270 /* Drop FDW tuplestore if any */
5271 ts = qs->fdw_tuplestore;
5272 qs->fdw_tuplestore = NULL;
5273 if (ts)
5274 tuplestore_end(ts);
5275
5276 /* Release per-table subsidiary storage */
5277 tables = qs->tables;
5278 foreach(lc, tables)
5279 {
5281
5282 ts = table->old_tuplestore;
5283 table->old_tuplestore = NULL;
5284 if (ts)
5285 tuplestore_end(ts);
5286 ts = table->new_tuplestore;
5287 table->new_tuplestore = NULL;
5288 if (ts)
5289 tuplestore_end(ts);
5290 if (table->storeslot)
5291 {
5292 TupleTableSlot *slot = table->storeslot;
5293
5294 table->storeslot = NULL;
5296 }
5297 }
5298
5299 /*
5300 * Now free the AfterTriggersTableData structs and list cells. Reset list
5301 * pointer first; if list_free_deep somehow gets an error, better to leak
5302 * that storage than have an infinite loop.
5303 */
5304 qs->tables = NIL;
5305 list_free_deep(tables);
5306
5307 list_free_deep(qs->batch_callbacks);
5308 qs->batch_callbacks = NIL;
5309}
5310
5311
5312/* ----------
5313 * AfterTriggerFireDeferred()
5314 *
5315 * Called just before the current transaction is committed. At this
5316 * time we invoke all pending DEFERRED triggers.
5317 *
5318 * It is possible for other modules to queue additional deferred triggers
5319 * during pre-commit processing; therefore xact.c may have to call this
5320 * multiple times.
5321 * ----------
5322 */
5323void
5325{
5326 AfterTriggerEventList *events;
5327 bool snap_pushed = false;
5328
5329 /* Must not be inside a query */
5331
5332 /*
5333 * If there are any triggers to fire, make sure we have set a snapshot for
5334 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5335 * can't assume ActiveSnapshot is valid on entry.)
5336 */
5337 events = &afterTriggers.events;
5338 if (events->head != NULL)
5339 {
5341 snap_pushed = true;
5342 }
5343
5344 /*
5345 * Run all the remaining triggers. Loop until they are all gone, in case
5346 * some trigger queues more for us to do.
5347 */
5349 while (afterTriggerMarkEvents(events, NULL, false))
5350 {
5352
5353 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5354 break; /* all fired */
5355 }
5356
5357 /* Flush any fast-path batches accumulated by the triggers just fired. */
5359
5361
5362 /*
5363 * We don't bother freeing the event list or batch_callbacks, since they
5364 * will go away anyway (and more efficiently than via pfree) in
5365 * AfterTriggerEndXact.
5366 */
5367
5368 if (snap_pushed)
5370}
5371
5372
5373/* ----------
5374 * AfterTriggerEndXact()
5375 *
5376 * The current transaction is finishing.
5377 *
5378 * Any unfired triggers are canceled so we simply throw
5379 * away anything we know.
5380 *
5381 * Note: it is possible for this to be called repeatedly in case of
5382 * error during transaction abort; therefore, do not complain if
5383 * already closed down.
5384 * ----------
5385 */
5386void
5388{
5389 /*
5390 * Forget the pending-events list.
5391 *
5392 * Since all the info is in TopTransactionContext or children thereof, we
5393 * don't really need to do anything to reclaim memory. However, the
5394 * pending-events list could be large, and so it's useful to discard it as
5395 * soon as possible --- especially if we are aborting because we ran out
5396 * of memory for the list!
5397 */
5399 {
5405 }
5406
5407 /*
5408 * Forget any subtransaction state as well. Since this can't be very
5409 * large, we let the eventual reset of TopTransactionContext free the
5410 * memory instead of doing it here.
5411 */
5414
5415
5416 /*
5417 * Forget the query stack and constraint-related state information. As
5418 * with the subtransaction state information, we don't bother freeing the
5419 * memory here.
5420 */
5424
5425 /* No more afterTriggers manipulation until next transaction starts. */
5427
5429
5433}
5434
5435/*
5436 * AfterTriggerBeginSubXact()
5437 *
5438 * Start a subtransaction.
5439 */
5440void
5442{
5443 int my_level = GetCurrentTransactionNestLevel();
5444
5445 /*
5446 * Allocate more space in the trans_stack if needed. (Note: because the
5447 * minimum nest level of a subtransaction is 2, we waste the first couple
5448 * entries of the array; not worth the notational effort to avoid it.)
5449 */
5450 while (my_level >= afterTriggers.maxtransdepth)
5451 {
5453 {
5454 /* Arbitrarily initialize for max of 8 subtransaction levels */
5457 8 * sizeof(AfterTriggersTransData));
5459 }
5460 else
5461 {
5462 /* repalloc will keep the stack in the same context */
5464
5469 }
5470 }
5471
5472 /*
5473 * Push the current information into the stack. The SET CONSTRAINTS state
5474 * is not saved until/unless changed. Likewise, we don't make a
5475 * per-subtransaction event context until needed.
5476 */
5477 afterTriggers.trans_stack[my_level].state = NULL;
5481}
5482
5483/*
5484 * AfterTriggerEndSubXact()
5485 *
5486 * The current subtransaction is ending.
5487 */
5488void
5490{
5491 int my_level = GetCurrentTransactionNestLevel();
5493 AfterTriggerEvent event;
5496
5497 /*
5498 * Pop the prior state if needed.
5499 */
5500 if (isCommit)
5501 {
5503 /* If we saved a prior state, we don't need it anymore */
5505 if (state != NULL)
5506 pfree(state);
5507 /* this avoids double pfree if error later: */
5508 afterTriggers.trans_stack[my_level].state = NULL;
5511 }
5512 else
5513 {
5514 /*
5515 * Aborting. It is possible subxact start failed before calling
5516 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5517 * trans_stack levels that aren't there.
5518 */
5519 if (my_level >= afterTriggers.maxtransdepth)
5520 return;
5521
5522 /*
5523 * Release query-level storage for queries being aborted, and restore
5524 * query_depth to its pre-subxact value. This assumes that a
5525 * subtransaction will not add events to query levels started in a
5526 * earlier transaction state.
5527 */
5529 {
5533 }
5536
5537 /*
5538 * Restore the global deferred-event list to its former length,
5539 * discarding any events queued by the subxact.
5540 */
5542 &afterTriggers.trans_stack[my_level].events);
5543
5544 /*
5545 * Restore the trigger state. If the saved state is NULL, then this
5546 * subxact didn't save it, so it doesn't need restoring.
5547 */
5549 if (state != NULL)
5550 {
5553 }
5554 /* this avoids double pfree if error later: */
5555 afterTriggers.trans_stack[my_level].state = NULL;
5556
5557 /*
5558 * Scan for any remaining deferred events that were marked DONE or IN
5559 * PROGRESS by this subxact or a child, and un-mark them. We can
5560 * recognize such events because they have a firing ID greater than or
5561 * equal to the firing_counter value we saved at subtransaction start.
5562 * (This essentially assumes that the current subxact includes all
5563 * subxacts started after it.)
5564 */
5567 {
5569
5570 if (event->ate_flags &
5572 {
5573 if (evtshared->ats_firing_id >= subxact_firing_id)
5574 event->ate_flags &=
5576 }
5577 }
5578 }
5579
5580 /* Reset in case a callback threw an error while firing. */
5582}
5583
5584/*
5585 * Get the transition table for the given event and depending on whether we are
5586 * processing the old or the new tuple.
5587 */
5588static Tuplestorestate *
5592 TransitionCaptureState *transition_capture)
5593{
5594 Tuplestorestate *tuplestore = NULL;
5595 bool delete_old_table = transition_capture->tcs_delete_old_table;
5596 bool update_old_table = transition_capture->tcs_update_old_table;
5597 bool update_new_table = transition_capture->tcs_update_new_table;
5598 bool insert_new_table = transition_capture->tcs_insert_new_table;
5599
5600 /*
5601 * For INSERT events NEW should be non-NULL, for DELETE events OLD should
5602 * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
5603 * non-NULL. But for UPDATE events fired for capturing transition tuples
5604 * during UPDATE partition-key row movement, OLD is NULL when the event is
5605 * for a row being inserted, whereas NEW is NULL when the event is for a
5606 * row being deleted.
5607 */
5609 TupIsNull(oldslot)));
5611 TupIsNull(newslot)));
5612
5613 if (!TupIsNull(oldslot))
5614 {
5616 if (event == TRIGGER_EVENT_DELETE && delete_old_table)
5617 tuplestore = transition_capture->tcs_delete_private->old_tuplestore;
5618 else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
5619 tuplestore = transition_capture->tcs_update_private->old_tuplestore;
5620 }
5621 else if (!TupIsNull(newslot))
5622 {
5624 if (event == TRIGGER_EVENT_INSERT && insert_new_table)
5625 tuplestore = transition_capture->tcs_insert_private->new_tuplestore;
5626 else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
5627 tuplestore = transition_capture->tcs_update_private->new_tuplestore;
5628 }
5629
5630 return tuplestore;
5631}
5632
5633/*
5634 * Add the given heap tuple to the given tuplestore, applying the conversion
5635 * map if necessary.
5636 *
5637 * If original_insert_tuple is given, we can add that tuple without conversion.
5638 */
5639static void
5641 int event,
5642 TransitionCaptureState *transition_capture,
5644 TupleTableSlot *slot,
5646 Tuplestorestate *tuplestore)
5647{
5648 TupleConversionMap *map;
5649
5650 /*
5651 * Nothing needs to be done if we don't have a tuplestore.
5652 */
5653 if (tuplestore == NULL)
5654 return;
5655
5658 else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5659 {
5661 TupleTableSlot *storeslot;
5662
5663 switch (event)
5664 {
5666 table = transition_capture->tcs_insert_private;
5667 break;
5669 table = transition_capture->tcs_update_private;
5670 break;
5672 table = transition_capture->tcs_delete_private;
5673 break;
5674 default:
5675 elog(ERROR, "invalid after-trigger event code: %d", event);
5676 table = NULL; /* keep compiler quiet */
5677 break;
5678 }
5679
5680 storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5681 execute_attr_map_slot(map->attrMap, slot, storeslot);
5682 tuplestore_puttupleslot(tuplestore, storeslot);
5683 }
5684 else
5685 tuplestore_puttupleslot(tuplestore, slot);
5686}
5687
5688/* ----------
5689 * AfterTriggerEnlargeQueryState()
5690 *
5691 * Prepare the necessary state so that we can record AFTER trigger events
5692 * queued by a query. It is allowed to have nested queries within a
5693 * (sub)transaction, so we need to have separate state for each query
5694 * nesting level.
5695 * ----------
5696 */
5697static void
5699{
5701
5703
5705 {
5706 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5707
5712 }
5713 else
5714 {
5715 /* repalloc will keep the stack in the same context */
5718 old_alloc * 2);
5719
5724 }
5725
5726 /* Initialize new array entries to empty */
5728 {
5730
5731 qs->events.head = NULL;
5732 qs->events.tail = NULL;
5733 qs->events.tailfree = NULL;
5734 qs->fdw_tuplestore = NULL;
5735 qs->tables = NIL;
5736 qs->batch_callbacks = NIL;
5737
5738 ++init_depth;
5739 }
5740}
5741
5742/*
5743 * Create an empty SetConstraintState with room for numalloc trigstates
5744 */
5745static SetConstraintState
5746SetConstraintStateCreate(int numalloc)
5747{
5749
5750 /* Behave sanely with numalloc == 0 */
5751 if (numalloc <= 0)
5752 numalloc = 1;
5753
5754 /*
5755 * We assume that zeroing will correctly initialize the state values.
5756 */
5759 offsetof(SetConstraintStateData, trigstates) +
5760 numalloc * sizeof(SetConstraintTriggerData));
5761
5762 state->numalloc = numalloc;
5763
5764 return state;
5765}
5766
5767/*
5768 * Copy a SetConstraintState
5769 */
5770static SetConstraintState
5772{
5774
5776
5777 state->all_isset = origstate->all_isset;
5778 state->all_isdeferred = origstate->all_isdeferred;
5779 state->numstates = origstate->numstates;
5780 memcpy(state->trigstates, origstate->trigstates,
5781 origstate->numstates * sizeof(SetConstraintTriggerData));
5782
5783 return state;
5784}
5785
5786/*
5787 * Add a per-trigger item to a SetConstraintState. Returns possibly-changed
5788 * pointer to the state object (it will change if we have to repalloc).
5789 */
5790static SetConstraintState
5792 Oid tgoid, bool tgisdeferred)
5793{
5794 if (state->numstates >= state->numalloc)
5795 {
5796 int newalloc = state->numalloc * 2;
5797
5798 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5801 offsetof(SetConstraintStateData, trigstates) +
5803 state->numalloc = newalloc;
5804 Assert(state->numstates < state->numalloc);
5805 }
5806
5807 state->trigstates[state->numstates].sct_tgoid = tgoid;
5808 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5809 state->numstates++;
5810
5811 return state;
5812}
5813
5814/* ----------
5815 * AfterTriggerSetState()
5816 *
5817 * Execute the SET CONSTRAINTS ... utility command.
5818 * ----------
5819 */
5820void
5822{
5823 int my_level = GetCurrentTransactionNestLevel();
5824
5825 /* If we haven't already done so, initialize our state. */
5826 if (afterTriggers.state == NULL)
5828
5829 /*
5830 * If in a subtransaction, and we didn't save the current state already,
5831 * save it so it can be restored if the subtransaction aborts.
5832 */
5833 if (my_level > 1 &&
5834 afterTriggers.trans_stack[my_level].state == NULL)
5835 {
5836 afterTriggers.trans_stack[my_level].state =
5838 }
5839
5840 /*
5841 * Handle SET CONSTRAINTS ALL ...
5842 */
5843 if (stmt->constraints == NIL)
5844 {
5845 /*
5846 * Forget any previous SET CONSTRAINTS commands in this transaction.
5847 */
5849
5850 /*
5851 * Set the per-transaction ALL state to known.
5852 */
5855 }
5856 else
5857 {
5860 List *conoidlist = NIL;
5861 List *tgoidlist = NIL;
5862 ListCell *lc;
5863
5864 /*
5865 * Handle SET CONSTRAINTS constraint-name [, ...]
5866 *
5867 * First, identify all the named constraints and make a list of their
5868 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5869 * the same name within a schema, the specifications are not
5870 * necessarily unique. Our strategy is to target all matching
5871 * constraints within the first search-path schema that has any
5872 * matches, but disregard matches in schemas beyond the first match.
5873 * (This is a bit odd but it's the historical behavior.)
5874 *
5875 * A constraint in a partitioned table may have corresponding
5876 * constraints in the partitions. Grab those too.
5877 */
5879
5880 foreach(lc, stmt->constraints)
5881 {
5882 RangeVar *constraint = lfirst(lc);
5883 bool found;
5885 ListCell *nslc;
5886
5887 if (constraint->catalogname)
5888 {
5889 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5890 ereport(ERROR,
5892 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5893 constraint->catalogname, constraint->schemaname,
5894 constraint->relname)));
5895 }
5896
5897 /*
5898 * If we're given the schema name with the constraint, look only
5899 * in that schema. If given a bare constraint name, use the
5900 * search path to find the first matching constraint.
5901 */
5902 if (constraint->schemaname)
5903 {
5905 false);
5906
5908 }
5909 else
5910 {
5912 }
5913
5914 found = false;
5915 foreach(nslc, namespacelist)
5916 {
5919 ScanKeyData skey[2];
5920 HeapTuple tup;
5921
5922 ScanKeyInit(&skey[0],
5925 CStringGetDatum(constraint->relname));
5926 ScanKeyInit(&skey[1],
5930
5932 true, NULL, 2, skey);
5933
5935 {
5937
5938 if (con->condeferrable)
5939 conoidlist = lappend_oid(conoidlist, con->oid);
5940 else if (stmt->deferred)
5941 ereport(ERROR,
5943 errmsg("constraint \"%s\" is not deferrable",
5944 constraint->relname)));
5945 found = true;
5946 }
5947
5949
5950 /*
5951 * Once we've found a matching constraint we do not search
5952 * later parts of the search path.
5953 */
5954 if (found)
5955 break;
5956 }
5957
5959
5960 /*
5961 * Not found ?
5962 */
5963 if (!found)
5964 ereport(ERROR,
5966 errmsg("constraint \"%s\" does not exist",
5967 constraint->relname)));
5968 }
5969
5970 /*
5971 * Scan for any possible descendants of the constraints. We append
5972 * whatever we find to the same list that we're scanning; this has the
5973 * effect that we create new scans for those, too, so if there are
5974 * further descendents, we'll also catch them.
5975 */
5976 foreach(lc, conoidlist)
5977 {
5978 Oid parent = lfirst_oid(lc);
5980 SysScanDesc scan;
5981 HeapTuple tuple;
5982
5983 ScanKeyInit(&key,
5986 ObjectIdGetDatum(parent));
5987
5988 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5989
5990 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5991 {
5993
5994 conoidlist = lappend_oid(conoidlist, con->oid);
5995 }
5996
5997 systable_endscan(scan);
5998 }
5999
6001
6002 /*
6003 * Now, locate the trigger(s) implementing each of these constraints,
6004 * and make a list of their OIDs.
6005 */
6007
6008 foreach(lc, conoidlist)
6009 {
6010 Oid conoid = lfirst_oid(lc);
6013 HeapTuple htup;
6014
6018 ObjectIdGetDatum(conoid));
6019
6021 NULL, 1, &skey);
6022
6024 {
6026
6027 /*
6028 * Silently skip triggers that are marked as non-deferrable in
6029 * pg_trigger. This is not an error condition, since a
6030 * deferrable RI constraint may have some non-deferrable
6031 * actions.
6032 */
6033 if (pg_trigger->tgdeferrable)
6035 }
6036
6038 }
6039
6041
6042 /*
6043 * Now we can set the trigger states of individual triggers for this
6044 * xact.
6045 */
6046 foreach(lc, tgoidlist)
6047 {
6048 Oid tgoid = lfirst_oid(lc);
6050 bool found = false;
6051 int i;
6052
6053 for (i = 0; i < state->numstates; i++)
6054 {
6055 if (state->trigstates[i].sct_tgoid == tgoid)
6056 {
6057 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
6058 found = true;
6059 break;
6060 }
6061 }
6062 if (!found)
6063 {
6065 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
6066 }
6067 }
6068 }
6069
6070 /*
6071 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
6072 * checks against that constraint must be made when the SET CONSTRAINTS
6073 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
6074 * apply retroactively. We've updated the constraints state, so scan the
6075 * list of previously deferred events to fire any that have now become
6076 * immediate.
6077 *
6078 * Obviously, if this was SET ... DEFERRED then it can't have converted
6079 * any unfired events to immediate, so we need do nothing in that case.
6080 */
6081 if (!stmt->deferred)
6082 {
6084 bool snapshot_set = false;
6085
6087 while (afterTriggerMarkEvents(events, NULL, true))
6088 {
6090
6091 /*
6092 * Make sure a snapshot has been established in case trigger
6093 * functions need one. Note that we avoid setting a snapshot if
6094 * we don't find at least one trigger that has to be fired now.
6095 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6096 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6097 * at the start of a transaction it's not possible for any trigger
6098 * events to be queued yet.)
6099 */
6100 if (!snapshot_set)
6101 {
6103 snapshot_set = true;
6104 }
6105
6106 /*
6107 * We can delete fired events if we are at top transaction level,
6108 * but we'd better not if inside a subtransaction, since the
6109 * subtransaction could later get rolled back.
6110 */
6112 !IsSubTransaction()))
6113 break; /* all fired */
6114 }
6115
6116 /*
6117 * Flush any fast-path batches accumulated by the triggers just fired.
6118 */
6123
6124 if (snapshot_set)
6126 }
6127}
6128
6129/* ----------
6130 * AfterTriggerPendingOnRel()
6131 * Test to see if there are any pending after-trigger events for rel.
6132 *
6133 * This is used by TRUNCATE, CLUSTER, ALTER TABLE, etc to detect whether
6134 * it is unsafe to perform major surgery on a relation. Note that only
6135 * local pending events are examined. We assume that having exclusive lock
6136 * on a rel guarantees there are no unserviced events in other backends ---
6137 * but having a lock does not prevent there being such events in our own.
6138 *
6139 * In some scenarios it'd be reasonable to remove pending events (more
6140 * specifically, mark them DONE by the current subxact) but without a lot
6141 * of knowledge of the trigger semantics we can't do this in general.
6142 * ----------
6143 */
6144bool
6146{
6147 AfterTriggerEvent event;
6149 int depth;
6150
6151 /* Scan queued events */
6153 {
6155
6156 /*
6157 * We can ignore completed events. (Even if a DONE flag is rolled
6158 * back by subxact abort, it's OK because the effects of the TRUNCATE
6159 * or whatever must get rolled back too.)
6160 */
6161 if (event->ate_flags & AFTER_TRIGGER_DONE)
6162 continue;
6163
6164 if (evtshared->ats_relid == relid)
6165 return true;
6166 }
6167
6168 /*
6169 * Also scan events queued by incomplete queries. This could only matter
6170 * if TRUNCATE/etc is executed by a function or trigger within an updating
6171 * query on the same relation, which is pretty perverse, but let's check.
6172 */
6173 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6174 {
6176 {
6178
6179 if (event->ate_flags & AFTER_TRIGGER_DONE)
6180 continue;
6181
6182 if (evtshared->ats_relid == relid)
6183 return true;
6184 }
6185 }
6186
6187 return false;
6188}
6189
6190/* ----------
6191 * AfterTriggerSaveEvent()
6192 *
6193 * Called by ExecA[RS]...Triggers() to queue up the triggers that should
6194 * be fired for an event.
6195 *
6196 * NOTE: this is called whenever there are any triggers associated with
6197 * the event (even if they are disabled). This function decides which
6198 * triggers actually need to be queued. It is also called after each row,
6199 * even if there are no triggers for that event, if there are any AFTER
6200 * STATEMENT triggers for the statement which use transition tables, so that
6201 * the transition tuplestores can be built. Furthermore, if the transition
6202 * capture is happening for UPDATEd rows being moved to another partition due
6203 * to the partition-key being changed, then this function is called once when
6204 * the row is deleted (to capture OLD row), and once when the row is inserted
6205 * into another partition (to capture NEW row). This is done separately because
6206 * DELETE and INSERT happen on different tables.
6207 *
6208 * Transition tuplestores are built now, rather than when events are pulled
6209 * off of the queue because AFTER ROW triggers are allowed to select from the
6210 * transition tables for the statement.
6211 *
6212 * This contains special support to queue the update events for the case where
6213 * a partitioned table undergoing a cross-partition update may have foreign
6214 * keys pointing into it. Normally, a partitioned table's row triggers are
6215 * not fired because the leaf partition(s) which are modified as a result of
6216 * the operation on the partitioned table contain the same triggers which are
6217 * fired instead. But that general scheme can cause problematic behavior with
6218 * foreign key triggers during cross-partition updates, which are implemented
6219 * as DELETE on the source partition followed by INSERT into the destination
6220 * partition. Specifically, firing DELETE triggers would lead to the wrong
6221 * foreign key action to be enforced considering that the original command is
6222 * UPDATE; in this case, this function is called with relinfo as the
6223 * partitioned table, and src_partinfo and dst_partinfo referring to the
6224 * source and target leaf partitions, respectively.
6225 *
6226 * is_crosspart_update is true either when a DELETE event is fired on the
6227 * source partition (which is to be ignored) or an UPDATE event is fired on
6228 * the root partitioned table.
6229 * ----------
6230 */
6231static void
6235 int event, bool row_trigger,
6238 TransitionCaptureState *transition_capture,
6240{
6241 Relation rel = relinfo->ri_RelationDesc;
6242 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
6245 char relkind = rel->rd_rel->relkind;
6246 int tgtype_event;
6247 int tgtype_level;
6248 int i;
6249 Tuplestorestate *fdw_tuplestore = NULL;
6250
6251 /*
6252 * Check state. We use a normal test not Assert because it is possible to
6253 * reach here in the wrong state given misconfigured RI triggers, in
6254 * particular deferring a cascade action trigger.
6255 */
6256 if (afterTriggers.query_depth < 0)
6257 elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
6258
6259 /* Be sure we have enough space to record events at this query depth. */
6262
6263 /*
6264 * If the directly named relation has any triggers with transition tables,
6265 * then we need to capture transition tuples.
6266 */
6267 if (row_trigger && transition_capture != NULL)
6268 {
6270
6271 /*
6272 * Capture the old tuple in the appropriate transition table based on
6273 * the event.
6274 */
6275 if (!TupIsNull(oldslot))
6276 {
6277 Tuplestorestate *old_tuplestore;
6278
6279 old_tuplestore = GetAfterTriggersTransitionTable(event,
6280 oldslot,
6281 NULL,
6282 transition_capture);
6283 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6284 oldslot, NULL, old_tuplestore);
6285 }
6286
6287 /*
6288 * Capture the new tuple in the appropriate transition table based on
6289 * the event.
6290 */
6291 if (!TupIsNull(newslot))
6292 {
6293 Tuplestorestate *new_tuplestore;
6294
6295 new_tuplestore = GetAfterTriggersTransitionTable(event,
6296 NULL,
6297 newslot,
6298 transition_capture);
6299 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6300 newslot, original_insert_tuple, new_tuplestore);
6301 }
6302
6303 /*
6304 * If transition tables are the only reason we're here, return. As
6305 * mentioned above, we can also be here during update tuple routing in
6306 * presence of transition tables, in which case this function is
6307 * called separately for OLD and NEW, so we expect exactly one of them
6308 * to be NULL.
6309 */
6310 if (trigdesc == NULL ||
6311 (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
6312 (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
6313 (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
6315 return;
6316 }
6317
6318 /*
6319 * We normally don't see partitioned tables here for row level triggers
6320 * except in the special case of a cross-partition update. In that case,
6321 * nodeModifyTable.c:ExecCrossPartitionUpdateForeignKey() calls here to
6322 * queue an update event on the root target partitioned table, also
6323 * passing the source and destination partitions and their tuples.
6324 */
6326 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
6328 TRIGGER_FIRED_BY_UPDATE(event) &&
6330
6331 /*
6332 * Validate the event code and collect the associated tuple CTIDs.
6333 *
6334 * The event code will be used both as a bitmask and an array offset, so
6335 * validation is important to make sure we don't walk off the edge of our
6336 * arrays.
6337 *
6338 * Also, if we're considering statement-level triggers, check whether we
6339 * already queued a set of them for this event, and cancel the prior set
6340 * if so. This preserves the behavior that statement-level triggers fire
6341 * just once per statement and fire after row-level triggers.
6342 */
6343 switch (event)
6344 {
6347 if (row_trigger)
6348 {
6349 Assert(oldslot == NULL);
6350 Assert(newslot != NULL);
6351 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
6352 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6353 }
6354 else
6355 {
6356 Assert(oldslot == NULL);
6357 Assert(newslot == NULL);
6358 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6359 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6361 CMD_INSERT, event);
6362 }
6363 break;
6366 if (row_trigger)
6367 {
6368 Assert(oldslot != NULL);
6369 Assert(newslot == NULL);
6370 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6371 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6372 }
6373 else
6374 {
6375 Assert(oldslot == NULL);
6376 Assert(newslot == NULL);
6377 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6378 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6380 CMD_DELETE, event);
6381 }
6382 break;
6385 if (row_trigger)
6386 {
6387 Assert(oldslot != NULL);
6388 Assert(newslot != NULL);
6389 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6390 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
6391
6392 /*
6393 * Also remember the OIDs of partitions to fetch these tuples
6394 * out of later in AfterTriggerExecute().
6395 */
6396 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6397 {
6399 new_event.ate_src_part =
6400 RelationGetRelid(src_partinfo->ri_RelationDesc);
6401 new_event.ate_dst_part =
6402 RelationGetRelid(dst_partinfo->ri_RelationDesc);
6403 }
6404 }
6405 else
6406 {
6407 Assert(oldslot == NULL);
6408 Assert(newslot == NULL);
6409 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6410 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6412 CMD_UPDATE, event);
6413 }
6414 break;
6417 Assert(oldslot == NULL);
6418 Assert(newslot == NULL);
6419 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6420 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6421 break;
6422 default:
6423 elog(ERROR, "invalid after-trigger event code: %d", event);
6424 tgtype_event = 0; /* keep compiler quiet */
6425 break;
6426 }
6427
6428 /* Determine flags */
6429 if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
6430 {
6431 if (row_trigger && event == TRIGGER_EVENT_UPDATE)
6432 {
6433 if (relkind == RELKIND_PARTITIONED_TABLE)
6435 else
6436 new_event.ate_flags = AFTER_TRIGGER_2CTID;
6437 }
6438 else
6439 new_event.ate_flags = AFTER_TRIGGER_1CTID;
6440 }
6441
6442 /* else, we'll initialize ate_flags for each trigger */
6443
6445
6446 /*
6447 * Must convert/copy the source and destination partition tuples into the
6448 * root partitioned table's format/slot, because the processing in the
6449 * loop below expects both oldslot and newslot tuples to be in that form.
6450 */
6451 if (row_trigger && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6452 {
6454 TupleConversionMap *map;
6455
6458 if (map)
6460 oldslot,
6461 rootslot);
6462 else
6464
6467 if (map)
6469 newslot,
6470 rootslot);
6471 else
6473 }
6474
6475 for (i = 0; i < trigdesc->numtriggers; i++)
6476 {
6477 Trigger *trigger = &trigdesc->triggers[i];
6478
6479 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
6482 tgtype_event))
6483 continue;
6484 if (!TriggerEnabled(estate, relinfo, trigger, event,
6486 continue;
6487
6488 if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
6489 {
6490 if (fdw_tuplestore == NULL)
6491 {
6492 fdw_tuplestore = GetCurrentFDWTuplestore();
6494 }
6495 else
6496 /* subsequent event for the same tuple */
6498 }
6499
6500 /*
6501 * If the trigger is a foreign key enforcement trigger, there are
6502 * certain cases where we can skip queueing the event because we can
6503 * tell by inspection that the FK constraint will still pass. There
6504 * are also some cases during cross-partition updates of a partitioned
6505 * table where queuing the event can be skipped.
6506 */
6508 {
6509 switch (RI_FKey_trigger_type(trigger->tgfoid))
6510 {
6511 case RI_TRIGGER_PK:
6512
6513 /*
6514 * For cross-partitioned updates of partitioned PK table,
6515 * skip the event fired by the component delete on the
6516 * source leaf partition unless the constraint originates
6517 * in the partition itself (!tgisclone), because the
6518 * update event that will be fired on the root
6519 * (partitioned) target table will be used to perform the
6520 * necessary foreign key enforcement action.
6521 */
6522 if (is_crosspart_update &&
6523 TRIGGER_FIRED_BY_DELETE(event) &&
6524 trigger->tgisclone)
6525 continue;
6526
6527 /* Update or delete on trigger's PK table */
6529 oldslot, newslot))
6530 {
6531 /* skip queuing this event */
6532 continue;
6533 }
6534 break;
6535
6536 case RI_TRIGGER_FK:
6537
6538 /*
6539 * Update on trigger's FK table. We can skip the update
6540 * event fired on a partitioned table during a
6541 * cross-partition of that table, because the insert event
6542 * that is fired on the destination leaf partition would
6543 * suffice to perform the necessary foreign key check.
6544 * Moreover, RI_FKey_fk_upd_check_required() expects to be
6545 * passed a tuple that contains system attributes, most of
6546 * which are not present in the virtual slot belonging to
6547 * a partitioned table.
6548 */
6549 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
6551 oldslot, newslot))
6552 {
6553 /* skip queuing this event */
6554 continue;
6555 }
6556 break;
6557
6558 case RI_TRIGGER_NONE:
6559
6560 /*
6561 * Not an FK trigger. No need to queue the update event
6562 * fired during a cross-partitioned update of a
6563 * partitioned table, because the same row trigger must be
6564 * present in the leaf partition(s) that are affected as
6565 * part of this update and the events fired on them are
6566 * queued instead.
6567 */
6568 if (row_trigger &&
6569 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6570 continue;
6571 break;
6572 }
6573 }
6574
6575 /*
6576 * If the trigger is a deferred unique constraint check trigger, only
6577 * queue it if the unique constraint was potentially violated, which
6578 * we know from index insertion time.
6579 */
6580 if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
6581 {
6582 if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
6583 continue; /* Uniqueness definitely not violated */
6584 }
6585
6586 /*
6587 * Fill in event structure and add it to the current query's queue.
6588 * Note we set ats_table to NULL whenever this trigger doesn't use
6589 * transition tables, to improve sharability of the shared event data.
6590 */
6591 new_shared.ats_event =
6592 (event & TRIGGER_EVENT_OPMASK) |
6594 (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
6595 (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
6596 new_shared.ats_tgoid = trigger->tgoid;
6597 new_shared.ats_relid = RelationGetRelid(rel);
6598 new_shared.ats_rolid = GetUserId();
6599 new_shared.ats_firing_id = 0;
6600 if ((trigger->tgoldtable || trigger->tgnewtable) &&
6601 transition_capture != NULL)
6602 {
6603 switch (event)
6604 {
6606 new_shared.ats_table = transition_capture->tcs_insert_private;
6607 break;
6609 new_shared.ats_table = transition_capture->tcs_update_private;
6610 break;
6612 new_shared.ats_table = transition_capture->tcs_delete_private;
6613 break;
6614 default:
6615 /* Must be TRUNCATE, see switch above */
6616 new_shared.ats_table = NULL;
6617 break;
6618 }
6619 }
6620 else
6621 new_shared.ats_table = NULL;
6622 new_shared.ats_modifiedcols = modifiedCols;
6623
6626 }
6627
6628 /*
6629 * Finally, spool any foreign tuple(s). The tuplestore squashes them to
6630 * minimal tuples, so this loses any system columns. The executor lost
6631 * those columns before us, for an unrelated reason, so this is fine.
6632 */
6633 if (fdw_tuplestore)
6634 {
6635 if (oldslot != NULL)
6636 tuplestore_puttupleslot(fdw_tuplestore, oldslot);
6637 if (newslot != NULL)
6638 tuplestore_puttupleslot(fdw_tuplestore, newslot);
6639 }
6640}
6641
6642/*
6643 * Detect whether we already queued BEFORE STATEMENT triggers for the given
6644 * relation + operation, and set the flag so the next call will report "true".
6645 */
6646static bool
6648{
6649 bool result;
6651
6652 /* Check state, like AfterTriggerSaveEvent. */
6653 if (afterTriggers.query_depth < 0)
6654 elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6655
6656 /* Be sure we have enough space to record events at this query depth. */
6659
6660 /*
6661 * We keep this state in the AfterTriggersTableData that also holds
6662 * transition tables for the relation + operation. In this way, if we are
6663 * forced to make a new set of transition tables because more tuples get
6664 * entered after we've already fired triggers, we will allow a new set of
6665 * statement triggers to get queued.
6666 */
6667 table = GetAfterTriggersTableData(relid, cmdType);
6668 result = table->before_trig_done;
6669 table->before_trig_done = true;
6670 return result;
6671}
6672
6673/*
6674 * If we previously queued a set of AFTER STATEMENT triggers for the given
6675 * relation + operation, and they've not been fired yet, cancel them. The
6676 * caller will queue a fresh set that's after any row-level triggers that may
6677 * have been queued by the current sub-statement, preserving (as much as
6678 * possible) the property that AFTER ROW triggers fire before AFTER STATEMENT
6679 * triggers, and that the latter only fire once. This deals with the
6680 * situation where several FK enforcement triggers sequentially queue triggers
6681 * for the same table into the same trigger query level. We can't fully
6682 * prevent odd behavior though: if there are AFTER ROW triggers taking
6683 * transition tables, we don't want to change the transition tables once the
6684 * first such trigger has seen them. In such a case, any additional events
6685 * will result in creating new transition tables and allowing new firings of
6686 * statement triggers.
6687 *
6688 * This also saves the current event list location so that a later invocation
6689 * of this function can cheaply find the triggers we're about to queue and
6690 * cancel them.
6691 */
6692static void
6693cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
6694{
6697
6698 /*
6699 * We keep this state in the AfterTriggersTableData that also holds
6700 * transition tables for the relation + operation. In this way, if we are
6701 * forced to make a new set of transition tables because more tuples get
6702 * entered after we've already fired triggers, we will allow a new set of
6703 * statement triggers to get queued without canceling the old ones.
6704 */
6705 table = GetAfterTriggersTableData(relid, cmdType);
6706
6707 if (table->after_trig_done)
6708 {
6709 /*
6710 * We want to start scanning from the tail location that existed just
6711 * before we inserted any statement triggers. But the events list
6712 * might've been entirely empty then, in which case scan from the
6713 * current head.
6714 */
6715 AfterTriggerEvent event;
6717
6718 if (table->after_trig_events.tail)
6719 {
6720 chunk = table->after_trig_events.tail;
6721 event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6722 }
6723 else
6724 {
6725 chunk = qs->events.head;
6726 event = NULL;
6727 }
6728
6729 for_each_chunk_from(chunk)
6730 {
6731 if (event == NULL)
6732 event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6733 for_each_event_from(event, chunk)
6734 {
6736
6737 /*
6738 * Exit loop when we reach events that aren't AS triggers for
6739 * the target relation.
6740 */
6741 if (evtshared->ats_relid != relid)
6742 goto done;
6743 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6744 goto done;
6745 if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6746 goto done;
6747 if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6748 goto done;
6749 /* OK, mark it DONE */
6750 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6751 event->ate_flags |= AFTER_TRIGGER_DONE;
6752 }
6753 /* signal we must reinitialize event ptr for next chunk */
6754 event = NULL;
6755 }
6756 }
6757done:
6758
6759 /* In any case, save current insertion point for next time */
6760 table->after_trig_done = true;
6761 table->after_trig_events = qs->events;
6762}
6763
6764/*
6765 * GUC assign_hook for session_replication_role
6766 */
6767void
6769{
6770 /*
6771 * Must flush the plan cache when changing replication role; but don't
6772 * flush unnecessarily.
6773 */
6776}
6777
6778/*
6779 * SQL function pg_trigger_depth()
6780 */
6781Datum
6783{
6785}
6786
6787/*
6788 * Check whether a trigger modified a virtual generated column and replace the
6789 * value with null if so.
6790 *
6791 * We need to check this so that we don't end up storing a non-null value in a
6792 * virtual generated column.
6793 *
6794 * We don't need to check for stored generated columns, since those will be
6795 * overwritten later anyway.
6796 */
6797static HeapTuple
6799{
6800 if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
6801 return tuple;
6802
6803 for (int i = 0; i < tupdesc->natts; i++)
6804 {
6805 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6806 {
6807 if (!heap_attisnull(tuple, i + 1, tupdesc))
6808 {
6809 int replCol = i + 1;
6810 Datum replValue = 0;
6811 bool replIsnull = true;
6812
6813 tuple = heap_modify_tuple_by_cols(tuple, tupdesc, 1, &replCol, &replValue, &replIsnull);
6814 }
6815 }
6816 }
6817
6818 return tuple;
6819}
6820
6821/*
6822 * RegisterAfterTriggerBatchCallback
6823 * Register a function to be called when the current trigger-firing
6824 * batch completes.
6825 *
6826 * Must be called from within a trigger function's execution context
6827 * (i.e., while afterTriggers state is active).
6828 *
6829 * The callback list is cleared after invocation, so the caller must
6830 * re-register for each new batch if needed.
6831 */
6832void
6834 void *arg)
6835{
6838
6839 /*
6840 * Allocate in TopTransactionContext so the item survives for the duration
6841 * of the batch, which may span multiple trigger invocations.
6842 *
6843 * Must be called while afterTriggers is active; callbacks registered
6844 * outside a trigger-firing context would never fire.
6845 */
6849 item = palloc(sizeof(AfterTriggerCallbackItem));
6850 item->callback = callback;
6851 item->arg = arg;
6852 if (afterTriggers.query_depth >= 0)
6853 {
6856
6857 qs->batch_callbacks = lappend(qs->batch_callbacks, item);
6858 }
6859 else
6863}
6864
6865/*
6866 * FireAfterTriggerBatchCallbacks
6867 * Invoke all callbacks in the given list.
6868 *
6869 * Memory cleanup of the list and its items is handled by the caller
6870 * (AfterTriggerFreeQuery for query-level callbacks, AfterTriggerEndXact
6871 * for top-level deferred callbacks).
6872 */
6873static void
6875{
6876 ListCell *lc;
6877
6880 foreach(lc, callbacks)
6881 {
6883
6884 item->callback(item->arg);
6885 }
6887}
6888
6889/*
6890 * AfterTriggerIsActive
6891 * Returns true if we're inside the after-trigger framework where
6892 * registered batch callbacks will actually be invoked.
6893 *
6894 * This is false during validateForeignKeyConstraint(), which calls
6895 * RI trigger functions directly outside the after-trigger framework.
6896 */
6897bool
6899{
6900 return afterTriggers.firing_depth > 0;
6901}
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:142
Bitmapset * bms_copy(const Bitmapset *a)
Definition bitmapset.c:122
#define Min(x, y)
Definition c.h:1091
#define Max(x, y)
Definition c.h:1085
#define Assert(condition)
Definition c.h:943
#define pg_fallthrough
Definition c.h:161
uint32 CommandId
Definition c.h:750
#define OidIsValid(objectId)
Definition c.h:858
size_t Size
Definition c.h:689
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
Datum arg
Definition elog.c:1322
int errcode(int sqlerrcode)
Definition elog.c:874
#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:1372
void ExecCloseResultRelations(EState *estate)
Definition execMain.c:1604
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:75
#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:612
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:523
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:1312
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1232
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:403
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1266
MemoryContext TopTransactionContext
Definition mcxt.c:171
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurTransactionContext
Definition mcxt.c:172
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define SECURITY_LOCAL_USERID_CHANGE
Definition miscadmin.h:330
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:273
@ CMD_MERGE
Definition nodes.h:279
@ CMD_INSERT
Definition nodes.h:277
@ CMD_DELETE
Definition nodes.h:278
@ CMD_UPDATE
Definition nodes.h:276
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
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:370
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:3948
struct AfterTriggerEventChunk * next
Definition trigger.c:3765
ItemPointerData ate_ctid2
Definition trigger.c:3712
TriggerFlags ate_flags
Definition trigger.c:3710
ItemPointerData ate_ctid1
Definition trigger.c:3711
AfterTriggerEventChunk * head
Definition trigger.c:3777
AfterTriggerEventChunk * tail
Definition trigger.c:3778
SetConstraintState state
Definition trigger.c:3884
AfterTriggersQueryData * query_stack
Definition trigger.c:3889
MemoryContext event_cxt
Definition trigger.c:3886
List * batch_callbacks
Definition trigger.c:3897
bool firing_batch_callbacks
Definition trigger.c:3899
CommandId firing_counter
Definition trigger.c:3883
AfterTriggersTransData * trans_stack
Definition trigger.c:3894
AfterTriggerEventList events
Definition trigger.c:3885
Tuplestorestate * fdw_tuplestore
Definition trigger.c:3913
AfterTriggerEventList events
Definition trigger.c:3912
Tuplestorestate * new_tuplestore
Definition trigger.c:3940
TupleTableSlot * storeslot
Definition trigger.c:3942
Tuplestorestate * old_tuplestore
Definition trigger.c:3938
AfterTriggerEventList after_trig_events
Definition trigger.c:3935
AfterTriggerEventList events
Definition trigger.c:3922
SetConstraintState state
Definition trigger.c:3921
CommandId firing_counter
Definition trigger.c:3924
List * es_tupleTable
Definition execnodes.h:748
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:5771
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition trigger.c:6693
static AfterTriggersData afterTriggers
Definition trigger.c:3952
#define AFTER_TRIGGER_FDW_FETCH
Definition trigger.c:3688
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition trigger.c:5791
#define AFTER_TRIGGER_IN_PROGRESS
Definition trigger.c:3685
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition trigger.c:4980
static void FireAfterTriggerBatchCallbacks(List *callbacks)
Definition trigger.c:6874
void AfterTriggerBeginXact(void)
Definition trigger.c:5106
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition trigger.c:4288
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition trigger.c:4889
static Bitmapset * afterTriggerCopyBitmap(Bitmapset *src)
Definition trigger.c:4075
#define CHUNK_DATA_START(cptr)
Definition trigger.c:3772
void AfterTriggerEndSubXact(bool isCommit)
Definition trigger.c:5489
void RegisterAfterTriggerBatchCallback(AfterTriggerBatchCallback callback, void *arg)
Definition trigger.c:6833
#define AFTER_TRIGGER_TUP_BITS
Definition trigger.c:3692
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition trigger.c:3484
#define AFTER_TRIGGER_1CTID
Definition trigger.c:3689
bool AfterTriggerIsActive(void)
Definition trigger.c:6898
Datum pg_trigger_depth(PG_FUNCTION_ARGS)
Definition trigger.c:6782
#define MAX_CHUNK_SIZE
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition trigger.c:5261
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition trigger.c:4227
#define GetTriggerSharedData(evt)
Definition trigger.c:3753
static int MyTriggerDepth
Definition trigger.c:68
#define for_each_chunk_from(cptr)
Definition trigger.c:3794
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition trigger.c:4636
static Tuplestorestate * GetAfterTriggersTransitionTable(int event, TupleTableSlot *oldslot, TupleTableSlot *newslot, TransitionCaptureState *transition_capture)
Definition trigger.c:5589
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition trigger.c:4720
struct AfterTriggerSharedData * AfterTriggerShared
Definition trigger.c:3693
void AfterTriggerSetState(ConstraintsSetStmt *stmt)
Definition trigger.c:5821
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:4350
static bool before_stmt_triggers_fired(Oid relid, CmdType cmdType)
Definition trigger.c:6647
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition trigger.c:4100
#define AFTER_TRIGGER_2CTID
Definition trigger.c:3690
#define SizeofTriggerEvent(evt)
Definition trigger.c:3744
int SessionReplicationRole
Definition trigger.c:65
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, TriggerInstrumentation *instr, MemoryContext per_tuple_context)
Definition trigger.c:2311
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition trigger.c:4030
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition trigger.c:5746
void AfterTriggerFireDeferred(void)
Definition trigger.c:5324
static void TransitionTableAddTuple(EState *estate, int event, TransitionCaptureState *transition_capture, ResultRelInfo *relinfo, TupleTableSlot *slot, TupleTableSlot *original_insert_tuple, Tuplestorestate *tuplestore)
Definition trigger.c:5640
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition trigger.c:4248
#define for_each_event_from(eptr, cptr)
Definition trigger.c:3796
#define MIN_CHUNK_SIZE
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition trigger.c:3994
void assign_session_replication_role(int newval, void *extra)
Definition trigger.c:6768
void AfterTriggerEndXact(bool isCommit)
Definition trigger.c:5387
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6145
#define AFTER_TRIGGER_FDW_REUSE
Definition trigger.c:3687
void AfterTriggerBeginSubXact(void)
Definition trigger.c:5441
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:6232
static HeapTuple check_modified_virtual_generated(TupleDesc tupdesc, HeapTuple tuple)
Definition trigger.c:6798
static TupleTableSlot * GetAfterTriggersStoreSlot(AfterTriggersTableData *table, TupleDesc tupdesc)
Definition trigger.c:4931
#define AFTER_TRIGGER_CP_UPDATE
Definition trigger.c:3691
void AfterTriggerEndQuery(EState *estate)
Definition trigger.c:5161
#define AFTER_TRIGGER_DONE
Definition trigger.c:3684
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition trigger.c:3790
SetConstraintStateData * SetConstraintState
Definition trigger.c:3636
static void AfterTriggerEnlargeQueryState(void)
Definition trigger.c:5698
#define for_each_event(eptr, cptr)
Definition trigger.c:3785
struct AfterTriggerEventData * AfterTriggerEvent
Definition trigger.c:3706
#define for_each_chunk(cptr, evtlist)
Definition trigger.c:3783
void AfterTriggerBeginQuery(void)
Definition trigger.c:5141
#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:543
int GetCurrentTransactionNestLevel(void)
Definition xact.c:931
bool IsSubTransaction(void)
Definition xact.c:5095

◆ for_each_chunk_from

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

Definition at line 3794 of file trigger.c.

◆ for_each_event

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

Definition at line 3785 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 3790 of file trigger.c.

◆ for_each_event_from

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

Definition at line 3796 of file trigger.c.

◆ GetTriggerSharedData

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

Definition at line 3753 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 3706 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3693 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

◆ AfterTriggersTableData

◆ AfterTriggersTransData

◆ SetConstraintState

◆ SetConstraintStateData

◆ SetConstraintTrigger

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3681 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 4100 of file trigger.c.

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

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 5141 of file trigger.c.

5142{
5143 /* Increase the query stack depth */
5145}

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 5441 of file trigger.c.

5442{
5443 int my_level = GetCurrentTransactionNestLevel();
5444
5445 /*
5446 * Allocate more space in the trans_stack if needed. (Note: because the
5447 * minimum nest level of a subtransaction is 2, we waste the first couple
5448 * entries of the array; not worth the notational effort to avoid it.)
5449 */
5450 while (my_level >= afterTriggers.maxtransdepth)
5451 {
5453 {
5454 /* Arbitrarily initialize for max of 8 subtransaction levels */
5457 8 * sizeof(AfterTriggersTransData));
5459 }
5460 else
5461 {
5462 /* repalloc will keep the stack in the same context */
5464
5469 }
5470 }
5471
5472 /*
5473 * Push the current information into the stack. The SET CONSTRAINTS state
5474 * is not saved until/unless changed. Likewise, we don't make a
5475 * per-subtransaction event context until needed.
5476 */
5477 afterTriggers.trans_stack[my_level].state = NULL;
5481}

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 5106 of file trigger.c.

5107{
5108 /*
5109 * Initialize after-trigger state structure to empty
5110 */
5111 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5116
5117 /*
5118 * Verify that there is no leftover state remaining. If these assertions
5119 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5120 * up properly.
5121 */
5129}

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 4030 of file trigger.c.

4031{
4032 Oid tgoid = evtshared->ats_tgoid;
4034 int i;
4035
4036 /*
4037 * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
4038 * constraints declared NOT DEFERRABLE), the state is always false.
4039 */
4040 if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
4041 return false;
4042
4043 /*
4044 * If constraint state exists, SET CONSTRAINTS might have been executed
4045 * either for this trigger or for all triggers.
4046 */
4047 if (state != NULL)
4048 {
4049 /* Check for SET CONSTRAINTS for this specific trigger. */
4050 for (i = 0; i < state->numstates; i++)
4051 {
4052 if (state->trigstates[i].sct_tgoid == tgoid)
4053 return state->trigstates[i].sct_tgisdeferred;
4054 }
4055
4056 /* Check for SET CONSTRAINTS ALL. */
4057 if (state->all_isset)
4058 return state->all_isdeferred;
4059 }
4060
4061 /*
4062 * Otherwise return the default state for the trigger.
4063 */
4064 return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
4065}

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 4075 of file trigger.c.

4076{
4077 Bitmapset *dst;
4079
4080 if (src == NULL)
4081 return NULL;
4082
4084
4085 dst = bms_copy(src);
4086
4088
4089 return dst;
4090}

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

Referenced by afterTriggerAddEvent().

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 4288 of file trigger.c.

4289{
4290 AfterTriggerEventChunk *target = qs->events.head;
4291 ListCell *lc;
4292
4293 Assert(target && target->next);
4294
4295 /*
4296 * First, update any pointers in the per-table data, so that they won't be
4297 * dangling. Resetting obsoleted pointers to NULL will make
4298 * cancel_prior_stmt_triggers start from the list head, which is fine.
4299 */
4300 foreach(lc, qs->tables)
4301 {
4303
4304 if (table->after_trig_done &&
4305 table->after_trig_events.tail == target)
4306 {
4307 table->after_trig_events.head = NULL;
4308 table->after_trig_events.tail = NULL;
4309 table->after_trig_events.tailfree = NULL;
4310 }
4311 }
4312
4313 /* Now we can flush the head chunk */
4314 qs->events.head = target->next;
4315 pfree(target);
4316}

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

Referenced by AfterTriggerEndQuery().

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 5161 of file trigger.c.

5162{
5164
5165 /* Must be inside a query, too */
5167
5168 /*
5169 * If we never even got as far as initializing the event stack, there
5170 * certainly won't be any events, so exit quickly.
5171 */
5173 {
5175 return;
5176 }
5177
5178 /*
5179 * Process all immediate-mode triggers queued by the query, and move the
5180 * deferred ones to the main list of deferred events.
5181 *
5182 * Notice that we decide which ones will be fired, and put the deferred
5183 * ones on the main list, before anything is actually fired. This ensures
5184 * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5185 * IMMEDIATE: all events we have decided to defer will be available for it
5186 * to fire.
5187 *
5188 * We loop in case a trigger queues more events at the same query level.
5189 * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5190 * will instead fire any triggers in a dedicated query level. Foreign key
5191 * enforcement triggers do add to the current query level, thanks to their
5192 * passing fire_triggers = false to SPI_execute_snapshot(). Other
5193 * C-language triggers might do likewise.
5194 *
5195 * If we find no firable events, we don't have to increment
5196 * firing_counter.
5197 */
5199
5201 for (;;)
5202 {
5203 if (afterTriggerMarkEvents(&qs->events, &afterTriggers.events, true))
5204 {
5206 AfterTriggerEventChunk *oldtail = qs->events.tail;
5207
5208 if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5209 break; /* all fired */
5210
5211 /*
5212 * Firing a trigger could result in query_stack being repalloc'd,
5213 * so we must recalculate qs after each afterTriggerInvokeEvents
5214 * call. Furthermore, it's unsafe to pass delete_ok = true here,
5215 * because that could cause afterTriggerInvokeEvents to try to
5216 * access qs->events after the stack has been repalloc'd.
5217 */
5219
5220 /*
5221 * We'll need to scan the events list again. To reduce the cost
5222 * of doing so, get rid of completely-fired chunks. We know that
5223 * all events were marked IN_PROGRESS or DONE at the conclusion of
5224 * afterTriggerMarkEvents, so any still-interesting events must
5225 * have been added after that, and so must be in the chunk that
5226 * was then the tail chunk, or in later chunks. So, zap all
5227 * chunks before oldtail. This is approximately the same set of
5228 * events we would have gotten rid of by passing delete_ok = true.
5229 */
5230 Assert(oldtail != NULL);
5231 while (qs->events.head != oldtail)
5233 }
5234 else
5235 break;
5236 }
5237
5238 /*
5239 * Fire batch callbacks before releasing query-level storage and before
5240 * decrementing query_depth. Callbacks may do real work (index probes,
5241 * error reporting).
5242 */
5243 FireAfterTriggerBatchCallbacks(qs->batch_callbacks);
5244
5245 /* Release query-level-local storage, including tuplestores if any */
5247
5250}

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 5489 of file trigger.c.

5490{
5491 int my_level = GetCurrentTransactionNestLevel();
5493 AfterTriggerEvent event;
5496
5497 /*
5498 * Pop the prior state if needed.
5499 */
5500 if (isCommit)
5501 {
5503 /* If we saved a prior state, we don't need it anymore */
5505 if (state != NULL)
5506 pfree(state);
5507 /* this avoids double pfree if error later: */
5508 afterTriggers.trans_stack[my_level].state = NULL;
5511 }
5512 else
5513 {
5514 /*
5515 * Aborting. It is possible subxact start failed before calling
5516 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5517 * trans_stack levels that aren't there.
5518 */
5519 if (my_level >= afterTriggers.maxtransdepth)
5520 return;
5521
5522 /*
5523 * Release query-level storage for queries being aborted, and restore
5524 * query_depth to its pre-subxact value. This assumes that a
5525 * subtransaction will not add events to query levels started in a
5526 * earlier transaction state.
5527 */
5529 {
5533 }
5536
5537 /*
5538 * Restore the global deferred-event list to its former length,
5539 * discarding any events queued by the subxact.
5540 */
5542 &afterTriggers.trans_stack[my_level].events);
5543
5544 /*
5545 * Restore the trigger state. If the saved state is NULL, then this
5546 * subxact didn't save it, so it doesn't need restoring.
5547 */
5549 if (state != NULL)
5550 {
5553 }
5554 /* this avoids double pfree if error later: */
5555 afterTriggers.trans_stack[my_level].state = NULL;
5556
5557 /*
5558 * Scan for any remaining deferred events that were marked DONE or IN
5559 * PROGRESS by this subxact or a child, and un-mark them. We can
5560 * recognize such events because they have a firing ID greater than or
5561 * equal to the firing_counter value we saved at subtransaction start.
5562 * (This essentially assumes that the current subxact includes all
5563 * subxacts started after it.)
5564 */
5567 {
5569
5570 if (event->ate_flags &
5572 {
5573 if (evtshared->ats_firing_id >= subxact_firing_id)
5574 event->ate_flags &=
5576 }
5577 }
5578 }
5579
5580 /* Reset in case a callback threw an error while firing. */
5582}

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 5387 of file trigger.c.

5388{
5389 /*
5390 * Forget the pending-events list.
5391 *
5392 * Since all the info is in TopTransactionContext or children thereof, we
5393 * don't really need to do anything to reclaim memory. However, the
5394 * pending-events list could be large, and so it's useful to discard it as
5395 * soon as possible --- especially if we are aborting because we ran out
5396 * of memory for the list!
5397 */
5399 {
5405 }
5406
5407 /*
5408 * Forget any subtransaction state as well. Since this can't be very
5409 * large, we let the eventual reset of TopTransactionContext free the
5410 * memory instead of doing it here.
5411 */
5414
5415
5416 /*
5417 * Forget the query stack and constraint-related state information. As
5418 * with the subtransaction state information, we don't bother freeing the
5419 * memory here.
5420 */
5424
5425 /* No more afterTriggers manipulation until next transaction starts. */
5427
5429
5433}

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 5698 of file trigger.c.

5699{
5701
5703
5705 {
5706 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5707
5712 }
5713 else
5714 {
5715 /* repalloc will keep the stack in the same context */
5718 old_alloc * 2);
5719
5724 }
5725
5726 /* Initialize new array entries to empty */
5728 {
5730
5731 qs->events.head = NULL;
5732 qs->events.tail = NULL;
5733 qs->events.tailfree = NULL;
5734 qs->fdw_tuplestore = NULL;
5735 qs->tables = NIL;
5736 qs->batch_callbacks = NIL;
5737
5738 ++init_depth;
5739 }
5740}

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 4350 of file trigger.c.

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

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 5324 of file trigger.c.

5325{
5326 AfterTriggerEventList *events;
5327 bool snap_pushed = false;
5328
5329 /* Must not be inside a query */
5331
5332 /*
5333 * If there are any triggers to fire, make sure we have set a snapshot for
5334 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5335 * can't assume ActiveSnapshot is valid on entry.)
5336 */
5337 events = &afterTriggers.events;
5338 if (events->head != NULL)
5339 {
5341 snap_pushed = true;
5342 }
5343
5344 /*
5345 * Run all the remaining triggers. Loop until they are all gone, in case
5346 * some trigger queues more for us to do.
5347 */
5349 while (afterTriggerMarkEvents(events, NULL, false))
5350 {
5352
5353 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5354 break; /* all fired */
5355 }
5356
5357 /* Flush any fast-path batches accumulated by the triggers just fired. */
5359
5361
5362 /*
5363 * We don't bother freeing the event list or batch_callbacks, since they
5364 * will go away anyway (and more efficiently than via pfree) in
5365 * AfterTriggerEndXact.
5366 */
5367
5368 if (snap_pushed)
5370}

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 4227 of file trigger.c.

4228{
4230
4231 while ((chunk = events->head) != NULL)
4232 {
4233 events->head = chunk->next;
4234 pfree(chunk);
4235 }
4236 events->tail = NULL;
4237 events->tailfree = NULL;
4238}

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 5261 of file trigger.c.

5262{
5263 Tuplestorestate *ts;
5264 List *tables;
5265 ListCell *lc;
5266
5267 /* Drop the trigger events */
5268 afterTriggerFreeEventList(&qs->events);
5269
5270 /* Drop FDW tuplestore if any */
5271 ts = qs->fdw_tuplestore;
5272 qs->fdw_tuplestore = NULL;
5273 if (ts)
5274 tuplestore_end(ts);
5275
5276 /* Release per-table subsidiary storage */
5277 tables = qs->tables;
5278 foreach(lc, tables)
5279 {
5281
5282 ts = table->old_tuplestore;
5283 table->old_tuplestore = NULL;
5284 if (ts)
5285 tuplestore_end(ts);
5286 ts = table->new_tuplestore;
5287 table->new_tuplestore = NULL;
5288 if (ts)
5289 tuplestore_end(ts);
5290 if (table->storeslot)
5291 {
5292 TupleTableSlot *slot = table->storeslot;
5293
5294 table->storeslot = NULL;
5296 }
5297 }
5298
5299 /*
5300 * Now free the AfterTriggersTableData structs and list cells. Reset list
5301 * pointer first; if list_free_deep somehow gets an error, better to leak
5302 * that storage than have an infinite loop.
5303 */
5304 qs->tables = NIL;
5305 list_free_deep(tables);
5306
5307 list_free_deep(qs->batch_callbacks);
5308 qs->batch_callbacks = NIL;
5309}

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 4720 of file trigger.c.

4724{
4725 bool all_fired = true;
4728 bool local_estate = false;
4730 Relation rel = NULL;
4731 TriggerDesc *trigdesc = NULL;
4732 FmgrInfo *finfo = NULL;
4735 *slot2 = NULL;
4736
4737 /* Make a local EState if need be */
4738 if (estate == NULL)
4739 {
4740 estate = CreateExecutorState();
4741 local_estate = true;
4742 }
4743
4744 /* Make a per-tuple memory context for trigger function calls */
4747 "AfterTriggerTupleContext",
4749
4750 for_each_chunk(chunk, *events)
4751 {
4752 AfterTriggerEvent event;
4753 bool all_fired_in_chunk = true;
4754
4755 for_each_event(event, chunk)
4756 {
4758
4759 /*
4760 * Is it one for me to fire?
4761 */
4762 if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4763 evtshared->ats_firing_id == firing_id)
4764 {
4766 *dst_rInfo;
4767
4768 /*
4769 * So let's fire it... but first, find the correct relation if
4770 * this is not the same relation as before.
4771 */
4772 if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4773 {
4774 rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4775 NULL);
4776 rel = rInfo->ri_RelationDesc;
4777 /* Catch calls with insufficient relcache refcounting */
4779 trigdesc = rInfo->ri_TrigDesc;
4780 /* caution: trigdesc could be NULL here */
4781 finfo = rInfo->ri_TrigFunctions;
4782 instr = rInfo->ri_TrigInstrument;
4783 if (slot1 != NULL)
4784 {
4787 slot1 = slot2 = NULL;
4788 }
4789 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4790 {
4795 }
4796 }
4797
4798 /*
4799 * Look up source and destination partition result rels of a
4800 * cross-partition update event.
4801 */
4802 if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4804 {
4805 Assert(OidIsValid(event->ate_src_part) &&
4806 OidIsValid(event->ate_dst_part));
4808 event->ate_src_part,
4809 rInfo);
4811 event->ate_dst_part,
4812 rInfo);
4813 }
4814 else
4816
4817 /*
4818 * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4819 * still set, so recursive examinations of the event list
4820 * won't try to re-fire it.
4821 */
4822 AfterTriggerExecute(estate, event, rInfo,
4824 trigdesc, finfo, instr,
4826
4827 /*
4828 * Mark the event as done.
4829 */
4830 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4831 event->ate_flags |= AFTER_TRIGGER_DONE;
4832 }
4833 else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4834 {
4835 /* something remains to be done */
4836 all_fired = all_fired_in_chunk = false;
4837 }
4838 }
4839
4840 /* Clear the chunk if delete_ok and nothing left of interest */
4842 {
4843 chunk->freeptr = CHUNK_DATA_START(chunk);
4844 chunk->endfree = chunk->endptr;
4845
4846 /*
4847 * If it's last chunk, must sync event list's tailfree too. Note
4848 * that delete_ok must NOT be passed as true if there could be
4849 * additional AfterTriggerEventList values pointing at this event
4850 * list, since we'd fail to fix their copies of tailfree.
4851 */
4852 if (chunk == events->tail)
4853 events->tailfree = chunk->freeptr;
4854 }
4855 }
4856 if (slot1 != NULL)
4857 {
4860 }
4861
4862 /* Release working resources */
4864
4865 if (local_estate)
4866 {
4868 ExecResetTupleTable(estate->es_tupleTable, false);
4869 FreeExecutorState(estate);
4870 }
4871
4872 return all_fired;
4873}

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 6898 of file trigger.c.

6899{
6900 return afterTriggers.firing_depth > 0;
6901}

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 4636 of file trigger.c.

4639{
4640 bool found = false;
4641 bool deferred_found = false;
4642 AfterTriggerEvent event;
4644
4645 for_each_event_chunk(event, chunk, *events)
4646 {
4648 bool defer_it = false;
4649
4650 if (!(event->ate_flags &
4652 {
4653 /*
4654 * This trigger hasn't been called or scheduled yet. Check if we
4655 * should call it now.
4656 */
4658 {
4659 defer_it = true;
4660 }
4661 else
4662 {
4663 /*
4664 * Mark it as to be fired in this firing cycle.
4665 */
4666 evtshared->ats_firing_id = afterTriggers.firing_counter;
4667 event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4668 found = true;
4669 }
4670 }
4671
4672 /*
4673 * If it's deferred, move it to move_list, if requested.
4674 */
4675 if (defer_it && move_list != NULL)
4676 {
4677 deferred_found = true;
4678 /* add it to move_list */
4680 /* mark original copy "done" so we don't do it again */
4681 event->ate_flags |= AFTER_TRIGGER_DONE;
4682 }
4683 }
4684
4685 /*
4686 * We could allow deferred triggers if, before the end of the
4687 * security-restricted operation, we were to verify that a SET CONSTRAINTS
4688 * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4689 */
4691 ereport(ERROR,
4693 errmsg("cannot fire deferred trigger within security-restricted operation")));
4694
4695 return found;
4696}

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 6145 of file trigger.c.

6146{
6147 AfterTriggerEvent event;
6149 int depth;
6150
6151 /* Scan queued events */
6153 {
6155
6156 /*
6157 * We can ignore completed events. (Even if a DONE flag is rolled
6158 * back by subxact abort, it's OK because the effects of the TRUNCATE
6159 * or whatever must get rolled back too.)
6160 */
6161 if (event->ate_flags & AFTER_TRIGGER_DONE)
6162 continue;
6163
6164 if (evtshared->ats_relid == relid)
6165 return true;
6166 }
6167
6168 /*
6169 * Also scan events queued by incomplete queries. This could only matter
6170 * if TRUNCATE/etc is executed by a function or trigger within an updating
6171 * query on the same relation, which is pretty perverse, but let's check.
6172 */
6173 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6174 {
6176 {
6178
6179 if (event->ate_flags & AFTER_TRIGGER_DONE)
6180 continue;
6181
6182 if (evtshared->ats_relid == relid)
6183 return true;
6184 }
6185 }
6186
6187 return false;
6188}

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 4248 of file trigger.c.

4250{
4253
4254 if (old_events->tail == NULL)
4255 {
4256 /* restoring to a completely empty state, so free everything */
4258 }
4259 else
4260 {
4261 *events = *old_events;
4262 /* free any chunks after the last one we want to keep */
4263 for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4264 {
4265 next_chunk = chunk->next;
4266 pfree(chunk);
4267 }
4268 /* and clean up the tail chunk to be the right length */
4269 events->tail->next = NULL;
4270 events->tail->freeptr = events->tailfree;
4271
4272 /*
4273 * We don't make any effort to remove now-unused shared data records.
4274 * They might still be useful, anyway.
4275 */
4276 }
4277}

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 6232 of file trigger.c.

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

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 5821 of file trigger.c.

5822{
5823 int my_level = GetCurrentTransactionNestLevel();
5824
5825 /* If we haven't already done so, initialize our state. */
5826 if (afterTriggers.state == NULL)
5828
5829 /*
5830 * If in a subtransaction, and we didn't save the current state already,
5831 * save it so it can be restored if the subtransaction aborts.
5832 */
5833 if (my_level > 1 &&
5834 afterTriggers.trans_stack[my_level].state == NULL)
5835 {
5836 afterTriggers.trans_stack[my_level].state =
5838 }
5839
5840 /*
5841 * Handle SET CONSTRAINTS ALL ...
5842 */
5843 if (stmt->constraints == NIL)
5844 {
5845 /*
5846 * Forget any previous SET CONSTRAINTS commands in this transaction.
5847 */
5849
5850 /*
5851 * Set the per-transaction ALL state to known.
5852 */
5855 }
5856 else
5857 {
5860 List *conoidlist = NIL;
5861 List *tgoidlist = NIL;
5862 ListCell *lc;
5863
5864 /*
5865 * Handle SET CONSTRAINTS constraint-name [, ...]
5866 *
5867 * First, identify all the named constraints and make a list of their
5868 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5869 * the same name within a schema, the specifications are not
5870 * necessarily unique. Our strategy is to target all matching
5871 * constraints within the first search-path schema that has any
5872 * matches, but disregard matches in schemas beyond the first match.
5873 * (This is a bit odd but it's the historical behavior.)
5874 *
5875 * A constraint in a partitioned table may have corresponding
5876 * constraints in the partitions. Grab those too.
5877 */
5879
5880 foreach(lc, stmt->constraints)
5881 {
5882 RangeVar *constraint = lfirst(lc);
5883 bool found;
5885 ListCell *nslc;
5886
5887 if (constraint->catalogname)
5888 {
5889 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5890 ereport(ERROR,
5892 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5893 constraint->catalogname, constraint->schemaname,
5894 constraint->relname)));
5895 }
5896
5897 /*
5898 * If we're given the schema name with the constraint, look only
5899 * in that schema. If given a bare constraint name, use the
5900 * search path to find the first matching constraint.
5901 */
5902 if (constraint->schemaname)
5903 {
5905 false);
5906
5908 }
5909 else
5910 {
5912 }
5913
5914 found = false;
5915 foreach(nslc, namespacelist)
5916 {
5919 ScanKeyData skey[2];
5920 HeapTuple tup;
5921
5922 ScanKeyInit(&skey[0],
5925 CStringGetDatum(constraint->relname));
5926 ScanKeyInit(&skey[1],
5930
5932 true, NULL, 2, skey);
5933
5935 {
5937
5938 if (con->condeferrable)
5939 conoidlist = lappend_oid(conoidlist, con->oid);
5940 else if (stmt->deferred)
5941 ereport(ERROR,
5943 errmsg("constraint \"%s\" is not deferrable",
5944 constraint->relname)));
5945 found = true;
5946 }
5947
5949
5950 /*
5951 * Once we've found a matching constraint we do not search
5952 * later parts of the search path.
5953 */
5954 if (found)
5955 break;
5956 }
5957
5959
5960 /*
5961 * Not found ?
5962 */
5963 if (!found)
5964 ereport(ERROR,
5966 errmsg("constraint \"%s\" does not exist",
5967 constraint->relname)));
5968 }
5969
5970 /*
5971 * Scan for any possible descendants of the constraints. We append
5972 * whatever we find to the same list that we're scanning; this has the
5973 * effect that we create new scans for those, too, so if there are
5974 * further descendents, we'll also catch them.
5975 */
5976 foreach(lc, conoidlist)
5977 {
5978 Oid parent = lfirst_oid(lc);
5980 SysScanDesc scan;
5981 HeapTuple tuple;
5982
5983 ScanKeyInit(&key,
5986 ObjectIdGetDatum(parent));
5987
5988 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5989
5990 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5991 {
5993
5994 conoidlist = lappend_oid(conoidlist, con->oid);
5995 }
5996
5997 systable_endscan(scan);
5998 }
5999
6001
6002 /*
6003 * Now, locate the trigger(s) implementing each of these constraints,
6004 * and make a list of their OIDs.
6005 */
6007
6008 foreach(lc, conoidlist)
6009 {
6010 Oid conoid = lfirst_oid(lc);
6013 HeapTuple htup;
6014
6018 ObjectIdGetDatum(conoid));
6019
6021 NULL, 1, &skey);
6022
6024 {
6026
6027 /*
6028 * Silently skip triggers that are marked as non-deferrable in
6029 * pg_trigger. This is not an error condition, since a
6030 * deferrable RI constraint may have some non-deferrable
6031 * actions.
6032 */
6033 if (pg_trigger->tgdeferrable)
6035 }
6036
6038 }
6039
6041
6042 /*
6043 * Now we can set the trigger states of individual triggers for this
6044 * xact.
6045 */
6046 foreach(lc, tgoidlist)
6047 {
6048 Oid tgoid = lfirst_oid(lc);
6050 bool found = false;
6051 int i;
6052
6053 for (i = 0; i < state->numstates; i++)
6054 {
6055 if (state->trigstates[i].sct_tgoid == tgoid)
6056 {
6057 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
6058 found = true;
6059 break;
6060 }
6061 }
6062 if (!found)
6063 {
6065 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
6066 }
6067 }
6068 }
6069
6070 /*
6071 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
6072 * checks against that constraint must be made when the SET CONSTRAINTS
6073 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
6074 * apply retroactively. We've updated the constraints state, so scan the
6075 * list of previously deferred events to fire any that have now become
6076 * immediate.
6077 *
6078 * Obviously, if this was SET ... DEFERRED then it can't have converted
6079 * any unfired events to immediate, so we need do nothing in that case.
6080 */
6081 if (!stmt->deferred)
6082 {
6084 bool snapshot_set = false;
6085
6087 while (afterTriggerMarkEvents(events, NULL, true))
6088 {
6090
6091 /*
6092 * Make sure a snapshot has been established in case trigger
6093 * functions need one. Note that we avoid setting a snapshot if
6094 * we don't find at least one trigger that has to be fired now.
6095 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6096 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6097 * at the start of a transaction it's not possible for any trigger
6098 * events to be queued yet.)
6099 */
6100 if (!snapshot_set)
6101 {
6103 snapshot_set = true;
6104 }
6105
6106 /*
6107 * We can delete fired events if we are at top transaction level,
6108 * but we'd better not if inside a subtransaction, since the
6109 * subtransaction could later get rolled back.
6110 */
6112 !IsSubTransaction()))
6113 break; /* all fired */
6114 }
6115
6116 /*
6117 * Flush any fast-path batches accumulated by the triggers just fired.
6118 */
6123
6124 if (snapshot_set)
6126 }
6127}

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 6768 of file trigger.c.

6769{
6770 /*
6771 * Must flush the plan cache when changing replication role; but don't
6772 * flush unnecessarily.
6773 */
6776}

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6647 of file trigger.c.

6648{
6649 bool result;
6651
6652 /* Check state, like AfterTriggerSaveEvent. */
6653 if (afterTriggers.query_depth < 0)
6654 elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6655
6656 /* Be sure we have enough space to record events at this query depth. */
6659
6660 /*
6661 * We keep this state in the AfterTriggersTableData that also holds
6662 * transition tables for the relation + operation. In this way, if we are
6663 * forced to make a new set of transition tables because more tuples get
6664 * entered after we've already fired triggers, we will allow a new set of
6665 * statement triggers to get queued.
6666 */
6667 table = GetAfterTriggersTableData(relid, cmdType);
6668 result = table->before_trig_done;
6669 table->before_trig_done = true;
6670 return result;
6671}

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 6693 of file trigger.c.

6694{
6697
6698 /*
6699 * We keep this state in the AfterTriggersTableData that also holds
6700 * transition tables for the relation + operation. In this way, if we are
6701 * forced to make a new set of transition tables because more tuples get
6702 * entered after we've already fired triggers, we will allow a new set of
6703 * statement triggers to get queued without canceling the old ones.
6704 */
6705 table = GetAfterTriggersTableData(relid, cmdType);
6706
6707 if (table->after_trig_done)
6708 {
6709 /*
6710 * We want to start scanning from the tail location that existed just
6711 * before we inserted any statement triggers. But the events list
6712 * might've been entirely empty then, in which case scan from the
6713 * current head.
6714 */
6715 AfterTriggerEvent event;
6717
6718 if (table->after_trig_events.tail)
6719 {
6720 chunk = table->after_trig_events.tail;
6721 event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6722 }
6723 else
6724 {
6725 chunk = qs->events.head;
6726 event = NULL;
6727 }
6728
6729 for_each_chunk_from(chunk)
6730 {
6731 if (event == NULL)
6732 event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6733 for_each_event_from(event, chunk)
6734 {
6736
6737 /*
6738 * Exit loop when we reach events that aren't AS triggers for
6739 * the target relation.
6740 */
6741 if (evtshared->ats_relid != relid)
6742 goto done;
6743 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6744 goto done;
6745 if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6746 goto done;
6747 if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6748 goto done;
6749 /* OK, mark it DONE */
6750 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6751 event->ate_flags |= AFTER_TRIGGER_DONE;
6752 }
6753 /* signal we must reinitialize event ptr for next chunk */
6754 event = NULL;
6755 }
6756 }
6757done:
6758
6759 /* In any case, save current insertion point for next time */
6760 table->after_trig_done = true;
6761 table->after_trig_events = qs->events;
6762}

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 6798 of file trigger.c.

6799{
6800 if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
6801 return tuple;
6802
6803 for (int i = 0; i < tupdesc->natts; i++)
6804 {
6805 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6806 {
6807 if (!heap_attisnull(tuple, i + 1, tupdesc))
6808 {
6809 int replCol = i + 1;
6810 Datum replValue = 0;
6811 bool replIsnull = true;
6812
6813 tuple = heap_modify_tuple_by_cols(tuple, tupdesc, 1, &replCol, &replValue, &replIsnull);
6814 }
6815 }
6816 }
6817
6818 return tuple;
6819}

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 2092 of file trigger.c.

2093{
2096 int i;
2097
2098 if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2099 return NULL;
2100
2102 memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2103
2104 trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2105 memcpy(trigger, trigdesc->triggers,
2106 trigdesc->numtriggers * sizeof(Trigger));
2107 newdesc->triggers = trigger;
2108
2109 for (i = 0; i < trigdesc->numtriggers; i++)
2110 {
2111 trigger->tgname = pstrdup(trigger->tgname);
2112 if (trigger->tgnattr > 0)
2113 {
2114 int16 *newattr;
2115
2116 newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2117 memcpy(newattr, trigger->tgattr,
2118 trigger->tgnattr * sizeof(int16));
2119 trigger->tgattr = newattr;
2120 }
2121 if (trigger->tgnargs > 0)
2122 {
2123 char **newargs;
2124 int16 j;
2125
2126 newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2127 for (j = 0; j < trigger->tgnargs; j++)
2128 newargs[j] = pstrdup(trigger->tgargs[j]);
2129 trigger->tgargs = newargs;
2130 }
2131 if (trigger->tgqual)
2132 trigger->tgqual = pstrdup(trigger->tgqual);
2133 if (trigger->tgoldtable)
2134 trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2135 if (trigger->tgnewtable)
2136 trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2137 trigger++;
2138 }
2139
2140 return newdesc;
2141}
int16_t int16
Definition c.h:619
#define palloc_object(type)
Definition fe_memutils.h:74
int j
Definition isn.c:78
char * pstrdup(const char *in)
Definition mcxt.c:1781

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
320 errmsg("permission denied: \"%s\" is a system catalog",
322
323 if (stmt->isconstraint)
324 {
325 /*
326 * We must take a lock on the target relation to protect against
327 * concurrent drop. It's not clear that AccessShareLock is strong
328 * enough, but we certainly need at least that much... otherwise, we
329 * might end up creating a pg_constraint entry referencing a
330 * nonexistent table.
331 */
333 {
336 }
337 else if (stmt->constrrel != NULL)
339 false);
340 }
341
342 /* permission checks */
343 if (!isInternal)
344 {
347 if (aclresult != ACLCHECK_OK)
350
352 {
355 if (aclresult != ACLCHECK_OK)
358 }
359 }
360
361 /*
362 * When called on a partitioned table to create a FOR EACH ROW trigger
363 * that's not internal, we create one trigger for each partition, too.
364 *
365 * For that, we'd better hold lock on all of them ahead of time.
366 */
367 partition_recurse = !isInternal && stmt->row &&
368 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
372
373 /* Compute tgtype */
374 TRIGGER_CLEAR_TYPE(tgtype);
375 if (stmt->row)
376 TRIGGER_SETT_ROW(tgtype);
377 tgtype |= stmt->timing;
378 tgtype |= stmt->events;
379
380 /* Disallow ROW-level TRUNCATE triggers */
381 if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
384 errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
385
386 /* INSTEAD triggers must be row-level, and can't have WHEN or columns */
387 if (TRIGGER_FOR_INSTEAD(tgtype))
388 {
389 if (!TRIGGER_FOR_ROW(tgtype))
392 errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
393 if (stmt->whenClause)
396 errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
397 if (stmt->columns != NIL)
400 errmsg("INSTEAD OF triggers cannot have column lists")));
401 }
402
403 /*
404 * We don't yet support naming ROW transition variables, but the parser
405 * recognizes the syntax so we can give a nicer message here.
406 *
407 * Per standard, REFERENCING TABLE names are only allowed on AFTER
408 * triggers. Per standard, REFERENCING ROW names are not allowed with FOR
409 * EACH STATEMENT. Per standard, each OLD/NEW, ROW/TABLE permutation is
410 * only allowed once. Per standard, OLD may not be specified when
411 * creating a trigger only for INSERT, and NEW may not be specified when
412 * creating a trigger only for DELETE.
413 *
414 * Notice that the standard allows an AFTER ... FOR EACH ROW trigger to
415 * reference both ROW and TABLE transition data.
416 */
417 if (stmt->transitionRels != NIL)
418 {
419 List *varList = stmt->transitionRels;
420 ListCell *lc;
421
422 foreach(lc, varList)
423 {
425
426 if (!(tt->isTable))
429 errmsg("ROW variable naming in the REFERENCING clause is not supported"),
430 errhint("Use OLD TABLE or NEW TABLE for naming transition tables.")));
431
432 /*
433 * Because of the above test, we omit further ROW-related testing
434 * below. If we later allow naming OLD and NEW ROW variables,
435 * adjustments will be needed below.
436 */
437
438 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
441 errmsg("\"%s\" is a foreign table",
443 errdetail("Triggers on foreign tables cannot have transition tables.")));
444
445 if (rel->rd_rel->relkind == RELKIND_VIEW)
448 errmsg("\"%s\" is a view",
450 errdetail("Triggers on views cannot have transition tables.")));
451
452 /*
453 * We currently don't allow row-level triggers with transition
454 * tables on partition or inheritance children. Such triggers
455 * would somehow need to see tuples converted to the format of the
456 * table they're attached to, and it's not clear which subset of
457 * tuples each child should see. See also the prohibitions in
458 * ATExecAttachPartition() and ATExecAddInherit().
459 */
460 if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id))
461 {
462 /* Use appropriate error message. */
463 if (rel->rd_rel->relispartition)
466 errmsg("ROW triggers with transition tables are not supported on partitions")));
467 else
470 errmsg("ROW triggers with transition tables are not supported on inheritance children")));
471 }
472
473 if (stmt->timing != TRIGGER_TYPE_AFTER)
476 errmsg("transition table name can only be specified for an AFTER trigger")));
477
478 if (TRIGGER_FOR_TRUNCATE(tgtype))
481 errmsg("TRUNCATE triggers with transition tables are not supported")));
482
483 /*
484 * We currently don't allow multi-event triggers ("INSERT OR
485 * UPDATE") with transition tables, because it's not clear how to
486 * handle INSERT ... ON CONFLICT statements which can fire both
487 * INSERT and UPDATE triggers. We show the inserted tuples to
488 * INSERT triggers and the updated tuples to UPDATE triggers, but
489 * it's not yet clear what INSERT OR UPDATE trigger should see.
490 * This restriction could be lifted if we can decide on the right
491 * semantics in a later release.
492 */
493 if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) +
494 (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) +
495 (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1)
498 errmsg("transition tables cannot be specified for triggers with more than one event")));
499
500 /*
501 * We currently don't allow column-specific triggers with
502 * transition tables. Per spec, that seems to require
503 * accumulating separate transition tables for each combination of
504 * columns, which is a lot of work for a rather marginal feature.
505 */
506 if (stmt->columns != NIL)
509 errmsg("transition tables cannot be specified for triggers with column lists")));
510
511 /*
512 * We disallow constraint triggers with transition tables, to
513 * protect the assumption that such triggers can't be deferred.
514 * See notes with AfterTriggers data structures, below.
515 *
516 * Currently this is enforced by the grammar, so just Assert here.
517 */
518 Assert(!stmt->isconstraint);
519
520 if (tt->isNew)
521 {
522 if (!(TRIGGER_FOR_INSERT(tgtype) ||
523 TRIGGER_FOR_UPDATE(tgtype)))
526 errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger")));
527
528 if (newtablename != NULL)
531 errmsg("NEW TABLE cannot be specified multiple times")));
532
533 newtablename = tt->name;
534 }
535 else
536 {
537 if (!(TRIGGER_FOR_DELETE(tgtype) ||
538 TRIGGER_FOR_UPDATE(tgtype)))
541 errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger")));
542
543 if (oldtablename != NULL)
546 errmsg("OLD TABLE cannot be specified multiple times")));
547
548 oldtablename = tt->name;
549 }
550 }
551
552 if (newtablename != NULL && oldtablename != NULL &&
556 errmsg("OLD TABLE name and NEW TABLE name cannot be the same")));
557 }
558
559 /*
560 * Parse the WHEN clause, if any and we weren't passed an already
561 * transformed one.
562 *
563 * Note that as a side effect, we fill whenRtable when parsing. If we got
564 * an already parsed clause, this does not occur, which is what we want --
565 * no point in adding redundant dependencies below.
566 */
567 if (!whenClause && stmt->whenClause)
568 {
569 ParseState *pstate;
571 List *varList;
572 ListCell *lc;
573
574 /* Set up a pstate to parse with */
575 pstate = make_parsestate(NULL);
576 pstate->p_sourcetext = queryString;
577
578 /*
579 * Set up nsitems for OLD and NEW references.
580 *
581 * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
582 */
585 makeAlias("old", NIL),
586 false, false);
587 addNSItemToQuery(pstate, nsitem, false, true, true);
590 makeAlias("new", NIL),
591 false, false);
592 addNSItemToQuery(pstate, nsitem, false, true, true);
593
594 /* Transform expression. Copy to be sure we don't modify original */
595 whenClause = transformWhereClause(pstate,
596 copyObject(stmt->whenClause),
598 "WHEN");
599 /* we have to fix its collations too */
600 assign_expr_collations(pstate, whenClause);
601
602 /*
603 * Check for disallowed references to OLD/NEW.
604 *
605 * NB: pull_var_clause is okay here only because we don't allow
606 * subselects in WHEN clauses; it would fail to examine the contents
607 * of subselects.
608 */
609 varList = pull_var_clause(whenClause, 0);
610 foreach(lc, varList)
611 {
612 Var *var = (Var *) lfirst(lc);
613
614 switch (var->varno)
615 {
616 case PRS2_OLD_VARNO:
617 if (!TRIGGER_FOR_ROW(tgtype))
620 errmsg("statement trigger's WHEN condition cannot reference column values"),
621 parser_errposition(pstate, var->location)));
622 if (TRIGGER_FOR_INSERT(tgtype))
625 errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
626 parser_errposition(pstate, var->location)));
627 /* system columns are okay here */
628 break;
629 case PRS2_NEW_VARNO:
630 if (!TRIGGER_FOR_ROW(tgtype))
633 errmsg("statement trigger's WHEN condition cannot reference column values"),
634 parser_errposition(pstate, var->location)));
635 if (TRIGGER_FOR_DELETE(tgtype))
638 errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
639 parser_errposition(pstate, var->location)));
640 if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
643 errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
644 parser_errposition(pstate, var->location)));
645 if (TRIGGER_FOR_BEFORE(tgtype) &&
646 var->varattno == 0 &&
647 RelationGetDescr(rel)->constr &&
648 (RelationGetDescr(rel)->constr->has_generated_stored ||
649 RelationGetDescr(rel)->constr->has_generated_virtual))
652 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
653 errdetail("A whole-row reference is used and the table contains generated columns."),
654 parser_errposition(pstate, var->location)));
655 if (TRIGGER_FOR_BEFORE(tgtype) &&
656 var->varattno > 0 &&
657 TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attgenerated)
660 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
661 errdetail("Column \"%s\" is a generated column.",
662 NameStr(TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attname)),
663 parser_errposition(pstate, var->location)));
664 break;
665 default:
666 /* can't happen without add_missing_from, so just elog */
667 elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
668 break;
669 }
670 }
671
672 /* we'll need the rtable for recordDependencyOnExpr */
673 whenRtable = pstate->p_rtable;
674
675 qual = nodeToString(whenClause);
676
677 free_parsestate(pstate);
678 }
679 else if (!whenClause)
680 {
681 whenClause = NULL;
682 whenRtable = NIL;
683 qual = NULL;
684 }
685 else
686 {
687 qual = nodeToString(whenClause);
688 whenRtable = NIL;
689 }
690
691 /*
692 * Find and validate the trigger function.
693 */
694 if (!OidIsValid(funcoid))
695 funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
696 if (!isInternal)
697 {
699 if (aclresult != ACLCHECK_OK)
701 NameListToString(stmt->funcname));
702 }
704 if (funcrettype != TRIGGEROID)
707 errmsg("function %s must return type %s",
708 NameListToString(stmt->funcname), "trigger")));
709
710 /*
711 * Scan pg_trigger to see if there is already a trigger of the same name.
712 * Skip this for internally generated triggers, since we'll modify the
713 * name to be unique below.
714 *
715 * NOTE that this is cool only because we have ShareRowExclusiveLock on
716 * the relation, so the trigger set won't be changing underneath us.
717 */
719 if (!isInternal)
720 {
723
724 ScanKeyInit(&skeys[0],
728
729 ScanKeyInit(&skeys[1],
732 CStringGetDatum(stmt->trigname));
733
735 NULL, 2, skeys);
736
737 /* There should be at most one matching tuple */
739 {
741
742 trigoid = oldtrigger->oid;
743 existing_constraint_oid = oldtrigger->tgconstraint;
744 existing_isInternal = oldtrigger->tgisinternal;
746 trigger_exists = true;
747 /* copy the tuple to use in CatalogTupleUpdate() */
748 tuple = heap_copytuple(tuple);
749 }
751 }
752
753 if (!trigger_exists)
754 {
755 /* Generate the OID for the new trigger. */
758 }
759 else
760 {
761 /*
762 * If OR REPLACE was specified, we'll replace the old trigger;
763 * otherwise complain about the duplicate name.
764 */
765 if (!stmt->replace)
768 errmsg("trigger \"%s\" for relation \"%s\" already exists",
769 stmt->trigname, RelationGetRelationName(rel))));
770
771 /*
772 * An internal trigger or a child trigger (isClone) cannot be replaced
773 * by a user-defined trigger. However, skip this test when
774 * in_partition, because then we're recursing from a partitioned table
775 * and the check was made at the parent level.
776 */
781 errmsg("trigger \"%s\" for relation \"%s\" is an internal or a child trigger",
782 stmt->trigname, RelationGetRelationName(rel))));
783
784 /*
785 * It is not allowed to replace with a constraint trigger; gram.y
786 * should have enforced this already.
787 */
788 Assert(!stmt->isconstraint);
789
790 /*
791 * It is not allowed to replace an existing constraint trigger,
792 * either. (The reason for these restrictions is partly that it seems
793 * difficult to deal with pending trigger events in such cases, and
794 * partly that the command might imply changing the constraint's
795 * properties as well, which doesn't seem nice.)
796 */
800 errmsg("trigger \"%s\" for relation \"%s\" is a constraint trigger",
801 stmt->trigname, RelationGetRelationName(rel))));
802 }
803
804 /*
805 * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
806 * corresponding pg_constraint entry.
807 */
808 if (stmt->isconstraint && !OidIsValid(constraintOid))
809 {
810 /* Internal callers should have made their own constraints */
815 stmt->deferrable,
816 stmt->initdeferred,
817 true, /* Is Enforced */
818 true,
819 InvalidOid, /* no parent */
820 RelationGetRelid(rel),
821 NULL, /* no conkey */
822 0,
823 0,
824 InvalidOid, /* no domain */
825 InvalidOid, /* no index */
826 InvalidOid, /* no foreign key */
827 NULL,
828 NULL,
829 NULL,
830 NULL,
831 0,
832 ' ',
833 ' ',
834 NULL,
835 0,
836 ' ',
837 NULL, /* no exclusion */
838 NULL, /* no check constraint */
839 NULL,
840 true, /* islocal */
841 0, /* inhcount */
842 true, /* noinherit */
843 false, /* conperiod */
844 isInternal); /* is_internal */
845 }
846
847 /*
848 * If trigger is internally generated, modify the provided trigger name to
849 * ensure uniqueness by appending the trigger OID. (Callers will usually
850 * supply a simple constant trigger name in these cases.)
851 */
852 if (isInternal)
853 {
855 "%s_%u", stmt->trigname, trigoid);
856 trigname = internaltrigname;
857 }
858 else
859 {
860 /* user-defined trigger; use the specified trigger name as-is */
861 trigname = stmt->trigname;
862 }
863
864 /*
865 * Build the new pg_trigger tuple.
866 */
867 memset(nulls, false, sizeof(nulls));
868
873 CStringGetDatum(trigname));
883
884 if (stmt->args)
885 {
886 ListCell *le;
887 char *args;
888 int16 nargs = list_length(stmt->args);
889 int len = 0;
890
891 foreach(le, stmt->args)
892 {
893 char *ar = strVal(lfirst(le));
894
895 len += strlen(ar) + 4;
896 for (; *ar; ar++)
897 {
898 if (*ar == '\\')
899 len++;
900 }
901 }
902 args = (char *) palloc(len + 1);
903 args[0] = '\0';
904 foreach(le, stmt->args)
905 {
906 char *s = strVal(lfirst(le));
907 char *d = args + strlen(args);
908
909 while (*s)
910 {
911 if (*s == '\\')
912 *d++ = '\\';
913 *d++ = *s++;
914 }
915 strcpy(d, "\\000");
916 }
919 CStringGetDatum(args));
920 }
921 else
922 {
925 CStringGetDatum(""));
926 }
927
928 /* build column number array if it's a column-specific trigger */
929 ncolumns = list_length(stmt->columns);
930 if (ncolumns == 0)
931 columns = NULL;
932 else
933 {
934 ListCell *cell;
935 int i = 0;
936
937 columns = (int16 *) palloc(ncolumns * sizeof(int16));
938 foreach(cell, stmt->columns)
939 {
940 char *name = strVal(lfirst(cell));
942 int j;
943
944 /* Lookup column name. System columns are not allowed */
945 attnum = attnameAttNum(rel, name, false);
949 errmsg("column \"%s\" of relation \"%s\" does not exist",
951
952 /* Check for duplicates */
953 for (j = i - 1; j >= 0; j--)
954 {
955 if (columns[j] == attnum)
958 errmsg("column \"%s\" specified more than once",
959 name)));
960 }
961
962 columns[i++] = attnum;
963 }
964 }
965 tgattr = buildint2vector(columns, ncolumns);
967
968 /* set tgqual if trigger has WHEN clause */
969 if (qual)
971 else
972 nulls[Anum_pg_trigger_tgqual - 1] = true;
973
974 if (oldtablename)
977 else
978 nulls[Anum_pg_trigger_tgoldtable - 1] = true;
979 if (newtablename)
982 else
983 nulls[Anum_pg_trigger_tgnewtable - 1] = true;
984
985 /*
986 * Insert or replace tuple in pg_trigger.
987 */
988 if (!trigger_exists)
989 {
990 tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
992 }
993 else
994 {
996
997 newtup = heap_form_tuple(tgrel->rd_att, values, nulls);
1000 }
1001
1002 heap_freetuple(tuple); /* free either original or new tuple */
1004
1008 if (oldtablename)
1010 if (newtablename)
1012
1013 /*
1014 * Update relation's pg_class entry; if necessary; and if not, send an SI
1015 * message to make other backends (and this one) rebuild relcache entries.
1016 */
1020 if (!HeapTupleIsValid(tuple))
1021 elog(ERROR, "cache lookup failed for relation %u",
1022 RelationGetRelid(rel));
1023 if (!((Form_pg_class) GETSTRUCT(tuple))->relhastriggers)
1024 {
1025 ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
1026
1027 CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
1028
1030 }
1031 else
1033
1034 heap_freetuple(tuple);
1036
1037 /*
1038 * If we're replacing a trigger, flush all the old dependencies before
1039 * recording new ones.
1040 */
1041 if (trigger_exists)
1043
1044 /*
1045 * Record dependencies for trigger. Always place a normal dependency on
1046 * the function.
1047 */
1048 myself.classId = TriggerRelationId;
1049 myself.objectId = trigoid;
1050 myself.objectSubId = 0;
1051
1053 referenced.objectId = funcoid;
1054 referenced.objectSubId = 0;
1056
1058 {
1059 /*
1060 * Internally-generated trigger for a constraint, so make it an
1061 * internal dependency of the constraint. We can skip depending on
1062 * the relation(s), as there'll be an indirect dependency via the
1063 * constraint.
1064 */
1066 referenced.objectId = constraintOid;
1067 referenced.objectSubId = 0;
1069 }
1070 else
1071 {
1072 /*
1073 * User CREATE TRIGGER, so place dependencies. We make trigger be
1074 * auto-dropped if its relation is dropped or if the FK relation is
1075 * dropped. (Auto drop is compatible with our pre-7.3 behavior.)
1076 */
1078 referenced.objectId = RelationGetRelid(rel);
1079 referenced.objectSubId = 0;
1081
1083 {
1085 referenced.objectId = constrrelid;
1086 referenced.objectSubId = 0;
1088 }
1089 /* Not possible to have an index dependency in this case */
1090 Assert(!OidIsValid(indexOid));
1091
1092 /*
1093 * If it's a user-specified constraint trigger, make the constraint
1094 * internally dependent on the trigger instead of vice versa.
1095 */
1097 {
1099 referenced.objectId = constraintOid;
1100 referenced.objectSubId = 0;
1102 }
1103
1104 /*
1105 * If it's a partition trigger, create the partition dependencies.
1106 */
1108 {
1113 }
1114 }
1115
1116 /* If column-specific trigger, add normal dependencies on columns */
1117 if (columns != NULL)
1118 {
1119 int i;
1120
1122 referenced.objectId = RelationGetRelid(rel);
1123 for (i = 0; i < ncolumns; i++)
1124 {
1125 referenced.objectSubId = columns[i];
1127 }
1128 }
1129
1130 /*
1131 * If it has a WHEN clause, add dependencies on objects mentioned in the
1132 * expression (eg, functions, as well as any columns used).
1133 */
1134 if (whenRtable != NIL)
1137
1138 /* Post creation hook for new trigger */
1140 isInternal);
1141
1142 /*
1143 * Lastly, create the trigger on child relations, if needed.
1144 */
1146 {
1147 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1148 int i;
1151
1153 "part trig clone",
1155
1156 /*
1157 * We don't currently expect to be called with a valid indexOid. If
1158 * that ever changes then we'll need to write code here to find the
1159 * corresponding child index.
1160 */
1161 Assert(!OidIsValid(indexOid));
1162
1164
1165 /* Iterate to create the trigger on each existing partition */
1166 for (i = 0; i < partdesc->nparts; i++)
1167 {
1170 Node *qual;
1171
1173
1174 /*
1175 * Initialize our fabricated parse node by copying the original
1176 * one, then resetting fields that we pass separately.
1177 */
1179 childStmt->funcname = NIL;
1180 childStmt->whenClause = NULL;
1181
1182 /* If there is a WHEN clause, create a modified copy of it */
1183 qual = copyObject(whenClause);
1184 qual = (Node *)
1186 childTbl, rel);
1187 qual = (Node *)
1189 childTbl, rel);
1190
1191 CreateTriggerFiringOn(childStmt, queryString,
1192 partdesc->oids[i], refRelOid,
1194 funcoid, trigoid, qual,
1196
1198
1200 }
1201
1204 }
1205
1206 /* Keep lock on target rel until end of xact */
1207 table_close(rel, NoLock);
1208
1209 return myself;
1210}
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:3879
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4082
#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:202
#define NameStr(name)
Definition c.h:835
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:448
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:684
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:1669
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:2148
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2223
Oid get_func_rettype(Oid funcid)
Definition lsyscache.c:1875
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:232
#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:47
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition pg_depend.c:303
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:260
static Datum PointerGetDatum(const void *X)
Definition postgres.h:342
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
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:135
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, 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 1728 of file trigger.c.

1731{
1733 int nkeys;
1734 ScanKeyData keys[2];
1736 HeapTuple tuple;
1737 bool found;
1738 bool changed;
1739
1740 /* Scan the relevant entries in pg_triggers */
1742
1743 ScanKeyInit(&keys[0],
1747 if (tgname)
1748 {
1749 ScanKeyInit(&keys[1],
1752 CStringGetDatum(tgname));
1753 nkeys = 2;
1754 }
1755 else
1756 nkeys = 1;
1757
1759 NULL, nkeys, keys);
1760
1761 found = changed = false;
1762
1763 while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1764 {
1766
1767 if (OidIsValid(tgparent) && tgparent != oldtrig->tgparentid)
1768 continue;
1769
1770 if (oldtrig->tgisinternal)
1771 {
1772 /* system trigger ... ok to process? */
1773 if (skip_system)
1774 continue;
1775 if (!superuser())
1776 ereport(ERROR,
1778 errmsg("permission denied: \"%s\" is a system trigger",
1779 NameStr(oldtrig->tgname))));
1780 }
1781
1782 found = true;
1783
1784 if (oldtrig->tgenabled != fires_when)
1785 {
1786 /* need to change this one ... make a copy to scribble on */
1789
1790 newtrig->tgenabled = fires_when;
1791
1793
1795
1796 changed = true;
1797 }
1798
1799 /*
1800 * When altering FOR EACH ROW triggers on a partitioned table, do the
1801 * same on the partitions as well, unless ONLY is specified.
1802 *
1803 * Note that we recurse even if we didn't change the trigger above,
1804 * because the partitions' copy of the trigger may have a different
1805 * value of tgenabled than the parent's trigger and thus might need to
1806 * be changed.
1807 */
1808 if (recurse &&
1809 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
1810 (TRIGGER_FOR_ROW(oldtrig->tgtype)))
1811 {
1812 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1813 int i;
1814
1815 for (i = 0; i < partdesc->nparts; i++)
1816 {
1817 Relation part;
1818
1819 part = relation_open(partdesc->oids[i], lockmode);
1820 /* Match on child triggers' tgparentid, not their name */
1822 fires_when, skip_system, recurse,
1823 lockmode);
1824 table_close(part, NoLock); /* keep lock till commit */
1825 }
1826 }
1827
1829 oldtrig->oid, 0);
1830 }
1831
1833
1835
1836 if (tgname && !found)
1837 ereport(ERROR,
1839 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1840 tgname, RelationGetRelationName(rel))));
1841
1842 /*
1843 * If we changed anything, broadcast a SI inval message to force each
1844 * backend (including our own!) to rebuild relation's relcache entry.
1845 * Otherwise they will fail to apply the change promptly.
1846 */
1847 if (changed)
1849}
void CacheInvalidateRelcache(Relation relation)
Definition inval.c:1635
#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:1728

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 2803 of file trigger.c.

2809{
2810 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2811
2812 if (relinfo->ri_FdwRoutine && transition_capture &&
2813 transition_capture->tcs_delete_old_table)
2814 {
2815 Assert(relinfo->ri_RootResultRelInfo);
2816 ereport(ERROR,
2818 errmsg("cannot collect transition tuples from child foreign tables")));
2819 }
2820
2821 if ((trigdesc && trigdesc->trig_delete_after_row) ||
2822 (transition_capture && transition_capture->tcs_delete_old_table))
2823 {
2825
2827 if (fdw_trigtuple == NULL)
2828 GetTupleForTrigger(estate,
2829 NULL,
2830 relinfo,
2831 tupleid,
2833 slot,
2834 false,
2835 NULL,
2836 NULL,
2837 NULL);
2838 else
2840
2843 true, slot, NULL, NIL, NULL,
2844 transition_capture,
2846 }
2847}
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:3346

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 2545 of file trigger.c.

2548{
2549 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2550
2551 if (relinfo->ri_FdwRoutine && transition_capture &&
2552 transition_capture->tcs_insert_new_table)
2553 {
2554 Assert(relinfo->ri_RootResultRelInfo);
2555 ereport(ERROR,
2557 errmsg("cannot collect transition tuples from child foreign tables")));
2558 }
2559
2560 if ((trigdesc && trigdesc->trig_insert_after_row) ||
2561 (transition_capture && transition_capture->tcs_insert_new_table))
2564 true, NULL, slot,
2566 transition_capture,
2567 false);
2568}

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 3146 of file trigger.c.

3155{
3156 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3157
3158 if (relinfo->ri_FdwRoutine && transition_capture &&
3159 (transition_capture->tcs_update_old_table ||
3160 transition_capture->tcs_update_new_table))
3161 {
3162 Assert(relinfo->ri_RootResultRelInfo);
3163 ereport(ERROR,
3165 errmsg("cannot collect transition tuples from child foreign tables")));
3166 }
3167
3168 if ((trigdesc && trigdesc->trig_update_after_row) ||
3169 (transition_capture &&
3170 (transition_capture->tcs_update_old_table ||
3171 transition_capture->tcs_update_new_table)))
3172 {
3173 /*
3174 * Note: if the UPDATE is converted into a DELETE+INSERT as part of
3175 * update-partition-key operation, then this function is also called
3176 * separately for DELETE and INSERT to capture transition table rows.
3177 * In such case, either old tuple or new tuple can be NULL.
3178 */
3181
3184
3187
3189 GetTupleForTrigger(estate,
3190 NULL,
3191 tupsrc,
3192 tupleid,
3194 oldslot,
3195 false,
3196 NULL,
3197 NULL,
3198 NULL);
3199 else if (fdw_trigtuple != NULL)
3201 else
3203
3207 true,
3210 transition_capture,
3212 }
3213}
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 2683 of file trigger.c.

2685{
2686 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2687
2688 if (trigdesc && trigdesc->trig_delete_after_statement)
2691 false, NULL, NULL, NIL, NULL, transition_capture,
2692 false);
2693}
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 2454 of file trigger.c.

2456{
2457 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2458
2459 if (trigdesc && trigdesc->trig_insert_after_statement)
2462 false, NULL, NULL, NIL, NULL, transition_capture,
2463 false);
2464}
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 3329 of file trigger.c.

3330{
3331 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3332
3333 if (trigdesc && trigdesc->trig_truncate_after_statement)
3335 NULL, NULL,
3337 false, NULL, NULL, NIL, NULL, NULL,
3338 false);
3339}
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 2955 of file trigger.c.

2957{
2958 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2959
2960 /* statement-level triggers operate on the parent table */
2961 Assert(relinfo->ri_RootResultRelInfo == NULL);
2962
2963 if (trigdesc && trigdesc->trig_update_after_statement)
2966 false, NULL, NULL, NIL,
2968 transition_capture,
2969 false);
2970}
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 2703 of file trigger.c.

2711{
2713 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2714 bool result = true;
2717 bool should_free = false;
2718 int i;
2719
2721 if (fdw_trigtuple == NULL)
2722 {
2724
2725 /*
2726 * Get a copy of the on-disk tuple we are planning to delete. In
2727 * general, if the tuple has been concurrently updated, we should
2728 * recheck it using EPQ. However, if this is a MERGE DELETE action,
2729 * we skip this EPQ recheck and leave it to the caller (it must do
2730 * additional rechecking, and might end up executing a different
2731 * action entirely).
2732 */
2733 if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2735 &epqslot_candidate, tmresult, tmfd))
2736 return false;
2737
2738 /*
2739 * If the tuple was concurrently updated and the caller of this
2740 * function requested for the updated tuple, skip the trigger
2741 * execution.
2742 */
2743 if (epqslot_candidate != NULL && epqslot != NULL)
2744 {
2746 return false;
2747 }
2748
2750 }
2751 else
2752 {
2754 ExecForceStoreHeapTuple(trigtuple, slot, false);
2755 }
2756
2761 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2762 for (i = 0; i < trigdesc->numtriggers; i++)
2763 {
2764 HeapTuple newtuple;
2765 Trigger *trigger = &trigdesc->triggers[i];
2766
2767 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2771 continue;
2772 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2773 NULL, slot, NULL))
2774 continue;
2775
2776 LocTriggerData.tg_trigslot = slot;
2777 LocTriggerData.tg_trigtuple = trigtuple;
2778 LocTriggerData.tg_trigger = trigger;
2780 i,
2781 relinfo->ri_TrigFunctions,
2782 relinfo->ri_TrigInstrument,
2783 GetPerTupleMemoryContext(estate));
2784 if (newtuple == NULL)
2785 {
2786 result = false; /* tell caller to suppress delete */
2787 break;
2788 }
2789 if (newtuple != trigtuple)
2790 heap_freetuple(newtuple);
2791 }
2792 if (should_free)
2794
2795 return result;
2796}
#define GetPerTupleMemoryContext(estate)
Definition executor.h:672
#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 2467 of file trigger.c.

2469{
2470 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2471 HeapTuple newtuple = NULL;
2472 bool should_free;
2474 int i;
2475
2480 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2481 for (i = 0; i < trigdesc->numtriggers; i++)
2482 {
2483 Trigger *trigger = &trigdesc->triggers[i];
2484 HeapTuple oldtuple;
2485
2486 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2490 continue;
2491 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2492 NULL, NULL, slot))
2493 continue;
2494
2495 if (!newtuple)
2496 newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2497
2498 LocTriggerData.tg_trigslot = slot;
2499 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2500 LocTriggerData.tg_trigger = trigger;
2502 i,
2503 relinfo->ri_TrigFunctions,
2504 relinfo->ri_TrigInstrument,
2505 GetPerTupleMemoryContext(estate));
2506 if (newtuple == NULL)
2507 {
2508 if (should_free)
2509 heap_freetuple(oldtuple);
2510 return false; /* "do nothing" */
2511 }
2512 else if (newtuple != oldtuple)
2513 {
2514 newtuple = check_modified_virtual_generated(RelationGetDescr(relinfo->ri_RelationDesc), newtuple);
2515
2516 ExecForceStoreHeapTuple(newtuple, slot, false);
2517
2518 /*
2519 * After a tuple in a partition goes through a trigger, the user
2520 * could have changed the partition key enough that the tuple no
2521 * longer fits the partition. Verify that.
2522 */
2523 if (trigger->tgisclone &&
2524 !ExecPartitionCheck(relinfo, slot, estate, false))
2525 ereport(ERROR,
2527 errmsg("moving row to another partition during a BEFORE FOR EACH ROW trigger is not supported"),
2528 errdetail("Before executing trigger \"%s\", the row was to be in partition \"%s.%s\".",
2529 trigger->tgname,
2531 RelationGetRelationName(relinfo->ri_RelationDesc))));
2532
2533 if (should_free)
2534 heap_freetuple(oldtuple);
2535
2536 /* signal tuple should be re-fetched if used */
2537 newtuple = NULL;
2538 }
2539 }
2540
2541 return true;
2542}
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition execMain.c:1885
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3588
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 2973 of file trigger.c.

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

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 2632 of file trigger.c.

2633{
2634 TriggerDesc *trigdesc;
2635 int i;
2637
2638 trigdesc = relinfo->ri_TrigDesc;
2639
2640 if (trigdesc == NULL)
2641 return;
2642 if (!trigdesc->trig_delete_before_statement)
2643 return;
2644
2645 /* no-op if we already fired BS triggers in this context */
2647 CMD_DELETE))
2648 return;
2649
2653 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2654 for (i = 0; i < trigdesc->numtriggers; i++)
2655 {
2656 Trigger *trigger = &trigdesc->triggers[i];
2657 HeapTuple newtuple;
2658
2659 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2663 continue;
2664 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2665 NULL, NULL, NULL))
2666 continue;
2667
2668 LocTriggerData.tg_trigger = trigger;
2670 i,
2671 relinfo->ri_TrigFunctions,
2672 relinfo->ri_TrigInstrument,
2673 GetPerTupleMemoryContext(estate));
2674
2675 if (newtuple)
2676 ereport(ERROR,
2678 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2679 }
2680}
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 2403 of file trigger.c.

2404{
2405 TriggerDesc *trigdesc;
2406 int i;
2408
2409 trigdesc = relinfo->ri_TrigDesc;
2410
2411 if (trigdesc == NULL)
2412 return;
2413 if (!trigdesc->trig_insert_before_statement)
2414 return;
2415
2416 /* no-op if we already fired BS triggers in this context */
2418 CMD_INSERT))
2419 return;
2420
2424 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2425 for (i = 0; i < trigdesc->numtriggers; i++)
2426 {
2427 Trigger *trigger = &trigdesc->triggers[i];
2428 HeapTuple newtuple;
2429
2430 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2434 continue;
2435 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2436 NULL, NULL, NULL))
2437 continue;
2438
2439 LocTriggerData.tg_trigger = trigger;
2441 i,
2442 relinfo->ri_TrigFunctions,
2443 relinfo->ri_TrigInstrument,
2444 GetPerTupleMemoryContext(estate));
2445
2446 if (newtuple)
2447 ereport(ERROR,
2449 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2450 }
2451}
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 3282 of file trigger.c.

3283{
3284 TriggerDesc *trigdesc;
3285 int i;
3287
3288 trigdesc = relinfo->ri_TrigDesc;
3289
3290 if (trigdesc == NULL)
3291 return;
3292 if (!trigdesc->trig_truncate_before_statement)
3293 return;
3294
3298 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3299
3300 for (i = 0; i < trigdesc->numtriggers; i++)
3301 {
3302 Trigger *trigger = &trigdesc->triggers[i];
3303 HeapTuple newtuple;
3304
3305 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3309 continue;
3310 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3311 NULL, NULL, NULL))
3312 continue;
3313
3314 LocTriggerData.tg_trigger = trigger;
3316 i,
3317 relinfo->ri_TrigFunctions,
3318 relinfo->ri_TrigInstrument,
3319 GetPerTupleMemoryContext(estate));
3320
3321 if (newtuple)
3322 ereport(ERROR,
3324 errmsg("BEFORE STATEMENT trigger cannot return a value")));
3325 }
3326}
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 2897 of file trigger.c.

2898{
2899 TriggerDesc *trigdesc;
2900 int i;
2902 Bitmapset *updatedCols;
2903
2904 trigdesc = relinfo->ri_TrigDesc;
2905
2906 if (trigdesc == NULL)
2907 return;
2908 if (!trigdesc->trig_update_before_statement)
2909 return;
2910
2911 /* no-op if we already fired BS triggers in this context */
2913 CMD_UPDATE))
2914 return;
2915
2916 /* statement-level triggers operate on the parent table */
2917 Assert(relinfo->ri_RootResultRelInfo == NULL);
2918
2919 updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
2920
2924 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2925 LocTriggerData.tg_updatedcols = updatedCols;
2926 for (i = 0; i < trigdesc->numtriggers; i++)
2927 {
2928 Trigger *trigger = &trigdesc->triggers[i];
2929 HeapTuple newtuple;
2930
2931 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2935 continue;
2936 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2937 updatedCols, NULL, NULL))
2938 continue;
2939
2940 LocTriggerData.tg_trigger = trigger;
2942 i,
2943 relinfo->ri_TrigFunctions,
2944 relinfo->ri_TrigInstrument,
2945 GetPerTupleMemoryContext(estate));
2946
2947 if (newtuple)
2948 ereport(ERROR,
2950 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2951 }
2952}
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 2311 of file trigger.c.

2316{
2317 LOCAL_FCINFO(fcinfo, 0);
2319 Datum result;
2321
2322 /*
2323 * Protect against code paths that may fail to initialize transition table
2324 * info.
2325 */
2327 TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event) ||
2328 TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) &&
2329 TRIGGER_FIRED_AFTER(trigdata->tg_event) &&
2330 !(trigdata->tg_event & AFTER_TRIGGER_DEFERRABLE) &&
2331 !(trigdata->tg_event & AFTER_TRIGGER_INITDEFERRED)) ||
2332 (trigdata->tg_oldtable == NULL && trigdata->tg_newtable == NULL));
2333
2334 finfo += tgindx;
2335
2336 /*
2337 * We cache fmgr lookup info, to avoid making the lookup again on each
2338 * call.
2339 */
2340 if (finfo->fn_oid == InvalidOid)
2341 fmgr_info(trigdata->tg_trigger->tgfoid, finfo);
2342
2343 Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid);
2344
2345 /*
2346 * If doing EXPLAIN ANALYZE, start charging time to this trigger.
2347 */
2348 if (instr)
2349 InstrStartTrigger(instr + tgindx);
2350
2351 /*
2352 * Do the function evaluation in the per-tuple memory context, so that
2353 * leaked memory will be reclaimed once per tuple. Note in particular that
2354 * any new tuple created by the trigger function will live till the end of
2355 * the tuple cycle.
2356 */
2358
2359 /*
2360 * Call the function, passing no arguments but setting a context.
2361 */
2362 InitFunctionCallInfoData(*fcinfo, finfo, 0,
2363 InvalidOid, (Node *) trigdata, NULL);
2364
2366
2368 PG_TRY();
2369 {
2370 result = FunctionCallInvoke(fcinfo);
2371 }
2372 PG_FINALLY();
2373 {
2375 }
2376 PG_END_TRY();
2377
2379
2381
2382 /*
2383 * Trigger protocol allows function to return a null pointer, but NOT to
2384 * set the isnull result flag.
2385 */
2386 if (fcinfo->isnull)
2387 ereport(ERROR,
2389 errmsg("trigger function %u returned null value",
2390 fcinfo->flinfo->fn_oid)));
2391
2392 /*
2393 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
2394 * the firing of the trigger.
2395 */
2396 if (instr)
2397 InstrStopTrigger(instr + tgindx, 1);
2398
2400}
#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 2850 of file trigger.c.

2852{
2853 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2856 int i;
2857
2862 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2863
2864 ExecForceStoreHeapTuple(trigtuple, slot, false);
2865
2866 for (i = 0; i < trigdesc->numtriggers; i++)
2867 {
2869 Trigger *trigger = &trigdesc->triggers[i];
2870
2871 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2875 continue;
2876 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2877 NULL, slot, NULL))
2878 continue;
2879
2880 LocTriggerData.tg_trigslot = slot;
2881 LocTriggerData.tg_trigtuple = trigtuple;
2882 LocTriggerData.tg_trigger = trigger;
2884 i,
2885 relinfo->ri_TrigFunctions,
2886 relinfo->ri_TrigInstrument,
2887 GetPerTupleMemoryContext(estate));
2888 if (rettuple == NULL)
2889 return false; /* Delete was suppressed */
2890 if (rettuple != trigtuple)
2892 }
2893 return true;
2894}
#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 2571 of file trigger.c.

2573{
2574 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2575 HeapTuple newtuple = NULL;
2576 bool should_free;
2578 int i;
2579
2584 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2585 for (i = 0; i < trigdesc->numtriggers; i++)
2586 {
2587 Trigger *trigger = &trigdesc->triggers[i];
2588 HeapTuple oldtuple;
2589
2590 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2594 continue;
2595 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2596 NULL, NULL, slot))
2597 continue;
2598
2599 if (!newtuple)
2600 newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2601
2602 LocTriggerData.tg_trigslot = slot;
2603 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2604 LocTriggerData.tg_trigger = trigger;
2606 i,
2607 relinfo->ri_TrigFunctions,
2608 relinfo->ri_TrigInstrument,
2609 GetPerTupleMemoryContext(estate));
2610 if (newtuple == NULL)
2611 {
2612 if (should_free)
2613 heap_freetuple(oldtuple);
2614 return false; /* "do nothing" */
2615 }
2616 else if (newtuple != oldtuple)
2617 {
2618 ExecForceStoreHeapTuple(newtuple, slot, false);
2619
2620 if (should_free)
2621 heap_freetuple(oldtuple);
2622
2623 /* signal tuple should be re-fetched if used */
2624 newtuple = NULL;
2625 }
2626 }
2627
2628 return true;
2629}

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 3216 of file trigger.c.

3218{
3219 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3221 HeapTuple newtuple = NULL;
3222 bool should_free;
3224 int i;
3225
3230 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3231
3233
3234 for (i = 0; i < trigdesc->numtriggers; i++)
3235 {
3236 Trigger *trigger = &trigdesc->triggers[i];
3237 HeapTuple oldtuple;
3238
3239 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3243 continue;
3244 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3245 NULL, oldslot, newslot))
3246 continue;
3247
3248 if (!newtuple)
3249 newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free);
3250
3251 LocTriggerData.tg_trigslot = oldslot;
3252 LocTriggerData.tg_trigtuple = trigtuple;
3253 LocTriggerData.tg_newslot = newslot;
3254 LocTriggerData.tg_newtuple = oldtuple = newtuple;
3255
3256 LocTriggerData.tg_trigger = trigger;
3258 i,
3259 relinfo->ri_TrigFunctions,
3260 relinfo->ri_TrigInstrument,
3261 GetPerTupleMemoryContext(estate));
3262 if (newtuple == NULL)
3263 {
3264 return false; /* "do nothing" */
3265 }
3266 else if (newtuple != oldtuple)
3267 {
3268 ExecForceStoreHeapTuple(newtuple, newslot, false);
3269
3270 if (should_free)
3271 heap_freetuple(oldtuple);
3272
3273 /* signal tuple should be re-fetched if used */
3274 newtuple = NULL;
3275 }
3276 }
3277
3278 return true;
3279}

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 2279 of file trigger.c.

2280{
2281 if (trigdesc != NULL)
2282 {
2283 int i;
2284
2285 for (i = 0; i < trigdesc->numtriggers; ++i)
2286 {
2287 Trigger *trigger = &trigdesc->triggers[i];
2288
2289 if (!TRIGGER_FOR_ROW(trigger->tgtype))
2290 continue;
2291 if (trigger->tgoldtable != NULL || trigger->tgnewtable != NULL)
2292 return trigger->tgname;
2293 }
2294 }
2295
2296 return NULL;
2297}
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 2147 of file trigger.c.

2148{
2150 int i;
2151
2152 if (trigdesc == NULL)
2153 return;
2154
2155 trigger = trigdesc->triggers;
2156 for (i = 0; i < trigdesc->numtriggers; i++)
2157 {
2158 pfree(trigger->tgname);
2159 if (trigger->tgnattr > 0)
2160 pfree(trigger->tgattr);
2161 if (trigger->tgnargs > 0)
2162 {
2163 while (--(trigger->tgnargs) >= 0)
2164 pfree(trigger->tgargs[trigger->tgnargs]);
2165 pfree(trigger->tgargs);
2166 }
2167 if (trigger->tgqual)
2168 pfree(trigger->tgqual);
2169 if (trigger->tgoldtable)
2170 pfree(trigger->tgoldtable);
2171 if (trigger->tgnewtable)
2172 pfree(trigger->tgnewtable);
2173 trigger++;
2174 }
2175 pfree(trigdesc->triggers);
2176 pfree(trigdesc);
2177}

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 1372 of file trigger.c.

1373{
1375 ScanKeyData skey[2];
1377 HeapTuple tup;
1378 Oid oid;
1379
1380 /*
1381 * Find the trigger, verify permissions, set up object address
1382 */
1384
1385 ScanKeyInit(&skey[0],
1388 ObjectIdGetDatum(relid));
1389 ScanKeyInit(&skey[1],
1392 CStringGetDatum(trigname));
1393
1395 NULL, 2, skey);
1396
1398
1399 if (!HeapTupleIsValid(tup))
1400 {
1401 if (!missing_ok)
1402 ereport(ERROR,
1404 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1405 trigname, get_rel_name(relid))));
1406 oid = InvalidOid;
1407 }
1408 else
1409 {
1410 oid = ((Form_pg_trigger) GETSTRUCT(tup))->oid;
1411 }
1412
1415 return oid;
1416}

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 4931 of file trigger.c.

4933{
4934 /* Create it if not already done. */
4935 if (!table->storeslot)
4936 {
4938
4939 /*
4940 * We need this slot only until AfterTriggerEndQuery, but making it
4941 * last till end-of-subxact is good enough. It'll be freed by
4942 * AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4943 * a different lifespan, so we'd better make a copy of that.
4944 */
4946 tupdesc = CreateTupleDescCopy(tupdesc);
4947 table->storeslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
4949 }
4950
4951 return table->storeslot;
4952}

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

Referenced by TransitionTableAddTuple().

◆ GetAfterTriggersTableData()

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

Definition at line 4889 of file trigger.c.

4890{
4894 ListCell *lc;
4895
4896 /* At this level, cmdType should not be, eg, CMD_MERGE */
4897 Assert(cmdType == CMD_INSERT ||
4898 cmdType == CMD_UPDATE ||
4899 cmdType == CMD_DELETE);
4900
4901 /* Caller should have ensured query_depth is OK. */
4905
4906 foreach(lc, qs->tables)
4907 {
4909 if (table->relid == relid && table->cmdType == cmdType &&
4910 !table->closed)
4911 return table;
4912 }
4913
4915
4917 table->relid = relid;
4918 table->cmdType = cmdType;
4919 qs->tables = lappend(qs->tables, table);
4920
4922
4923 return table;
4924}

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 5589 of file trigger.c.

5593{
5594 Tuplestorestate *tuplestore = NULL;
5595 bool delete_old_table = transition_capture->tcs_delete_old_table;
5596 bool update_old_table = transition_capture->tcs_update_old_table;
5597 bool update_new_table = transition_capture->tcs_update_new_table;
5598 bool insert_new_table = transition_capture->tcs_insert_new_table;
5599
5600 /*
5601 * For INSERT events NEW should be non-NULL, for DELETE events OLD should
5602 * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
5603 * non-NULL. But for UPDATE events fired for capturing transition tuples
5604 * during UPDATE partition-key row movement, OLD is NULL when the event is
5605 * for a row being inserted, whereas NEW is NULL when the event is for a
5606 * row being deleted.
5607 */
5609 TupIsNull(oldslot)));
5611 TupIsNull(newslot)));
5612
5613 if (!TupIsNull(oldslot))
5614 {
5616 if (event == TRIGGER_EVENT_DELETE && delete_old_table)
5617 tuplestore = transition_capture->tcs_delete_private->old_tuplestore;
5618 else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
5619 tuplestore = transition_capture->tcs_update_private->old_tuplestore;
5620 }
5621 else if (!TupIsNull(newslot))
5622 {
5624 if (event == TRIGGER_EVENT_INSERT && insert_new_table)
5625 tuplestore = transition_capture->tcs_insert_private->new_tuplestore;
5626 else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
5627 tuplestore = transition_capture->tcs_update_private->new_tuplestore;
5628 }
5629
5630 return tuplestore;
5631}

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 3346 of file trigger.c.

3356{
3357 Relation relation = relinfo->ri_RelationDesc;
3358
3359 if (epqslot != NULL)
3360 {
3362 TM_FailureData tmfd;
3363 int lockflags = 0;
3364
3365 *epqslot = NULL;
3366
3367 /* caller must pass an epqstate if EvalPlanQual is possible */
3368 Assert(epqstate != NULL);
3369
3370 /*
3371 * lock tuple for update
3372 */
3375 test = table_tuple_lock(relation, tid, estate->es_snapshot, oldslot,
3376 estate->es_output_cid,
3377 lockmode, LockWaitBlock,
3378 lockflags,
3379 &tmfd);
3380
3381 /* Let the caller know about the status of this operation */
3382 if (tmresultp)
3383 *tmresultp = test;
3384 if (tmfdp)
3385 *tmfdp = tmfd;
3386
3387 switch (test)
3388 {
3389 case TM_SelfModified:
3390
3391 /*
3392 * The target tuple was already updated or deleted by the
3393 * current command, or by a later command in the current
3394 * transaction. We ignore the tuple in the former case, and
3395 * throw error in the latter case, for the same reasons
3396 * enumerated in ExecUpdate and ExecDelete in
3397 * nodeModifyTable.c.
3398 */
3399 if (tmfd.cmax != estate->es_output_cid)
3400 ereport(ERROR,
3402 errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
3403 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3404
3405 /* treat it as deleted; do not process */
3406 return false;
3407
3408 case TM_Ok:
3409 if (tmfd.traversed)
3410 {
3411 /*
3412 * Recheck the tuple using EPQ, if requested. Otherwise,
3413 * just return that it was concurrently updated.
3414 */
3415 if (do_epq_recheck)
3416 {
3417 *epqslot = EvalPlanQual(epqstate,
3418 relation,
3419 relinfo->ri_RangeTableIndex,
3420 oldslot);
3421
3422 /*
3423 * If PlanQual failed for updated tuple - we must not
3424 * process this tuple!
3425 */
3426 if (TupIsNull(*epqslot))
3427 {
3428 *epqslot = NULL;
3429 return false;
3430 }
3431 }
3432 else
3433 {
3434 if (tmresultp)
3436 return false;
3437 }
3438 }
3439 break;
3440
3441 case TM_Updated:
3443 ereport(ERROR,
3445 errmsg("could not serialize access due to concurrent update")));
3446 elog(ERROR, "unexpected table_tuple_lock status: %u", test);
3447 break;
3448
3449 case TM_Deleted:
3451 ereport(ERROR,
3453 errmsg("could not serialize access due to concurrent delete")));
3454 /* tuple was deleted */
3455 return false;
3456
3457 case TM_Invisible:
3458 elog(ERROR, "attempted to lock invisible tuple");
3459 break;
3460
3461 default:
3462 elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
3463 return false; /* keep compiler quiet */
3464 }
3465 }
3466 else
3467 {
3468 /*
3469 * We expect the tuple to be present, thus very simple error handling
3470 * suffices.
3471 */
3472 if (!table_tuple_fetch_row_version(relation, tid, SnapshotAny,
3473 oldslot))
3474 elog(ERROR, "failed to fetch tuple for trigger");
3475 }
3476
3477 return true;
3478}
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition execMain.c:2678
@ 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:718
Snapshot es_snapshot
Definition execnodes.h:696
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:1648
#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 4980 of file trigger.c.

4981{
4983 bool need_old_upd,
4992
4993 if (trigdesc == NULL)
4994 return NULL;
4995
4996 /* Detect which table(s) we need. */
4997 switch (cmdType)
4998 {
4999 case CMD_INSERT:
5002 break;
5003 case CMD_UPDATE:
5006 need_old_del = need_new_ins = false;
5007 break;
5008 case CMD_DELETE:
5011 break;
5012 case CMD_MERGE:
5017 break;
5018 default:
5019 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
5020 /* keep compiler quiet */
5022 break;
5023 }
5025 return NULL;
5026
5027 /* Check state, like AfterTriggerSaveEvent. */
5028 if (afterTriggers.query_depth < 0)
5029 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
5030
5031 /* Be sure we have enough space to record events at this query depth. */
5034
5035 /*
5036 * Find or create AfterTriggersTableData struct(s) to hold the
5037 * tuplestore(s). If there's a matching struct but it's marked closed,
5038 * ignore it; we need a newer one.
5039 *
5040 * Note: MERGE must use the same AfterTriggersTableData structs as INSERT,
5041 * UPDATE, and DELETE, so that any MERGE'd tuples are added to the same
5042 * tuplestores as tuples from any INSERT, UPDATE, or DELETE commands
5043 * running in the same top-level command (e.g., in a writable CTE).
5044 *
5045 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
5046 * allocated in the current (sub)transaction's CurTransactionContext, and
5047 * the tuplestores are managed by the (sub)transaction's resource owner.
5048 * This is sufficient lifespan because we do not allow triggers using
5049 * transition tables to be deferrable; they will be fired during
5050 * AfterTriggerEndQuery, after which it's okay to delete the data.
5051 */
5052 if (need_new_ins)
5054 else
5055 ins_table = NULL;
5056
5059 else
5060 upd_table = NULL;
5061
5062 if (need_old_del)
5064 else
5065 del_table = NULL;
5066
5067 /* Now create required tuplestore(s), if we don't have them already. */
5071
5072 if (need_old_upd && upd_table->old_tuplestore == NULL)
5073 upd_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5074 if (need_new_upd && upd_table->new_tuplestore == NULL)
5075 upd_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5076 if (need_old_del && del_table->old_tuplestore == NULL)
5077 del_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5078 if (need_new_ins && ins_table->new_tuplestore == NULL)
5079 ins_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5080
5083
5084 /* Now build the TransitionCaptureState struct, in caller's context */
5086 state->tcs_delete_old_table = need_old_del;
5087 state->tcs_update_old_table = need_old_upd;
5088 state->tcs_update_new_table = need_new_upd;
5089 state->tcs_insert_new_table = need_new_ins;
5090 state->tcs_insert_private = ins_table;
5091 state->tcs_update_private = upd_table;
5092 state->tcs_delete_private = del_table;
5093
5094 return state;
5095}

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 6782 of file trigger.c.

6783{
6785}

References MyTriggerDepth, and PG_RETURN_INT32.

◆ RangeVarCallbackForRenameTrigger()

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

Definition at line 1422 of file trigger.c.

1424{
1425 HeapTuple tuple;
1427
1428 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1429 if (!HeapTupleIsValid(tuple))
1430 return; /* concurrently dropped */
1431 form = (Form_pg_class) GETSTRUCT(tuple);
1432
1433 /* only tables and views can have triggers */
1434 if (form->relkind != RELKIND_RELATION && form->relkind != RELKIND_VIEW &&
1435 form->relkind != RELKIND_FOREIGN_TABLE &&
1436 form->relkind != RELKIND_PARTITIONED_TABLE)
1437 ereport(ERROR,
1439 errmsg("relation \"%s\" cannot have triggers",
1440 rv->relname),
1442
1443 /* you must own the table to rename one of its triggers */
1446 if (!allowSystemTableMods && IsSystemClass(relid, form))
1447 ereport(ERROR,
1449 errmsg("permission denied: \"%s\" is a system catalog",
1450 rv->relname)));
1451
1452 ReleaseSysCache(tuple);
1453}
@ ACLCHECK_NOT_OWNER
Definition acl.h:186
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4133
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_relkind_not_supported(), errmsg, ERROR, fb(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IsSystemClass(), object_ownercheck(), ObjectIdGetDatum(), ReleaseSysCache(), RangeVar::relname, and SearchSysCache1().

Referenced by renametrig().

◆ RegisterAfterTriggerBatchCallback()

void RegisterAfterTriggerBatchCallback ( AfterTriggerBatchCallback  callback,
void arg 
)

Definition at line 6833 of file trigger.c.

6835{
6838
6839 /*
6840 * Allocate in TopTransactionContext so the item survives for the duration
6841 * of the batch, which may span multiple trigger invocations.
6842 *
6843 * Must be called while afterTriggers is active; callbacks registered
6844 * outside a trigger-firing context would never fire.
6845 */
6849 item = palloc(sizeof(AfterTriggerCallbackItem));
6850 item->callback = callback;
6851 item->arg = arg;
6852 if (afterTriggers.query_depth >= 0)
6853 {
6856
6857 qs->batch_callbacks = lappend(qs->batch_callbacks, item);
6858 }
6859 else
6863}

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 1863 of file trigger.c.

1864{
1865 TriggerDesc *trigdesc;
1866 int numtrigs;
1867 int maxtrigs;
1868 Trigger *triggers;
1872 HeapTuple htup;
1874 int i;
1875
1876 /*
1877 * Allocate a working array to hold the triggers (the array is extended if
1878 * necessary)
1879 */
1880 maxtrigs = 16;
1881 triggers = (Trigger *) palloc(maxtrigs * sizeof(Trigger));
1882 numtrigs = 0;
1883
1884 /*
1885 * Note: since we scan the triggers using TriggerRelidNameIndexId, we will
1886 * be reading the triggers in name order, except possibly during
1887 * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn
1888 * ensures that triggers will be fired in name order.
1889 */
1894
1897 NULL, 1, &skey);
1898
1900 {
1902 Trigger *build;
1903 Datum datum;
1904 bool isnull;
1905
1906 if (numtrigs >= maxtrigs)
1907 {
1908 maxtrigs *= 2;
1909 triggers = (Trigger *) repalloc(triggers, maxtrigs * sizeof(Trigger));
1910 }
1911 build = &(triggers[numtrigs]);
1912
1913 build->tgoid = pg_trigger->oid;
1915 NameGetDatum(&pg_trigger->tgname)));
1916 build->tgfoid = pg_trigger->tgfoid;
1917 build->tgtype = pg_trigger->tgtype;
1918 build->tgenabled = pg_trigger->tgenabled;
1919 build->tgisinternal = pg_trigger->tgisinternal;
1920 build->tgisclone = OidIsValid(pg_trigger->tgparentid);
1921 build->tgconstrrelid = pg_trigger->tgconstrrelid;
1922 build->tgconstrindid = pg_trigger->tgconstrindid;
1923 build->tgconstraint = pg_trigger->tgconstraint;
1924 build->tgdeferrable = pg_trigger->tgdeferrable;
1925 build->tginitdeferred = pg_trigger->tginitdeferred;
1926 build->tgnargs = pg_trigger->tgnargs;
1927 /* tgattr is first var-width field, so OK to access directly */
1928 build->tgnattr = pg_trigger->tgattr.dim1;
1929 if (build->tgnattr > 0)
1930 {
1931 build->tgattr = (int16 *) palloc(build->tgnattr * sizeof(int16));
1932 memcpy(build->tgattr, &(pg_trigger->tgattr.values),
1933 build->tgnattr * sizeof(int16));
1934 }
1935 else
1936 build->tgattr = NULL;
1937 if (build->tgnargs > 0)
1938 {
1939 bytea *val;
1940 char *p;
1941
1944 tgrel->rd_att, &isnull));
1945 if (isnull)
1946 elog(ERROR, "tgargs is null in trigger for relation \"%s\"",
1947 RelationGetRelationName(relation));
1948 p = (char *) VARDATA_ANY(val);
1949 build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
1950 for (i = 0; i < build->tgnargs; i++)
1951 {
1952 build->tgargs[i] = pstrdup(p);
1953 p += strlen(p) + 1;
1954 }
1955 }
1956 else
1957 build->tgargs = NULL;
1958
1960 tgrel->rd_att, &isnull);
1961 if (!isnull)
1962 build->tgoldtable =
1964 else
1965 build->tgoldtable = NULL;
1966
1968 tgrel->rd_att, &isnull);
1969 if (!isnull)
1970 build->tgnewtable =
1972 else
1973 build->tgnewtable = NULL;
1974
1975 datum = fastgetattr(htup, Anum_pg_trigger_tgqual,
1976 tgrel->rd_att, &isnull);
1977 if (!isnull)
1978 build->tgqual = TextDatumGetCString(datum);
1979 else
1980 build->tgqual = NULL;
1981
1982 numtrigs++;
1983 }
1984
1987
1988 /* There might not be any triggers */
1989 if (numtrigs == 0)
1990 {
1991 pfree(triggers);
1992 return;
1993 }
1994
1995 /* Build trigdesc */
1996 trigdesc = palloc0_object(TriggerDesc);
1997 trigdesc->triggers = triggers;
1998 trigdesc->numtriggers = numtrigs;
1999 for (i = 0; i < numtrigs; i++)
2000 SetTriggerFlags(trigdesc, &(triggers[i]));
2001
2002 /* Copy completed trigdesc into cache storage */
2004 relation->trigdesc = CopyTriggerDesc(trigdesc);
2006
2007 /* Release working memory */
2008 FreeTriggerDesc(trigdesc);
2009}
#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:169
Datum nameout(PG_FUNCTION_ARGS)
Definition name.c:71
static char * DatumGetCString(Datum X)
Definition postgres.h:355
static Datum NameGetDatum(const NameData *X)
Definition postgres.h:393
TriggerDesc * trigdesc
Definition rel.h:117
Definition c.h:776
void FreeTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2147
static void SetTriggerFlags(TriggerDesc *trigdesc, Trigger *trigger)
Definition trigger.c:2015
TriggerDesc * CopyTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2092
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 1293 of file trigger.c.

1294{
1297 ScanKeyData skey[1];
1298 HeapTuple tup;
1299 Oid relid;
1300 Relation rel;
1301
1303
1304 /*
1305 * Find the trigger to delete.
1306 */
1307 ScanKeyInit(&skey[0],
1310 ObjectIdGetDatum(trigOid));
1311
1313 NULL, 1, skey);
1314
1316 if (!HeapTupleIsValid(tup))
1317 elog(ERROR, "could not find tuple for trigger %u", trigOid);
1318
1319 /*
1320 * Open and exclusive-lock the relation the trigger belongs to.
1321 */
1322 relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
1323
1324 rel = table_open(relid, AccessExclusiveLock);
1325
1326 if (rel->rd_rel->relkind != RELKIND_RELATION &&
1327 rel->rd_rel->relkind != RELKIND_VIEW &&
1328 rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
1329 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1330 ereport(ERROR,
1332 errmsg("relation \"%s\" cannot have triggers",
1334 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
1335
1337 ereport(ERROR,
1339 errmsg("permission denied: \"%s\" is a system catalog",
1341
1342 /*
1343 * Delete the pg_trigger tuple.
1344 */
1345 CatalogTupleDelete(tgrel, &tup->t_self);
1346
1349
1350 /*
1351 * We do not bother to try to determine whether any other triggers remain,
1352 * which would be needed in order to decide whether it's safe to clear the
1353 * relation's relhastriggers. (In any case, there might be a concurrent
1354 * process adding new triggers.) Instead, just force a relcache inval to
1355 * make other backends (and this one too!) rebuild their relcache entries.
1356 * There's no great harm in leaving relhastriggers true even if there are
1357 * no triggers left.
1358 */
1360
1361 /* Keep lock on trigger's rel until end of xact */
1362 table_close(rel, NoLock);
1363}
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 1469 of file trigger.c.

1470{
1471 Oid tgoid;
1474 HeapTuple tuple;
1476 ScanKeyData key[2];
1477 Oid relid;
1478 ObjectAddress address;
1479
1480 /*
1481 * Look up name, check permissions, and acquire lock (which we will NOT
1482 * release until end of transaction).
1483 */
1485 0,
1487 NULL);
1488
1489 /* Have lock already, so just need to build relcache entry. */
1490 targetrel = relation_open(relid, NoLock);
1491
1492 /*
1493 * On partitioned tables, this operation recurses to partitions. Lock all
1494 * tables upfront.
1495 */
1496 if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1498
1500
1501 /*
1502 * Search for the trigger to modify.
1503 */
1504 ScanKeyInit(&key[0],
1507 ObjectIdGetDatum(relid));
1508 ScanKeyInit(&key[1],
1511 PointerGetDatum(stmt->subname));
1513 NULL, 2, key);
1515 {
1517
1519 tgoid = trigform->oid;
1520
1521 /*
1522 * If the trigger descends from a trigger on a parent partitioned
1523 * table, reject the rename. We don't allow a trigger in a partition
1524 * to differ in name from that of its parent: that would lead to an
1525 * inconsistency that pg_dump would not reproduce.
1526 */
1527 if (OidIsValid(trigform->tgparentid))
1528 ereport(ERROR,
1530 errmsg("cannot rename trigger \"%s\" on table \"%s\"",
1532 errhint("Rename the trigger on the partitioned table \"%s\" instead.",
1533 get_rel_name(get_partition_parent(relid, false))));
1534
1535
1536 /* Rename the trigger on this relation ... */
1537 renametrig_internal(tgrel, targetrel, tuple, stmt->newname,
1538 stmt->subname);
1539
1540 /* ... and if it is partitioned, recurse to its partitions */
1541 if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1542 {
1544
1545 for (int i = 0; i < partdesc->nparts; i++)
1546 {
1547 Oid partitionId = partdesc->oids[i];
1548
1550 stmt->newname, stmt->subname);
1551 }
1552 }
1553 }
1554 else
1555 {
1556 ereport(ERROR,
1558 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1560 }
1561
1562 ObjectAddressSet(address, TriggerRelationId, tgoid);
1563
1565
1567
1568 /*
1569 * Close rel, but keep exclusive lock!
1570 */
1572
1573 return address;
1574}
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:1584
static void RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition trigger.c:1422
static void renametrig_partition(Relation tgrel, Oid partitionId, Oid parentTriggerOid, const char *newname, const char *expected_name)
Definition trigger.c:1655

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 1584 of file trigger.c.

1586{
1587 HeapTuple tuple;
1589 ScanKeyData key[2];
1591
1592 /* If the trigger already has the new name, nothing to do. */
1594 if (strcmp(NameStr(tgform->tgname), newname) == 0)
1595 return;
1596
1597 /*
1598 * Before actually trying the rename, search for triggers with the same
1599 * name. The update would fail with an ugly message in that case, and it
1600 * is better to throw a nicer error.
1601 */
1602 ScanKeyInit(&key[0],
1606 ScanKeyInit(&key[1],
1609 PointerGetDatum(newname));
1611 NULL, 2, key);
1613 ereport(ERROR,
1615 errmsg("trigger \"%s\" for relation \"%s\" already exists",
1618
1619 /*
1620 * The target name is free; update the existing pg_trigger tuple with it.
1621 */
1622 tuple = heap_copytuple(trigtup); /* need a modifiable copy */
1623 tgform = (Form_pg_trigger) GETSTRUCT(tuple);
1624
1625 /*
1626 * If the trigger has a name different from what we expected, let the user
1627 * know. (We can proceed anyway, since we must have reached here following
1628 * a tgparentid link.)
1629 */
1630 if (strcmp(NameStr(tgform->tgname), expected_name) != 0)
1632 errmsg("renamed trigger \"%s\" on relation \"%s\"",
1633 NameStr(tgform->tgname),
1635
1636 namestrcpy(&tgform->tgname, newname);
1637
1638 CatalogTupleUpdate(tgrel, &tuple->t_self, tuple);
1639
1641
1642 /*
1643 * Invalidate relation's relcache entry so that other backends (and this
1644 * one too!) are sent SI message to make them rebuild relcache entries.
1645 * (Ideally this should happen automatically...)
1646 */
1648}
#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 1655 of file trigger.c.

1657{
1660 HeapTuple tuple;
1661
1662 /*
1663 * Given a relation and the OID of a trigger on parent relation, find the
1664 * corresponding trigger in the child and rename that trigger to the given
1665 * name.
1666 */
1667 ScanKeyInit(&key,
1672 NULL, 1, &key);
1673 while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1674 {
1677
1678 if (tgform->tgparentid != parentTriggerOid)
1679 continue; /* not our trigger */
1680
1682
1683 /* Rename the trigger on this partition */
1685
1686 /* And if this relation is partitioned, recurse to its partitions */
1687 if (partitionRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1688 {
1690 true);
1691
1692 for (int i = 0; i < partdesc->nparts; i++)
1693 {
1694 Oid partoid = partdesc->oids[i];
1695
1696 renametrig_partition(tgrel, partoid, tgform->oid, newname,
1697 NameStr(tgform->tgname));
1698 }
1699 }
1701
1702 /* There should be at most one matching tuple */
1703 break;
1704 }
1706}

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 5791 of file trigger.c.

5793{
5794 if (state->numstates >= state->numalloc)
5795 {
5796 int newalloc = state->numalloc * 2;
5797
5798 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5801 offsetof(SetConstraintStateData, trigstates) +
5803 state->numalloc = newalloc;
5804 Assert(state->numstates < state->numalloc);
5805 }
5806
5807 state->trigstates[state->numstates].sct_tgoid = tgoid;
5808 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5809 state->numstates++;
5810
5811 return state;
5812}

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCopy()

static SetConstraintState SetConstraintStateCopy ( SetConstraintState  origstate)
static

Definition at line 5771 of file trigger.c.

5772{
5774
5776
5777 state->all_isset = origstate->all_isset;
5778 state->all_isdeferred = origstate->all_isdeferred;
5779 state->numstates = origstate->numstates;
5780 memcpy(state->trigstates, origstate->trigstates,
5781 origstate->numstates * sizeof(SetConstraintTriggerData));
5782
5783 return state;
5784}

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCreate()

static SetConstraintState SetConstraintStateCreate ( int  numalloc)
static

Definition at line 5746 of file trigger.c.

5747{
5749
5750 /* Behave sanely with numalloc == 0 */
5751 if (numalloc <= 0)
5752 numalloc = 1;
5753
5754 /*
5755 * We assume that zeroing will correctly initialize the state values.
5756 */
5759 offsetof(SetConstraintStateData, trigstates) +
5760 numalloc * sizeof(SetConstraintTriggerData));
5761
5762 state->numalloc = numalloc;
5763
5764 return state;
5765}

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

Referenced by AfterTriggerSetState(), and SetConstraintStateCopy().

◆ SetTriggerFlags()

static void SetTriggerFlags ( TriggerDesc trigdesc,
Trigger trigger 
)
static

Definition at line 2015 of file trigger.c.

2016{
2017 int16 tgtype = trigger->tgtype;
2018
2019 trigdesc->trig_insert_before_row |=
2022 trigdesc->trig_insert_after_row |=
2025 trigdesc->trig_insert_instead_row |=
2028 trigdesc->trig_insert_before_statement |=
2031 trigdesc->trig_insert_after_statement |=
2034 trigdesc->trig_update_before_row |=
2037 trigdesc->trig_update_after_row |=
2040 trigdesc->trig_update_instead_row |=
2043 trigdesc->trig_update_before_statement |=
2046 trigdesc->trig_update_after_statement |=
2049 trigdesc->trig_delete_before_row |=
2052 trigdesc->trig_delete_after_row |=
2055 trigdesc->trig_delete_instead_row |=
2058 trigdesc->trig_delete_before_statement |=
2061 trigdesc->trig_delete_after_statement |=
2064 /* there are no row-level truncate triggers */
2071
2072 trigdesc->trig_insert_new_table |=
2073 (TRIGGER_FOR_INSERT(tgtype) &&
2075 trigdesc->trig_update_old_table |=
2076 (TRIGGER_FOR_UPDATE(tgtype) &&
2078 trigdesc->trig_update_new_table |=
2079 (TRIGGER_FOR_UPDATE(tgtype) &&
2081 trigdesc->trig_delete_old_table |=
2082 (TRIGGER_FOR_DELETE(tgtype) &&
2084}
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 5640 of file trigger.c.

5647{
5648 TupleConversionMap *map;
5649
5650 /*
5651 * Nothing needs to be done if we don't have a tuplestore.
5652 */
5653 if (tuplestore == NULL)
5654 return;
5655
5658 else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5659 {
5661 TupleTableSlot *storeslot;
5662
5663 switch (event)
5664 {
5666 table = transition_capture->tcs_insert_private;
5667 break;
5669 table = transition_capture->tcs_update_private;
5670 break;
5672 table = transition_capture->tcs_delete_private;
5673 break;
5674 default:
5675 elog(ERROR, "invalid after-trigger event code: %d", event);
5676 table = NULL; /* keep compiler quiet */
5677 break;
5678 }
5679
5680 storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5681 execute_attr_map_slot(map->attrMap, slot, storeslot);
5682 tuplestore_puttupleslot(tuplestore, storeslot);
5683 }
5684 else
5685 tuplestore_puttupleslot(tuplestore, slot);
5686}

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 3484 of file trigger.c.

3488{
3489 /* Check replication-role-dependent enable state */
3491 {
3492 if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN ||
3493 trigger->tgenabled == TRIGGER_DISABLED)
3494 return false;
3495 }
3496 else /* ORIGIN or LOCAL role */
3497 {
3498 if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA ||
3499 trigger->tgenabled == TRIGGER_DISABLED)
3500 return false;
3501 }
3502
3503 /*
3504 * Check for column-specific trigger (only possible for UPDATE, and in
3505 * fact we *must* ignore tgattr for other event types)
3506 */
3507 if (trigger->tgnattr > 0 && TRIGGER_FIRED_BY_UPDATE(event))
3508 {
3509 int i;
3510 bool modified;
3511
3512 modified = false;
3513 for (i = 0; i < trigger->tgnattr; i++)
3514 {
3516 modifiedCols))
3517 {
3518 modified = true;
3519 break;
3520 }
3521 }
3522 if (!modified)
3523 return false;
3524 }
3525
3526 /* Check for WHEN clause */
3527 if (trigger->tgqual)
3528 {
3530 ExprContext *econtext;
3532 int i;
3533
3534 Assert(estate != NULL);
3535
3536 /*
3537 * trigger is an element of relinfo->ri_TrigDesc->triggers[]; find the
3538 * matching element of relinfo->ri_TrigWhenExprs[]
3539 */
3540 i = trigger - relinfo->ri_TrigDesc->triggers;
3541 predicate = &relinfo->ri_TrigWhenExprs[i];
3542
3543 /*
3544 * If first time through for this WHEN expression, build expression
3545 * nodetrees for it. Keep them in the per-query memory context so
3546 * they'll survive throughout the query.
3547 */
3548 if (*predicate == NULL)
3549 {
3550 Node *tgqual;
3551
3553 tgqual = stringToNode(trigger->tgqual);
3554 tgqual = expand_generated_columns_in_expr(tgqual, relinfo->ri_RelationDesc, PRS2_OLD_VARNO);
3555 tgqual = expand_generated_columns_in_expr(tgqual, relinfo->ri_RelationDesc, PRS2_NEW_VARNO);
3556 /* Change references to OLD and NEW to INNER_VAR and OUTER_VAR */
3559 /* ExecPrepareQual wants implicit-AND form */
3560 tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
3561 *predicate = ExecPrepareQual((List *) tgqual, estate);
3563 }
3564
3565 /*
3566 * We will use the EState's per-tuple context for evaluating WHEN
3567 * expressions (creating it if it's not already there).
3568 */
3569 econtext = GetPerTupleExprContext(estate);
3570
3571 /*
3572 * Finally evaluate the expression, making the old and/or new tuples
3573 * available as INNER_VAR/OUTER_VAR respectively.
3574 */
3575 econtext->ecxt_innertuple = oldslot;
3576 econtext->ecxt_outertuple = newslot;
3577 if (!ExecQual(*predicate, econtext))
3578 return false;
3579 }
3580
3581 return true;
3582}
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:510
ExprState * ExecPrepareQual(List *qual, EState *estate)
Definition execExpr.c:826
#define GetPerTupleExprContext(estate)
Definition executor.h:667
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition executor.h:529
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:746
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 1222 of file trigger.c.

1226{
1228 ScanKeyData skey[1];
1230 HeapTuple tuple,
1231 newtup;
1234
1235 /*
1236 * Find the trigger to delete.
1237 */
1238 ScanKeyInit(&skey[0],
1242
1244 NULL, 1, skey);
1245
1246 tuple = systable_getnext(tgscan);
1247 if (!HeapTupleIsValid(tuple))
1248 elog(ERROR, "could not find tuple for trigger %u", childTrigId);
1249 newtup = heap_copytuple(tuple);
1252 {
1253 /* don't allow setting parent for a constraint that already has one */
1254 if (OidIsValid(trigForm->tgparentid))
1255 elog(ERROR, "trigger %u already has a parent trigger",
1256 childTrigId);
1257
1258 trigForm->tgparentid = parentTrigId;
1259
1261
1263
1266
1269 }
1270 else
1271 {
1272 trigForm->tgparentid = InvalidOid;
1273
1275
1282 }
1283
1286}
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:353

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