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 * Recompute qs first: the loop above refreshes it after each
5244 * afterTriggerInvokeEvents() call (see comment there), but the "all
5245 * fired" break exits without doing so, leaving qs potentially stale here.
5246 */
5248 FireAfterTriggerBatchCallbacks(qs->batch_callbacks);
5249
5250 /* Release query-level-local storage, including tuplestores if any */
5252
5255}
5256
5257
5258/*
5259 * AfterTriggerFreeQuery
5260 * Release subsidiary storage for a trigger query level.
5261 * This includes closing down tuplestores.
5262 * Note: it's important for this to be safe if interrupted by an error
5263 * and then called again for the same query level.
5264 */
5265static void
5267{
5268 Tuplestorestate *ts;
5269 List *tables;
5270 ListCell *lc;
5271
5272 /* Drop the trigger events */
5273 afterTriggerFreeEventList(&qs->events);
5274
5275 /* Drop FDW tuplestore if any */
5276 ts = qs->fdw_tuplestore;
5277 qs->fdw_tuplestore = NULL;
5278 if (ts)
5279 tuplestore_end(ts);
5280
5281 /* Release per-table subsidiary storage */
5282 tables = qs->tables;
5283 foreach(lc, tables)
5284 {
5286
5287 ts = table->old_tuplestore;
5288 table->old_tuplestore = NULL;
5289 if (ts)
5290 tuplestore_end(ts);
5291 ts = table->new_tuplestore;
5292 table->new_tuplestore = NULL;
5293 if (ts)
5294 tuplestore_end(ts);
5295 if (table->storeslot)
5296 {
5297 TupleTableSlot *slot = table->storeslot;
5298
5299 table->storeslot = NULL;
5301 }
5302 }
5303
5304 /*
5305 * Now free the AfterTriggersTableData structs and list cells. Reset list
5306 * pointer first; if list_free_deep somehow gets an error, better to leak
5307 * that storage than have an infinite loop.
5308 */
5309 qs->tables = NIL;
5310 list_free_deep(tables);
5311
5312 list_free_deep(qs->batch_callbacks);
5313 qs->batch_callbacks = NIL;
5314}
5315
5316
5317/* ----------
5318 * AfterTriggerFireDeferred()
5319 *
5320 * Called just before the current transaction is committed. At this
5321 * time we invoke all pending DEFERRED triggers.
5322 *
5323 * It is possible for other modules to queue additional deferred triggers
5324 * during pre-commit processing; therefore xact.c may have to call this
5325 * multiple times.
5326 * ----------
5327 */
5328void
5330{
5331 AfterTriggerEventList *events;
5332 bool snap_pushed = false;
5333
5334 /* Must not be inside a query */
5336
5337 /*
5338 * If there are any triggers to fire, make sure we have set a snapshot for
5339 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5340 * can't assume ActiveSnapshot is valid on entry.)
5341 */
5342 events = &afterTriggers.events;
5343 if (events->head != NULL)
5344 {
5346 snap_pushed = true;
5347 }
5348
5349 /*
5350 * Run all the remaining triggers. Loop until they are all gone, in case
5351 * some trigger queues more for us to do.
5352 */
5354 while (afterTriggerMarkEvents(events, NULL, false))
5355 {
5357
5358 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5359 break; /* all fired */
5360 }
5361
5362 /* Flush any fast-path batches accumulated by the triggers just fired. */
5364
5366
5367 /*
5368 * We don't bother freeing the event list or batch_callbacks, since they
5369 * will go away anyway (and more efficiently than via pfree) in
5370 * AfterTriggerEndXact.
5371 */
5372
5373 if (snap_pushed)
5375}
5376
5377
5378/* ----------
5379 * AfterTriggerEndXact()
5380 *
5381 * The current transaction is finishing.
5382 *
5383 * Any unfired triggers are canceled so we simply throw
5384 * away anything we know.
5385 *
5386 * Note: it is possible for this to be called repeatedly in case of
5387 * error during transaction abort; therefore, do not complain if
5388 * already closed down.
5389 * ----------
5390 */
5391void
5393{
5394 /*
5395 * Forget the pending-events list.
5396 *
5397 * Since all the info is in TopTransactionContext or children thereof, we
5398 * don't really need to do anything to reclaim memory. However, the
5399 * pending-events list could be large, and so it's useful to discard it as
5400 * soon as possible --- especially if we are aborting because we ran out
5401 * of memory for the list!
5402 */
5404 {
5410 }
5411
5412 /*
5413 * Forget any subtransaction state as well. Since this can't be very
5414 * large, we let the eventual reset of TopTransactionContext free the
5415 * memory instead of doing it here.
5416 */
5419
5420
5421 /*
5422 * Forget the query stack and constraint-related state information. As
5423 * with the subtransaction state information, we don't bother freeing the
5424 * memory here.
5425 */
5429
5430 /* No more afterTriggers manipulation until next transaction starts. */
5432
5434
5438}
5439
5440/*
5441 * AfterTriggerBeginSubXact()
5442 *
5443 * Start a subtransaction.
5444 */
5445void
5447{
5448 int my_level = GetCurrentTransactionNestLevel();
5449
5450 /*
5451 * Allocate more space in the trans_stack if needed. (Note: because the
5452 * minimum nest level of a subtransaction is 2, we waste the first couple
5453 * entries of the array; not worth the notational effort to avoid it.)
5454 */
5455 while (my_level >= afterTriggers.maxtransdepth)
5456 {
5458 {
5459 /* Arbitrarily initialize for max of 8 subtransaction levels */
5462 8 * sizeof(AfterTriggersTransData));
5464 }
5465 else
5466 {
5467 /* repalloc will keep the stack in the same context */
5469
5474 }
5475 }
5476
5477 /*
5478 * Push the current information into the stack. The SET CONSTRAINTS state
5479 * is not saved until/unless changed. Likewise, we don't make a
5480 * per-subtransaction event context until needed.
5481 */
5482 afterTriggers.trans_stack[my_level].state = NULL;
5486}
5487
5488/*
5489 * AfterTriggerEndSubXact()
5490 *
5491 * The current subtransaction is ending.
5492 */
5493void
5495{
5496 int my_level = GetCurrentTransactionNestLevel();
5498 AfterTriggerEvent event;
5501
5502 /*
5503 * Pop the prior state if needed.
5504 */
5505 if (isCommit)
5506 {
5508 /* If we saved a prior state, we don't need it anymore */
5510 if (state != NULL)
5511 pfree(state);
5512 /* this avoids double pfree if error later: */
5513 afterTriggers.trans_stack[my_level].state = NULL;
5516 }
5517 else
5518 {
5519 /*
5520 * Aborting. It is possible subxact start failed before calling
5521 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5522 * trans_stack levels that aren't there.
5523 */
5524 if (my_level >= afterTriggers.maxtransdepth)
5525 return;
5526
5527 /*
5528 * Release query-level storage for queries being aborted, and restore
5529 * query_depth to its pre-subxact value. This assumes that a
5530 * subtransaction will not add events to query levels started in a
5531 * earlier transaction state.
5532 */
5534 {
5538 }
5541
5542 /*
5543 * Restore the global deferred-event list to its former length,
5544 * discarding any events queued by the subxact.
5545 */
5547 &afterTriggers.trans_stack[my_level].events);
5548
5549 /*
5550 * Restore the trigger state. If the saved state is NULL, then this
5551 * subxact didn't save it, so it doesn't need restoring.
5552 */
5554 if (state != NULL)
5555 {
5558 }
5559 /* this avoids double pfree if error later: */
5560 afterTriggers.trans_stack[my_level].state = NULL;
5561
5562 /*
5563 * Scan for any remaining deferred events that were marked DONE or IN
5564 * PROGRESS by this subxact or a child, and un-mark them. We can
5565 * recognize such events because they have a firing ID greater than or
5566 * equal to the firing_counter value we saved at subtransaction start.
5567 * (This essentially assumes that the current subxact includes all
5568 * subxacts started after it.)
5569 */
5572 {
5574
5575 if (event->ate_flags &
5577 {
5578 if (evtshared->ats_firing_id >= subxact_firing_id)
5579 event->ate_flags &=
5581 }
5582 }
5583 }
5584
5585 /* Reset in case a callback threw an error while firing. */
5587}
5588
5589/*
5590 * Get the transition table for the given event and depending on whether we are
5591 * processing the old or the new tuple.
5592 */
5593static Tuplestorestate *
5597 TransitionCaptureState *transition_capture)
5598{
5599 Tuplestorestate *tuplestore = NULL;
5600 bool delete_old_table = transition_capture->tcs_delete_old_table;
5601 bool update_old_table = transition_capture->tcs_update_old_table;
5602 bool update_new_table = transition_capture->tcs_update_new_table;
5603 bool insert_new_table = transition_capture->tcs_insert_new_table;
5604
5605 /*
5606 * For INSERT events NEW should be non-NULL, for DELETE events OLD should
5607 * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
5608 * non-NULL. But for UPDATE events fired for capturing transition tuples
5609 * during UPDATE partition-key row movement, OLD is NULL when the event is
5610 * for a row being inserted, whereas NEW is NULL when the event is for a
5611 * row being deleted.
5612 */
5614 TupIsNull(oldslot)));
5616 TupIsNull(newslot)));
5617
5618 if (!TupIsNull(oldslot))
5619 {
5621 if (event == TRIGGER_EVENT_DELETE && delete_old_table)
5622 tuplestore = transition_capture->tcs_delete_private->old_tuplestore;
5623 else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
5624 tuplestore = transition_capture->tcs_update_private->old_tuplestore;
5625 }
5626 else if (!TupIsNull(newslot))
5627 {
5629 if (event == TRIGGER_EVENT_INSERT && insert_new_table)
5630 tuplestore = transition_capture->tcs_insert_private->new_tuplestore;
5631 else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
5632 tuplestore = transition_capture->tcs_update_private->new_tuplestore;
5633 }
5634
5635 return tuplestore;
5636}
5637
5638/*
5639 * Add the given heap tuple to the given tuplestore, applying the conversion
5640 * map if necessary.
5641 *
5642 * If original_insert_tuple is given, we can add that tuple without conversion.
5643 */
5644static void
5646 int event,
5647 TransitionCaptureState *transition_capture,
5649 TupleTableSlot *slot,
5651 Tuplestorestate *tuplestore)
5652{
5653 TupleConversionMap *map;
5654
5655 /*
5656 * Nothing needs to be done if we don't have a tuplestore.
5657 */
5658 if (tuplestore == NULL)
5659 return;
5660
5663 else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5664 {
5666 TupleTableSlot *storeslot;
5667
5668 switch (event)
5669 {
5671 table = transition_capture->tcs_insert_private;
5672 break;
5674 table = transition_capture->tcs_update_private;
5675 break;
5677 table = transition_capture->tcs_delete_private;
5678 break;
5679 default:
5680 elog(ERROR, "invalid after-trigger event code: %d", event);
5681 table = NULL; /* keep compiler quiet */
5682 break;
5683 }
5684
5685 storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5686 execute_attr_map_slot(map->attrMap, slot, storeslot);
5687 tuplestore_puttupleslot(tuplestore, storeslot);
5688 }
5689 else
5690 tuplestore_puttupleslot(tuplestore, slot);
5691}
5692
5693/* ----------
5694 * AfterTriggerEnlargeQueryState()
5695 *
5696 * Prepare the necessary state so that we can record AFTER trigger events
5697 * queued by a query. It is allowed to have nested queries within a
5698 * (sub)transaction, so we need to have separate state for each query
5699 * nesting level.
5700 * ----------
5701 */
5702static void
5704{
5706
5708
5710 {
5711 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5712
5717 }
5718 else
5719 {
5720 /* repalloc will keep the stack in the same context */
5723 old_alloc * 2);
5724
5729 }
5730
5731 /* Initialize new array entries to empty */
5733 {
5735
5736 qs->events.head = NULL;
5737 qs->events.tail = NULL;
5738 qs->events.tailfree = NULL;
5739 qs->fdw_tuplestore = NULL;
5740 qs->tables = NIL;
5741 qs->batch_callbacks = NIL;
5742
5743 ++init_depth;
5744 }
5745}
5746
5747/*
5748 * Create an empty SetConstraintState with room for numalloc trigstates
5749 */
5750static SetConstraintState
5751SetConstraintStateCreate(int numalloc)
5752{
5754
5755 /* Behave sanely with numalloc == 0 */
5756 if (numalloc <= 0)
5757 numalloc = 1;
5758
5759 /*
5760 * We assume that zeroing will correctly initialize the state values.
5761 */
5764 offsetof(SetConstraintStateData, trigstates) +
5765 numalloc * sizeof(SetConstraintTriggerData));
5766
5767 state->numalloc = numalloc;
5768
5769 return state;
5770}
5771
5772/*
5773 * Copy a SetConstraintState
5774 */
5775static SetConstraintState
5777{
5779
5781
5782 state->all_isset = origstate->all_isset;
5783 state->all_isdeferred = origstate->all_isdeferred;
5784 state->numstates = origstate->numstates;
5785 memcpy(state->trigstates, origstate->trigstates,
5786 origstate->numstates * sizeof(SetConstraintTriggerData));
5787
5788 return state;
5789}
5790
5791/*
5792 * Add a per-trigger item to a SetConstraintState. Returns possibly-changed
5793 * pointer to the state object (it will change if we have to repalloc).
5794 */
5795static SetConstraintState
5797 Oid tgoid, bool tgisdeferred)
5798{
5799 if (state->numstates >= state->numalloc)
5800 {
5801 int newalloc = state->numalloc * 2;
5802
5803 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5806 offsetof(SetConstraintStateData, trigstates) +
5808 state->numalloc = newalloc;
5809 Assert(state->numstates < state->numalloc);
5810 }
5811
5812 state->trigstates[state->numstates].sct_tgoid = tgoid;
5813 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5814 state->numstates++;
5815
5816 return state;
5817}
5818
5819/* ----------
5820 * AfterTriggerSetState()
5821 *
5822 * Execute the SET CONSTRAINTS ... utility command.
5823 * ----------
5824 */
5825void
5827{
5828 int my_level = GetCurrentTransactionNestLevel();
5829
5830 /* If we haven't already done so, initialize our state. */
5831 if (afterTriggers.state == NULL)
5833
5834 /*
5835 * If in a subtransaction, and we didn't save the current state already,
5836 * save it so it can be restored if the subtransaction aborts.
5837 */
5838 if (my_level > 1 &&
5839 afterTriggers.trans_stack[my_level].state == NULL)
5840 {
5841 afterTriggers.trans_stack[my_level].state =
5843 }
5844
5845 /*
5846 * Handle SET CONSTRAINTS ALL ...
5847 */
5848 if (stmt->constraints == NIL)
5849 {
5850 /*
5851 * Forget any previous SET CONSTRAINTS commands in this transaction.
5852 */
5854
5855 /*
5856 * Set the per-transaction ALL state to known.
5857 */
5860 }
5861 else
5862 {
5865 List *conoidlist = NIL;
5866 List *tgoidlist = NIL;
5867 ListCell *lc;
5868
5869 /*
5870 * Handle SET CONSTRAINTS constraint-name [, ...]
5871 *
5872 * First, identify all the named constraints and make a list of their
5873 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5874 * the same name within a schema, the specifications are not
5875 * necessarily unique. Our strategy is to target all matching
5876 * constraints within the first search-path schema that has any
5877 * matches, but disregard matches in schemas beyond the first match.
5878 * (This is a bit odd but it's the historical behavior.)
5879 *
5880 * A constraint in a partitioned table may have corresponding
5881 * constraints in the partitions. Grab those too.
5882 */
5884
5885 foreach(lc, stmt->constraints)
5886 {
5887 RangeVar *constraint = lfirst(lc);
5888 bool found;
5890 ListCell *nslc;
5891
5892 if (constraint->catalogname)
5893 {
5894 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5895 ereport(ERROR,
5897 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5898 constraint->catalogname, constraint->schemaname,
5899 constraint->relname)));
5900 }
5901
5902 /*
5903 * If we're given the schema name with the constraint, look only
5904 * in that schema. If given a bare constraint name, use the
5905 * search path to find the first matching constraint.
5906 */
5907 if (constraint->schemaname)
5908 {
5910 false);
5911
5913 }
5914 else
5915 {
5917 }
5918
5919 found = false;
5920 foreach(nslc, namespacelist)
5921 {
5924 ScanKeyData skey[2];
5925 HeapTuple tup;
5926
5927 ScanKeyInit(&skey[0],
5930 CStringGetDatum(constraint->relname));
5931 ScanKeyInit(&skey[1],
5935
5937 true, NULL, 2, skey);
5938
5940 {
5942
5943 if (con->condeferrable)
5944 conoidlist = lappend_oid(conoidlist, con->oid);
5945 else if (stmt->deferred)
5946 ereport(ERROR,
5948 errmsg("constraint \"%s\" is not deferrable",
5949 constraint->relname)));
5950 found = true;
5951 }
5952
5954
5955 /*
5956 * Once we've found a matching constraint we do not search
5957 * later parts of the search path.
5958 */
5959 if (found)
5960 break;
5961 }
5962
5964
5965 /*
5966 * Not found ?
5967 */
5968 if (!found)
5969 ereport(ERROR,
5971 errmsg("constraint \"%s\" does not exist",
5972 constraint->relname)));
5973 }
5974
5975 /*
5976 * Scan for any possible descendants of the constraints. We append
5977 * whatever we find to the same list that we're scanning; this has the
5978 * effect that we create new scans for those, too, so if there are
5979 * further descendents, we'll also catch them.
5980 */
5981 foreach(lc, conoidlist)
5982 {
5983 Oid parent = lfirst_oid(lc);
5985 SysScanDesc scan;
5986 HeapTuple tuple;
5987
5988 ScanKeyInit(&key,
5991 ObjectIdGetDatum(parent));
5992
5993 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5994
5995 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5996 {
5998
5999 conoidlist = lappend_oid(conoidlist, con->oid);
6000 }
6001
6002 systable_endscan(scan);
6003 }
6004
6006
6007 /*
6008 * Now, locate the trigger(s) implementing each of these constraints,
6009 * and make a list of their OIDs.
6010 */
6012
6013 foreach(lc, conoidlist)
6014 {
6015 Oid conoid = lfirst_oid(lc);
6018 HeapTuple htup;
6019
6023 ObjectIdGetDatum(conoid));
6024
6026 NULL, 1, &skey);
6027
6029 {
6031
6032 /*
6033 * Silently skip triggers that are marked as non-deferrable in
6034 * pg_trigger. This is not an error condition, since a
6035 * deferrable RI constraint may have some non-deferrable
6036 * actions.
6037 */
6038 if (pg_trigger->tgdeferrable)
6040 }
6041
6043 }
6044
6046
6047 /*
6048 * Now we can set the trigger states of individual triggers for this
6049 * xact.
6050 */
6051 foreach(lc, tgoidlist)
6052 {
6053 Oid tgoid = lfirst_oid(lc);
6055 bool found = false;
6056 int i;
6057
6058 for (i = 0; i < state->numstates; i++)
6059 {
6060 if (state->trigstates[i].sct_tgoid == tgoid)
6061 {
6062 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
6063 found = true;
6064 break;
6065 }
6066 }
6067 if (!found)
6068 {
6070 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
6071 }
6072 }
6073 }
6074
6075 /*
6076 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
6077 * checks against that constraint must be made when the SET CONSTRAINTS
6078 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
6079 * apply retroactively. We've updated the constraints state, so scan the
6080 * list of previously deferred events to fire any that have now become
6081 * immediate.
6082 *
6083 * Obviously, if this was SET ... DEFERRED then it can't have converted
6084 * any unfired events to immediate, so we need do nothing in that case.
6085 */
6086 if (!stmt->deferred)
6087 {
6089 bool snapshot_set = false;
6090
6092 while (afterTriggerMarkEvents(events, NULL, true))
6093 {
6095
6096 /*
6097 * Make sure a snapshot has been established in case trigger
6098 * functions need one. Note that we avoid setting a snapshot if
6099 * we don't find at least one trigger that has to be fired now.
6100 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6101 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6102 * at the start of a transaction it's not possible for any trigger
6103 * events to be queued yet.)
6104 */
6105 if (!snapshot_set)
6106 {
6108 snapshot_set = true;
6109 }
6110
6111 /*
6112 * We can delete fired events if we are at top transaction level,
6113 * but we'd better not if inside a subtransaction, since the
6114 * subtransaction could later get rolled back.
6115 */
6117 !IsSubTransaction()))
6118 break; /* all fired */
6119 }
6120
6121 /*
6122 * Flush any fast-path batches accumulated by the triggers just fired.
6123 */
6128
6129 if (snapshot_set)
6131 }
6132}
6133
6134/* ----------
6135 * AfterTriggerPendingOnRel()
6136 * Test to see if there are any pending after-trigger events for rel.
6137 *
6138 * This is used by TRUNCATE, CLUSTER, ALTER TABLE, etc to detect whether
6139 * it is unsafe to perform major surgery on a relation. Note that only
6140 * local pending events are examined. We assume that having exclusive lock
6141 * on a rel guarantees there are no unserviced events in other backends ---
6142 * but having a lock does not prevent there being such events in our own.
6143 *
6144 * In some scenarios it'd be reasonable to remove pending events (more
6145 * specifically, mark them DONE by the current subxact) but without a lot
6146 * of knowledge of the trigger semantics we can't do this in general.
6147 * ----------
6148 */
6149bool
6151{
6152 AfterTriggerEvent event;
6154 int depth;
6155
6156 /* Scan queued events */
6158 {
6160
6161 /*
6162 * We can ignore completed events. (Even if a DONE flag is rolled
6163 * back by subxact abort, it's OK because the effects of the TRUNCATE
6164 * or whatever must get rolled back too.)
6165 */
6166 if (event->ate_flags & AFTER_TRIGGER_DONE)
6167 continue;
6168
6169 if (evtshared->ats_relid == relid)
6170 return true;
6171 }
6172
6173 /*
6174 * Also scan events queued by incomplete queries. This could only matter
6175 * if TRUNCATE/etc is executed by a function or trigger within an updating
6176 * query on the same relation, which is pretty perverse, but let's check.
6177 */
6178 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6179 {
6181 {
6183
6184 if (event->ate_flags & AFTER_TRIGGER_DONE)
6185 continue;
6186
6187 if (evtshared->ats_relid == relid)
6188 return true;
6189 }
6190 }
6191
6192 return false;
6193}
6194
6195/* ----------
6196 * AfterTriggerSaveEvent()
6197 *
6198 * Called by ExecA[RS]...Triggers() to queue up the triggers that should
6199 * be fired for an event.
6200 *
6201 * NOTE: this is called whenever there are any triggers associated with
6202 * the event (even if they are disabled). This function decides which
6203 * triggers actually need to be queued. It is also called after each row,
6204 * even if there are no triggers for that event, if there are any AFTER
6205 * STATEMENT triggers for the statement which use transition tables, so that
6206 * the transition tuplestores can be built. Furthermore, if the transition
6207 * capture is happening for UPDATEd rows being moved to another partition due
6208 * to the partition-key being changed, then this function is called once when
6209 * the row is deleted (to capture OLD row), and once when the row is inserted
6210 * into another partition (to capture NEW row). This is done separately because
6211 * DELETE and INSERT happen on different tables.
6212 *
6213 * Transition tuplestores are built now, rather than when events are pulled
6214 * off of the queue because AFTER ROW triggers are allowed to select from the
6215 * transition tables for the statement.
6216 *
6217 * This contains special support to queue the update events for the case where
6218 * a partitioned table undergoing a cross-partition update may have foreign
6219 * keys pointing into it. Normally, a partitioned table's row triggers are
6220 * not fired because the leaf partition(s) which are modified as a result of
6221 * the operation on the partitioned table contain the same triggers which are
6222 * fired instead. But that general scheme can cause problematic behavior with
6223 * foreign key triggers during cross-partition updates, which are implemented
6224 * as DELETE on the source partition followed by INSERT into the destination
6225 * partition. Specifically, firing DELETE triggers would lead to the wrong
6226 * foreign key action to be enforced considering that the original command is
6227 * UPDATE; in this case, this function is called with relinfo as the
6228 * partitioned table, and src_partinfo and dst_partinfo referring to the
6229 * source and target leaf partitions, respectively.
6230 *
6231 * is_crosspart_update is true either when a DELETE event is fired on the
6232 * source partition (which is to be ignored) or an UPDATE event is fired on
6233 * the root partitioned table.
6234 * ----------
6235 */
6236static void
6240 int event, bool row_trigger,
6243 TransitionCaptureState *transition_capture,
6245{
6246 Relation rel = relinfo->ri_RelationDesc;
6247 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
6250 char relkind = rel->rd_rel->relkind;
6251 int tgtype_event;
6252 int tgtype_level;
6253 int i;
6254 Tuplestorestate *fdw_tuplestore = NULL;
6255
6256 /*
6257 * Check state. We use a normal test not Assert because it is possible to
6258 * reach here in the wrong state given misconfigured RI triggers, in
6259 * particular deferring a cascade action trigger.
6260 */
6261 if (afterTriggers.query_depth < 0)
6262 elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
6263
6264 /* Be sure we have enough space to record events at this query depth. */
6267
6268 /*
6269 * If the directly named relation has any triggers with transition tables,
6270 * then we need to capture transition tuples.
6271 */
6272 if (row_trigger && transition_capture != NULL)
6273 {
6275
6276 /*
6277 * Capture the old tuple in the appropriate transition table based on
6278 * the event.
6279 */
6280 if (!TupIsNull(oldslot))
6281 {
6282 Tuplestorestate *old_tuplestore;
6283
6284 old_tuplestore = GetAfterTriggersTransitionTable(event,
6285 oldslot,
6286 NULL,
6287 transition_capture);
6288 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6289 oldslot, NULL, old_tuplestore);
6290 }
6291
6292 /*
6293 * Capture the new tuple in the appropriate transition table based on
6294 * the event.
6295 */
6296 if (!TupIsNull(newslot))
6297 {
6298 Tuplestorestate *new_tuplestore;
6299
6300 new_tuplestore = GetAfterTriggersTransitionTable(event,
6301 NULL,
6302 newslot,
6303 transition_capture);
6304 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6305 newslot, original_insert_tuple, new_tuplestore);
6306 }
6307
6308 /*
6309 * If transition tables are the only reason we're here, return. As
6310 * mentioned above, we can also be here during update tuple routing in
6311 * presence of transition tables, in which case this function is
6312 * called separately for OLD and NEW, so we expect exactly one of them
6313 * to be NULL.
6314 */
6315 if (trigdesc == NULL ||
6316 (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
6317 (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
6318 (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
6320 return;
6321 }
6322
6323 /*
6324 * We normally don't see partitioned tables here for row level triggers
6325 * except in the special case of a cross-partition update. In that case,
6326 * nodeModifyTable.c:ExecCrossPartitionUpdateForeignKey() calls here to
6327 * queue an update event on the root target partitioned table, also
6328 * passing the source and destination partitions and their tuples.
6329 */
6331 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
6333 TRIGGER_FIRED_BY_UPDATE(event) &&
6335
6336 /*
6337 * Validate the event code and collect the associated tuple CTIDs.
6338 *
6339 * The event code will be used both as a bitmask and an array offset, so
6340 * validation is important to make sure we don't walk off the edge of our
6341 * arrays.
6342 *
6343 * Also, if we're considering statement-level triggers, check whether we
6344 * already queued a set of them for this event, and cancel the prior set
6345 * if so. This preserves the behavior that statement-level triggers fire
6346 * just once per statement and fire after row-level triggers.
6347 */
6348 switch (event)
6349 {
6352 if (row_trigger)
6353 {
6354 Assert(oldslot == NULL);
6355 Assert(newslot != NULL);
6356 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
6357 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6358 }
6359 else
6360 {
6361 Assert(oldslot == NULL);
6362 Assert(newslot == NULL);
6363 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6364 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6366 CMD_INSERT, event);
6367 }
6368 break;
6371 if (row_trigger)
6372 {
6373 Assert(oldslot != NULL);
6374 Assert(newslot == NULL);
6375 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6376 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6377 }
6378 else
6379 {
6380 Assert(oldslot == NULL);
6381 Assert(newslot == NULL);
6382 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6383 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6385 CMD_DELETE, event);
6386 }
6387 break;
6390 if (row_trigger)
6391 {
6392 Assert(oldslot != NULL);
6393 Assert(newslot != NULL);
6394 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6395 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
6396
6397 /*
6398 * Also remember the OIDs of partitions to fetch these tuples
6399 * out of later in AfterTriggerExecute().
6400 */
6401 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6402 {
6404 new_event.ate_src_part =
6405 RelationGetRelid(src_partinfo->ri_RelationDesc);
6406 new_event.ate_dst_part =
6407 RelationGetRelid(dst_partinfo->ri_RelationDesc);
6408 }
6409 }
6410 else
6411 {
6412 Assert(oldslot == NULL);
6413 Assert(newslot == NULL);
6414 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6415 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6417 CMD_UPDATE, event);
6418 }
6419 break;
6422 Assert(oldslot == NULL);
6423 Assert(newslot == NULL);
6424 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6425 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6426 break;
6427 default:
6428 elog(ERROR, "invalid after-trigger event code: %d", event);
6429 tgtype_event = 0; /* keep compiler quiet */
6430 break;
6431 }
6432
6433 /* Determine flags */
6434 if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
6435 {
6436 if (row_trigger && event == TRIGGER_EVENT_UPDATE)
6437 {
6438 if (relkind == RELKIND_PARTITIONED_TABLE)
6440 else
6441 new_event.ate_flags = AFTER_TRIGGER_2CTID;
6442 }
6443 else
6444 new_event.ate_flags = AFTER_TRIGGER_1CTID;
6445 }
6446
6447 /* else, we'll initialize ate_flags for each trigger */
6448
6450
6451 /*
6452 * Must convert/copy the source and destination partition tuples into the
6453 * root partitioned table's format/slot, because the processing in the
6454 * loop below expects both oldslot and newslot tuples to be in that form.
6455 */
6456 if (row_trigger && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6457 {
6459 TupleConversionMap *map;
6460
6463 if (map)
6465 oldslot,
6466 rootslot);
6467 else
6469
6472 if (map)
6474 newslot,
6475 rootslot);
6476 else
6478 }
6479
6480 for (i = 0; i < trigdesc->numtriggers; i++)
6481 {
6482 Trigger *trigger = &trigdesc->triggers[i];
6483
6484 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
6487 tgtype_event))
6488 continue;
6489 if (!TriggerEnabled(estate, relinfo, trigger, event,
6491 continue;
6492
6493 if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
6494 {
6495 if (fdw_tuplestore == NULL)
6496 {
6497 fdw_tuplestore = GetCurrentFDWTuplestore();
6499 }
6500 else
6501 /* subsequent event for the same tuple */
6503 }
6504
6505 /*
6506 * If the trigger is a foreign key enforcement trigger, there are
6507 * certain cases where we can skip queueing the event because we can
6508 * tell by inspection that the FK constraint will still pass. There
6509 * are also some cases during cross-partition updates of a partitioned
6510 * table where queuing the event can be skipped.
6511 */
6513 {
6514 switch (RI_FKey_trigger_type(trigger->tgfoid))
6515 {
6516 case RI_TRIGGER_PK:
6517
6518 /*
6519 * For cross-partitioned updates of partitioned PK table,
6520 * skip the event fired by the component delete on the
6521 * source leaf partition unless the constraint originates
6522 * in the partition itself (!tgisclone), because the
6523 * update event that will be fired on the root
6524 * (partitioned) target table will be used to perform the
6525 * necessary foreign key enforcement action.
6526 */
6527 if (is_crosspart_update &&
6528 TRIGGER_FIRED_BY_DELETE(event) &&
6529 trigger->tgisclone)
6530 continue;
6531
6532 /* Update or delete on trigger's PK table */
6534 oldslot, newslot))
6535 {
6536 /* skip queuing this event */
6537 continue;
6538 }
6539 break;
6540
6541 case RI_TRIGGER_FK:
6542
6543 /*
6544 * Update on trigger's FK table. We can skip the update
6545 * event fired on a partitioned table during a
6546 * cross-partition of that table, because the insert event
6547 * that is fired on the destination leaf partition would
6548 * suffice to perform the necessary foreign key check.
6549 * Moreover, RI_FKey_fk_upd_check_required() expects to be
6550 * passed a tuple that contains system attributes, most of
6551 * which are not present in the virtual slot belonging to
6552 * a partitioned table.
6553 */
6554 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
6556 oldslot, newslot))
6557 {
6558 /* skip queuing this event */
6559 continue;
6560 }
6561 break;
6562
6563 case RI_TRIGGER_NONE:
6564
6565 /*
6566 * Not an FK trigger. No need to queue the update event
6567 * fired during a cross-partitioned update of a
6568 * partitioned table, because the same row trigger must be
6569 * present in the leaf partition(s) that are affected as
6570 * part of this update and the events fired on them are
6571 * queued instead.
6572 */
6573 if (row_trigger &&
6574 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6575 continue;
6576 break;
6577 }
6578 }
6579
6580 /*
6581 * If the trigger is a deferred unique constraint check trigger, only
6582 * queue it if the unique constraint was potentially violated, which
6583 * we know from index insertion time.
6584 */
6585 if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
6586 {
6587 if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
6588 continue; /* Uniqueness definitely not violated */
6589 }
6590
6591 /*
6592 * Fill in event structure and add it to the current query's queue.
6593 * Note we set ats_table to NULL whenever this trigger doesn't use
6594 * transition tables, to improve sharability of the shared event data.
6595 */
6596 new_shared.ats_event =
6597 (event & TRIGGER_EVENT_OPMASK) |
6599 (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
6600 (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
6601 new_shared.ats_tgoid = trigger->tgoid;
6602 new_shared.ats_relid = RelationGetRelid(rel);
6603 new_shared.ats_rolid = GetUserId();
6604 new_shared.ats_firing_id = 0;
6605 if ((trigger->tgoldtable || trigger->tgnewtable) &&
6606 transition_capture != NULL)
6607 {
6608 switch (event)
6609 {
6611 new_shared.ats_table = transition_capture->tcs_insert_private;
6612 break;
6614 new_shared.ats_table = transition_capture->tcs_update_private;
6615 break;
6617 new_shared.ats_table = transition_capture->tcs_delete_private;
6618 break;
6619 default:
6620 /* Must be TRUNCATE, see switch above */
6621 new_shared.ats_table = NULL;
6622 break;
6623 }
6624 }
6625 else
6626 new_shared.ats_table = NULL;
6627 new_shared.ats_modifiedcols = modifiedCols;
6628
6631 }
6632
6633 /*
6634 * Finally, spool any foreign tuple(s). The tuplestore squashes them to
6635 * minimal tuples, so this loses any system columns. The executor lost
6636 * those columns before us, for an unrelated reason, so this is fine.
6637 */
6638 if (fdw_tuplestore)
6639 {
6640 if (oldslot != NULL)
6641 tuplestore_puttupleslot(fdw_tuplestore, oldslot);
6642 if (newslot != NULL)
6643 tuplestore_puttupleslot(fdw_tuplestore, newslot);
6644 }
6645}
6646
6647/*
6648 * Detect whether we already queued BEFORE STATEMENT triggers for the given
6649 * relation + operation, and set the flag so the next call will report "true".
6650 */
6651static bool
6653{
6654 bool result;
6656
6657 /* Check state, like AfterTriggerSaveEvent. */
6658 if (afterTriggers.query_depth < 0)
6659 elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6660
6661 /* Be sure we have enough space to record events at this query depth. */
6664
6665 /*
6666 * We keep this state in the AfterTriggersTableData that also holds
6667 * transition tables for the relation + operation. In this way, if we are
6668 * forced to make a new set of transition tables because more tuples get
6669 * entered after we've already fired triggers, we will allow a new set of
6670 * statement triggers to get queued.
6671 */
6672 table = GetAfterTriggersTableData(relid, cmdType);
6673 result = table->before_trig_done;
6674 table->before_trig_done = true;
6675 return result;
6676}
6677
6678/*
6679 * If we previously queued a set of AFTER STATEMENT triggers for the given
6680 * relation + operation, and they've not been fired yet, cancel them. The
6681 * caller will queue a fresh set that's after any row-level triggers that may
6682 * have been queued by the current sub-statement, preserving (as much as
6683 * possible) the property that AFTER ROW triggers fire before AFTER STATEMENT
6684 * triggers, and that the latter only fire once. This deals with the
6685 * situation where several FK enforcement triggers sequentially queue triggers
6686 * for the same table into the same trigger query level. We can't fully
6687 * prevent odd behavior though: if there are AFTER ROW triggers taking
6688 * transition tables, we don't want to change the transition tables once the
6689 * first such trigger has seen them. In such a case, any additional events
6690 * will result in creating new transition tables and allowing new firings of
6691 * statement triggers.
6692 *
6693 * This also saves the current event list location so that a later invocation
6694 * of this function can cheaply find the triggers we're about to queue and
6695 * cancel them.
6696 */
6697static void
6698cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
6699{
6702
6703 /*
6704 * We keep this state in the AfterTriggersTableData that also holds
6705 * transition tables for the relation + operation. In this way, if we are
6706 * forced to make a new set of transition tables because more tuples get
6707 * entered after we've already fired triggers, we will allow a new set of
6708 * statement triggers to get queued without canceling the old ones.
6709 */
6710 table = GetAfterTriggersTableData(relid, cmdType);
6711
6712 if (table->after_trig_done)
6713 {
6714 /*
6715 * We want to start scanning from the tail location that existed just
6716 * before we inserted any statement triggers. But the events list
6717 * might've been entirely empty then, in which case scan from the
6718 * current head.
6719 */
6720 AfterTriggerEvent event;
6722
6723 if (table->after_trig_events.tail)
6724 {
6725 chunk = table->after_trig_events.tail;
6726 event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6727 }
6728 else
6729 {
6730 chunk = qs->events.head;
6731 event = NULL;
6732 }
6733
6734 for_each_chunk_from(chunk)
6735 {
6736 if (event == NULL)
6737 event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6738 for_each_event_from(event, chunk)
6739 {
6741
6742 /*
6743 * Exit loop when we reach events that aren't AS triggers for
6744 * the target relation.
6745 */
6746 if (evtshared->ats_relid != relid)
6747 goto done;
6748 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6749 goto done;
6750 if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6751 goto done;
6752 if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6753 goto done;
6754 /* OK, mark it DONE */
6755 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6756 event->ate_flags |= AFTER_TRIGGER_DONE;
6757 }
6758 /* signal we must reinitialize event ptr for next chunk */
6759 event = NULL;
6760 }
6761 }
6762done:
6763
6764 /* In any case, save current insertion point for next time */
6765 table->after_trig_done = true;
6766 table->after_trig_events = qs->events;
6767}
6768
6769/*
6770 * GUC assign_hook for session_replication_role
6771 */
6772void
6774{
6775 /*
6776 * Must flush the plan cache when changing replication role; but don't
6777 * flush unnecessarily.
6778 */
6781}
6782
6783/*
6784 * SQL function pg_trigger_depth()
6785 */
6786Datum
6788{
6790}
6791
6792/*
6793 * Check whether a trigger modified a virtual generated column and replace the
6794 * value with null if so.
6795 *
6796 * We need to check this so that we don't end up storing a non-null value in a
6797 * virtual generated column.
6798 *
6799 * We don't need to check for stored generated columns, since those will be
6800 * overwritten later anyway.
6801 */
6802static HeapTuple
6804{
6805 if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
6806 return tuple;
6807
6808 for (int i = 0; i < tupdesc->natts; i++)
6809 {
6810 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6811 {
6812 if (!heap_attisnull(tuple, i + 1, tupdesc))
6813 {
6814 int replCol = i + 1;
6815 Datum replValue = 0;
6816 bool replIsnull = true;
6817
6818 tuple = heap_modify_tuple_by_cols(tuple, tupdesc, 1, &replCol, &replValue, &replIsnull);
6819 }
6820 }
6821 }
6822
6823 return tuple;
6824}
6825
6826/*
6827 * RegisterAfterTriggerBatchCallback
6828 * Register a function to be called when the current trigger-firing
6829 * batch completes.
6830 *
6831 * Must be called from within a trigger function's execution context
6832 * (i.e., while afterTriggers state is active).
6833 *
6834 * The callback list is cleared after invocation, so the caller must
6835 * re-register for each new batch if needed.
6836 */
6837void
6839 void *arg)
6840{
6843
6844 /*
6845 * Allocate in TopTransactionContext so the item survives for the duration
6846 * of the batch, which may span multiple trigger invocations.
6847 *
6848 * Must be called while afterTriggers is active; callbacks registered
6849 * outside a trigger-firing context would never fire.
6850 */
6854 item = palloc(sizeof(AfterTriggerCallbackItem));
6855 item->callback = callback;
6856 item->arg = arg;
6857 if (afterTriggers.query_depth >= 0)
6858 {
6861
6862 qs->batch_callbacks = lappend(qs->batch_callbacks, item);
6863 }
6864 else
6868}
6869
6870/*
6871 * FireAfterTriggerBatchCallbacks
6872 * Invoke all callbacks in the given list.
6873 *
6874 * Memory cleanup of the list and its items is handled by the caller
6875 * (AfterTriggerFreeQuery for query-level callbacks, AfterTriggerEndXact
6876 * for top-level deferred callbacks).
6877 */
6878static void
6880{
6881 ListCell *lc;
6882
6885 foreach(lc, callbacks)
6886 {
6888
6889 item->callback(item->arg);
6890 }
6892}
6893
6894/*
6895 * AfterTriggerIsActive
6896 * Returns true if we're inside the after-trigger framework where
6897 * registered batch callbacks will actually be invoked.
6898 *
6899 * This is false during validateForeignKeyConstraint(), which calls
6900 * RI trigger functions directly outside the after-trigger framework.
6901 */
6902bool
6904{
6905 return afterTriggers.firing_depth > 0;
6906}
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:1323
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid, ResultRelInfo *rootRelInfo)
Definition execMain.c: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:90
#define PG_RETURN_INT32(x)
Definition fmgr.h:355
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
void systable_endscan(SysScanDesc sysscan)
Definition genam.c: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:1323
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1235
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1269
MemoryContext TopTransactionContext
Definition mcxt.c:172
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1635
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc(Size size)
Definition mcxt.c:1390
MemoryContext CurTransactionContext
Definition mcxt.c:173
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define SECURITY_LOCAL_USERID_CHANGE
Definition miscadmin.h: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:138
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
#define list_make1_oid(x1)
Definition pg_list.h:274
#define lfirst_oid(lc)
Definition pg_list.h:174
static const struct lconv_member_info table[]
END_CATALOG_STRUCT typedef FormData_pg_trigger * Form_pg_trigger
Definition pg_trigger.h:84
void ResetPlanCache(void)
Definition plancache.c:2328
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
unsigned int Oid
static int fb(int x)
#define RelationHasReferenceCountZero(relation)
Definition rel.h:500
#define RelationGetRelid(relation)
Definition rel.h:516
ResourceOwner CurrentResourceOwner
Definition resowner.c:173
ResourceOwner CurTransactionResourceOwner
Definition resowner.c:174
bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
int RI_FKey_trigger_type(Oid tgfoid)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition snapmgr.c:682
void PopActiveSnapshot(void)
Definition snapmgr.c:775
#define SnapshotAny
Definition snapmgr.h:33
#define BTEqualStrategyNumber
Definition stratnum.h:31
AfterTriggerBatchCallback callback
Definition trigger.c: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:5776
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition trigger.c:6698
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:5796
#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:6879
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:5494
void RegisterAfterTriggerBatchCallback(AfterTriggerBatchCallback callback, void *arg)
Definition trigger.c:6838
#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:6903
Datum pg_trigger_depth(PG_FUNCTION_ARGS)
Definition trigger.c:6787
#define MAX_CHUNK_SIZE
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition trigger.c:5266
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:5594
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:5826
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:6652
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:5751
void AfterTriggerFireDeferred(void)
Definition trigger.c:5329
static void TransitionTableAddTuple(EState *estate, int event, TransitionCaptureState *transition_capture, ResultRelInfo *relinfo, TupleTableSlot *slot, TupleTableSlot *original_insert_tuple, Tuplestorestate *tuplestore)
Definition trigger.c:5645
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:6773
void AfterTriggerEndXact(bool isCommit)
Definition trigger.c:5392
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6150
#define AFTER_TRIGGER_FDW_REUSE
Definition trigger.c:3687
void AfterTriggerBeginSubXact(void)
Definition trigger.c:5446
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:6237
static HeapTuple check_modified_virtual_generated(TupleDesc tupdesc, HeapTuple tuple)
Definition trigger.c:6803
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:5703
#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:544
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 5446 of file trigger.c.

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

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 * Recompute qs first: the loop above refreshes it after each
5244 * afterTriggerInvokeEvents() call (see comment there), but the "all
5245 * fired" break exits without doing so, leaving qs potentially stale here.
5246 */
5248 FireAfterTriggerBatchCallbacks(qs->batch_callbacks);
5249
5250 /* Release query-level-local storage, including tuplestores if any */
5252
5255}

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

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

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

5393{
5394 /*
5395 * Forget the pending-events list.
5396 *
5397 * Since all the info is in TopTransactionContext or children thereof, we
5398 * don't really need to do anything to reclaim memory. However, the
5399 * pending-events list could be large, and so it's useful to discard it as
5400 * soon as possible --- especially if we are aborting because we ran out
5401 * of memory for the list!
5402 */
5404 {
5410 }
5411
5412 /*
5413 * Forget any subtransaction state as well. Since this can't be very
5414 * large, we let the eventual reset of TopTransactionContext free the
5415 * memory instead of doing it here.
5416 */
5419
5420
5421 /*
5422 * Forget the query stack and constraint-related state information. As
5423 * with the subtransaction state information, we don't bother freeing the
5424 * memory here.
5425 */
5429
5430 /* No more afterTriggers manipulation until next transaction starts. */
5432
5434
5438}

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

5704{
5706
5708
5710 {
5711 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5712
5717 }
5718 else
5719 {
5720 /* repalloc will keep the stack in the same context */
5723 old_alloc * 2);
5724
5729 }
5730
5731 /* Initialize new array entries to empty */
5733 {
5735
5736 qs->events.head = NULL;
5737 qs->events.tail = NULL;
5738 qs->events.tailfree = NULL;
5739 qs->fdw_tuplestore = NULL;
5740 qs->tables = NIL;
5741 qs->batch_callbacks = NIL;
5742
5743 ++init_depth;
5744 }
5745}

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

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

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

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

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

6904{
6905 return afterTriggers.firing_depth > 0;
6906}

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

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

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

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

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

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

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

6774{
6775 /*
6776 * Must flush the plan cache when changing replication role; but don't
6777 * flush unnecessarily.
6778 */
6781}

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6652 of file trigger.c.

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

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

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

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

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

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:89
int j
Definition isn.c:78
char * pstrdup(const char *in)
Definition mcxt.c:1910

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

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

◆ CreateTrigger()

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

Definition at line 162 of file trigger.c.

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

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

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

◆ CreateTriggerFiringOn()

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

Definition at line 179 of file trigger.c.

184{
185 int16 tgtype;
186 int ncolumns;
187 int16 *columns;
188 int2vector *tgattr;
190 char *qual;
192 bool nulls[Natts_pg_trigger];
193 Relation rel;
197 HeapTuple tuple = NULL;
201 char *trigname;
205 char *oldtablename = NULL;
206 char *newtablename = NULL;
208 bool trigger_exists = false;
210 bool existing_isInternal = false;
211 bool existing_isClone = false;
212
213 if (OidIsValid(relOid))
214 rel = table_open(relOid, ShareRowExclusiveLock);
215 else
216 rel = table_openrv(stmt->relation, ShareRowExclusiveLock);
217
218 /*
219 * Triggers must be on tables or views, and there are additional
220 * relation-type-specific restrictions.
221 */
222 if (rel->rd_rel->relkind == RELKIND_RELATION)
223 {
224 /* Tables can't have INSTEAD OF triggers */
225 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
226 stmt->timing != TRIGGER_TYPE_AFTER)
229 errmsg("\"%s\" is a table",
231 errdetail("Tables cannot have INSTEAD OF triggers.")));
232 }
233 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
234 {
235 /* Partitioned tables can't have INSTEAD OF triggers */
236 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
237 stmt->timing != TRIGGER_TYPE_AFTER)
240 errmsg("\"%s\" is a table",
242 errdetail("Tables cannot have INSTEAD OF triggers.")));
243
244 /*
245 * FOR EACH ROW triggers have further restrictions
246 */
247 if (stmt->row)
248 {
249 /*
250 * Disallow use of transition tables.
251 *
252 * Note that we have another restriction about transition tables
253 * in partitions; search for 'has_superclass' below for an
254 * explanation. The check here is just to protect from the fact
255 * that if we allowed it here, the creation would succeed for a
256 * partitioned table with no partitions, but would be blocked by
257 * the other restriction when the first partition was created,
258 * which is very unfriendly behavior.
259 */
260 if (stmt->transitionRels != NIL)
263 errmsg("\"%s\" is a partitioned table",
265 errdetail("ROW triggers with transition tables are not supported on partitioned tables.")));
266 }
267 }
268 else if (rel->rd_rel->relkind == RELKIND_VIEW)
269 {
270 /*
271 * Views can have INSTEAD OF triggers (which we check below are
272 * row-level), or statement-level BEFORE/AFTER triggers.
273 */
274 if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row)
277 errmsg("\"%s\" is a view",
279 errdetail("Views cannot have row-level BEFORE or AFTER triggers.")));
280 /* Disallow TRUNCATE triggers on VIEWs */
281 if (TRIGGER_FOR_TRUNCATE(stmt->events))
284 errmsg("\"%s\" is a view",
286 errdetail("Views cannot have TRUNCATE triggers.")));
287 }
288 else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
289 {
290 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
291 stmt->timing != TRIGGER_TYPE_AFTER)
294 errmsg("\"%s\" is a foreign table",
296 errdetail("Foreign tables cannot have INSTEAD OF triggers.")));
297
298 /*
299 * We disallow constraint triggers to protect the assumption that
300 * triggers on FKs can't be deferred. See notes with AfterTriggers
301 * data structures, below.
302 */
303 if (stmt->isconstraint)
306 errmsg("\"%s\" is a foreign table",
308 errdetail("Foreign tables cannot have constraint triggers.")));
309 }
310 else
313 errmsg("relation \"%s\" cannot have triggers",
316
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:3880
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4083
#define InvalidAttrNumber
Definition attnum.h:23
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define CStringGetTextDatum(s)
Definition builtins.h:98
Datum byteain(PG_FUNCTION_ARGS)
Definition bytea.c:201
#define NameStr(name)
Definition c.h: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:688
bool allowSystemTableMods
Definition globals.c:132
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:686
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition indexing.c:233
int2vector * buildint2vector(const int16 *int2s, int n)
Definition int.c:114
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition inval.c:1666
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:107
#define NoLock
Definition lockdefs.h:34
#define ShareRowExclusiveLock
Definition lockdefs.h:41
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_rel_name(Oid relid)
Definition lsyscache.c:2159
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2234
Oid get_func_rettype(Oid funcid)
Definition lsyscache.c:1886
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:261
static Datum Int16GetDatum(int16 X)
Definition postgres.h:172
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
#define PointerGetDatum(X)
Definition postgres.h:354
static Datum CharGetDatum(char X)
Definition postgres.h:132
#define InvalidOid
#define PRS2_OLD_VARNO
Definition primnodes.h:251
#define PRS2_NEW_VARNO
Definition primnodes.h:252
#define RelationGetDescr(relation)
Definition rel.h:542
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationGetNamespace(relation)
Definition rel.h:557
#define ERRCODE_DUPLICATE_OBJECT
Definition streamutil.c:30
ItemPointerData t_self
Definition htup.h:65
Definition nodes.h: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:1632
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
bool superuser(void)
Definition superuser.c:47
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition trigger.c: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:3599
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:495

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

Referenced by ExecSimpleRelationUpdate(), and ExecUpdatePrologue().

◆ ExecBSDeleteTriggers()

void ExecBSDeleteTriggers ( EState estate,
ResultRelInfo relinfo 
)

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

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

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:1646
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition tableam.h:299
#define IsolationUsesXactSnapshot()
Definition xact.h:52

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

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

◆ MakeTransitionCaptureState()

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

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

6788{
6790}

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:4134
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 6838 of file trigger.c.

6840{
6843
6844 /*
6845 * Allocate in TopTransactionContext so the item survives for the duration
6846 * of the batch, which may span multiple trigger invocations.
6847 *
6848 * Must be called while afterTriggers is active; callbacks registered
6849 * outside a trigger-firing context would never fire.
6850 */
6854 item = palloc(sizeof(AfterTriggerCallbackItem));
6855 item->callback = callback;
6856 item->arg = arg;
6857 if (afterTriggers.query_depth >= 0)
6858 {
6861
6862 qs->batch_callbacks = lappend(qs->batch_callbacks, item);
6863 }
6864 else
6868}

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:170
Datum nameout(PG_FUNCTION_ARGS)
Definition name.c:71
static char * DatumGetCString(Datum X)
Definition postgres.h:365
static Datum NameGetDatum(const NameData *X)
Definition postgres.h:406
TriggerDesc * trigdesc
Definition rel.h:117
Definition c.h: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 5796 of file trigger.c.

5798{
5799 if (state->numstates >= state->numalloc)
5800 {
5801 int newalloc = state->numalloc * 2;
5802
5803 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5806 offsetof(SetConstraintStateData, trigstates) +
5808 state->numalloc = newalloc;
5809 Assert(state->numstates < state->numalloc);
5810 }
5811
5812 state->trigstates[state->numstates].sct_tgoid = tgoid;
5813 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5814 state->numstates++;
5815
5816 return state;
5817}

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCopy()

static SetConstraintState SetConstraintStateCopy ( SetConstraintState  origstate)
static

Definition at line 5776 of file trigger.c.

5777{
5779
5781
5782 state->all_isset = origstate->all_isset;
5783 state->all_isdeferred = origstate->all_isdeferred;
5784 state->numstates = origstate->numstates;
5785 memcpy(state->trigstates, origstate->trigstates,
5786 origstate->numstates * sizeof(SetConstraintTriggerData));
5787
5788 return state;
5789}

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCreate()

static SetConstraintState SetConstraintStateCreate ( int  numalloc)
static

Definition at line 5751 of file trigger.c.

5752{
5754
5755 /* Behave sanely with numalloc == 0 */
5756 if (numalloc <= 0)
5757 numalloc = 1;
5758
5759 /*
5760 * We assume that zeroing will correctly initialize the state values.
5761 */
5764 offsetof(SetConstraintStateData, trigstates) +
5765 numalloc * sizeof(SetConstraintTriggerData));
5766
5767 state->numalloc = numalloc;
5768
5769 return state;
5770}

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

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

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