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
 

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
 

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, Instrumentation *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, Instrumentation *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 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)
 

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 */
3897
3899{
3900 AfterTriggerEventList events; /* events pending from this query */
3901 Tuplestorestate *fdw_tuplestore; /* foreign tuples for said events */
3902 List *tables; /* list of AfterTriggersTableData, see below */
3903};
3904
3906{
3907 /* these fields are just for resetting at subtrans abort: */
3908 SetConstraintState state; /* saved S C state, or NULL if not yet saved */
3909 AfterTriggerEventList events; /* saved list pointer */
3910 int query_depth; /* saved query_depth */
3911 CommandId firing_counter; /* saved firing_counter */
3912};
3913
3915{
3916 /* relid + cmdType form the lookup key for these structs: */
3917 Oid relid; /* target table's OID */
3918 CmdType cmdType; /* event type, CMD_INSERT/UPDATE/DELETE */
3919 bool closed; /* true when no longer OK to add tuples */
3920 bool before_trig_done; /* did we already queue BS triggers? */
3921 bool after_trig_done; /* did we already queue AS triggers? */
3922 AfterTriggerEventList after_trig_events; /* if so, saved list pointer */
3923
3924 /* "old" transition table for UPDATE/DELETE, if any */
3926 /* "new" transition table for INSERT/UPDATE, if any */
3928
3929 TupleTableSlot *storeslot; /* for converting to tuplestore's format */
3930};
3931
3933
3934static void AfterTriggerExecute(EState *estate,
3935 AfterTriggerEvent event,
3939 TriggerDesc *trigdesc,
3940 FmgrInfo *finfo,
3941 Instrumentation *instr,
3946 CmdType cmdType);
3948 TupleDesc tupdesc);
3952 TransitionCaptureState *transition_capture);
3953static void TransitionTableAddTuple(EState *estate,
3954 int event,
3955 TransitionCaptureState *transition_capture,
3957 TupleTableSlot *slot,
3959 Tuplestorestate *tuplestore);
3961static SetConstraintState SetConstraintStateCreate(int numalloc);
3964 Oid tgoid, bool tgisdeferred);
3965static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent);
3966
3967
3968/*
3969 * Get the FDW tuplestore for the current trigger query level, creating it
3970 * if necessary.
3971 */
3972static Tuplestorestate *
3974{
3975 Tuplestorestate *ret;
3976
3978 if (ret == NULL)
3979 {
3982
3983 /*
3984 * Make the tuplestore valid until end of subtransaction. We really
3985 * only need it until AfterTriggerEndQuery().
3986 */
3990
3991 ret = tuplestore_begin_heap(false, false, work_mem);
3992
3995
3997 }
3998
3999 return ret;
4000}
4001
4002/* ----------
4003 * afterTriggerCheckState()
4004 *
4005 * Returns true if the trigger event is actually in state DEFERRED.
4006 * ----------
4007 */
4008static bool
4010{
4011 Oid tgoid = evtshared->ats_tgoid;
4013 int i;
4014
4015 /*
4016 * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
4017 * constraints declared NOT DEFERRABLE), the state is always false.
4018 */
4019 if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
4020 return false;
4021
4022 /*
4023 * If constraint state exists, SET CONSTRAINTS might have been executed
4024 * either for this trigger or for all triggers.
4025 */
4026 if (state != NULL)
4027 {
4028 /* Check for SET CONSTRAINTS for this specific trigger. */
4029 for (i = 0; i < state->numstates; i++)
4030 {
4031 if (state->trigstates[i].sct_tgoid == tgoid)
4032 return state->trigstates[i].sct_tgisdeferred;
4033 }
4034
4035 /* Check for SET CONSTRAINTS ALL. */
4036 if (state->all_isset)
4037 return state->all_isdeferred;
4038 }
4039
4040 /*
4041 * Otherwise return the default state for the trigger.
4042 */
4043 return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
4044}
4045
4046/* ----------
4047 * afterTriggerCopyBitmap()
4048 *
4049 * Copy bitmap into AfterTriggerEvents memory context, which is where the after
4050 * trigger events are kept.
4051 * ----------
4052 */
4053static Bitmapset *
4055{
4056 Bitmapset *dst;
4058
4059 if (src == NULL)
4060 return NULL;
4061
4063
4064 dst = bms_copy(src);
4065
4067
4068 return dst;
4069}
4070
4071/* ----------
4072 * afterTriggerAddEvent()
4073 *
4074 * Add a new trigger event to the specified queue.
4075 * The passed-in event data is copied.
4076 * ----------
4077 */
4078static void
4081{
4087
4088 /*
4089 * If empty list or not enough room in the tail chunk, make a new chunk.
4090 * We assume here that a new shared record will always be needed.
4091 */
4092 chunk = events->tail;
4093 if (chunk == NULL ||
4094 chunk->endfree - chunk->freeptr < needed)
4095 {
4097
4098 /* Create event context if we didn't already */
4102 "AfterTriggerEvents",
4104
4105 /*
4106 * Chunk size starts at 1KB and is allowed to increase up to 1MB.
4107 * These numbers are fairly arbitrary, though there is a hard limit at
4108 * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
4109 * shared records using the available space in ate_flags. Another
4110 * constraint is that if the chunk size gets too huge, the search loop
4111 * below would get slow given a (not too common) usage pattern with
4112 * many distinct event types in a chunk. Therefore, we double the
4113 * preceding chunk size only if there weren't too many shared records
4114 * in the preceding chunk; otherwise we halve it. This gives us some
4115 * ability to adapt to the actual usage pattern of the current query
4116 * while still having large chunk sizes in typical usage. All chunk
4117 * sizes used should be MAXALIGN multiples, to ensure that the shared
4118 * records will be aligned safely.
4119 */
4120#define MIN_CHUNK_SIZE 1024
4121#define MAX_CHUNK_SIZE (1024*1024)
4122
4123#if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
4124#error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
4125#endif
4126
4127 if (chunk == NULL)
4129 else
4130 {
4131 /* preceding chunk size... */
4132 chunksize = chunk->endptr - (char *) chunk;
4133 /* check number of shared records in preceding chunk */
4134 if ((chunk->endptr - chunk->endfree) <=
4135 (100 * sizeof(AfterTriggerSharedData)))
4136 chunksize *= 2; /* okay, double it */
4137 else
4138 chunksize /= 2; /* too many shared records */
4140 }
4142 chunk->next = NULL;
4143 chunk->freeptr = CHUNK_DATA_START(chunk);
4144 chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
4145 Assert(chunk->endfree - chunk->freeptr >= needed);
4146
4147 if (events->tail == NULL)
4148 {
4149 Assert(events->head == NULL);
4150 events->head = chunk;
4151 }
4152 else
4153 events->tail->next = chunk;
4154 events->tail = chunk;
4155 /* events->tailfree is now out of sync, but we'll fix it below */
4156 }
4157
4158 /*
4159 * Try to locate a matching shared-data record already in the chunk. If
4160 * none, make a new one. The search begins with the most recently added
4161 * record, since newer ones are most likely to match.
4162 */
4163 for (newshared = (AfterTriggerShared) chunk->endfree;
4164 (char *) newshared < chunk->endptr;
4165 newshared++)
4166 {
4167 /* compare fields roughly by probability of them being different */
4168 if (newshared->ats_tgoid == evtshared->ats_tgoid &&
4169 newshared->ats_event == evtshared->ats_event &&
4170 newshared->ats_firing_id == 0 &&
4171 newshared->ats_table == evtshared->ats_table &&
4172 newshared->ats_relid == evtshared->ats_relid &&
4173 newshared->ats_rolid == evtshared->ats_rolid &&
4174 bms_equal(newshared->ats_modifiedcols,
4175 evtshared->ats_modifiedcols))
4176 break;
4177 }
4178 if ((char *) newshared >= chunk->endptr)
4179 {
4180 newshared = ((AfterTriggerShared) chunk->endfree) - 1;
4181 *newshared = *evtshared;
4182 /* now we must make a suitably-long-lived copy of the bitmap */
4183 newshared->ats_modifiedcols = afterTriggerCopyBitmap(evtshared->ats_modifiedcols);
4184 newshared->ats_firing_id = 0; /* just to be sure */
4185 chunk->endfree = (char *) newshared;
4186 }
4187
4188 /* Insert the data */
4189 newevent = (AfterTriggerEvent) chunk->freeptr;
4190 memcpy(newevent, event, eventsize);
4191 /* ... and link the new event to its shared record */
4192 newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
4193 newevent->ate_flags |= (char *) newshared - (char *) newevent;
4194
4195 chunk->freeptr += eventsize;
4196 events->tailfree = chunk->freeptr;
4197}
4198
4199/* ----------
4200 * afterTriggerFreeEventList()
4201 *
4202 * Free all the event storage in the given list.
4203 * ----------
4204 */
4205static void
4207{
4209
4210 while ((chunk = events->head) != NULL)
4211 {
4212 events->head = chunk->next;
4213 pfree(chunk);
4214 }
4215 events->tail = NULL;
4216 events->tailfree = NULL;
4217}
4218
4219/* ----------
4220 * afterTriggerRestoreEventList()
4221 *
4222 * Restore an event list to its prior length, removing all the events
4223 * added since it had the value old_events.
4224 * ----------
4225 */
4226static void
4229{
4232
4233 if (old_events->tail == NULL)
4234 {
4235 /* restoring to a completely empty state, so free everything */
4237 }
4238 else
4239 {
4240 *events = *old_events;
4241 /* free any chunks after the last one we want to keep */
4242 for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4243 {
4244 next_chunk = chunk->next;
4245 pfree(chunk);
4246 }
4247 /* and clean up the tail chunk to be the right length */
4248 events->tail->next = NULL;
4249 events->tail->freeptr = events->tailfree;
4250
4251 /*
4252 * We don't make any effort to remove now-unused shared data records.
4253 * They might still be useful, anyway.
4254 */
4255 }
4256}
4257
4258/* ----------
4259 * afterTriggerDeleteHeadEventChunk()
4260 *
4261 * Remove the first chunk of events from the query level's event list.
4262 * Keep any event list pointers elsewhere in the query level's data
4263 * structures in sync.
4264 * ----------
4265 */
4266static void
4268{
4269 AfterTriggerEventChunk *target = qs->events.head;
4270 ListCell *lc;
4271
4272 Assert(target && target->next);
4273
4274 /*
4275 * First, update any pointers in the per-table data, so that they won't be
4276 * dangling. Resetting obsoleted pointers to NULL will make
4277 * cancel_prior_stmt_triggers start from the list head, which is fine.
4278 */
4279 foreach(lc, qs->tables)
4280 {
4282
4283 if (table->after_trig_done &&
4284 table->after_trig_events.tail == target)
4285 {
4286 table->after_trig_events.head = NULL;
4287 table->after_trig_events.tail = NULL;
4288 table->after_trig_events.tailfree = NULL;
4289 }
4290 }
4291
4292 /* Now we can flush the head chunk */
4293 qs->events.head = target->next;
4294 pfree(target);
4295}
4296
4297
4298/* ----------
4299 * AfterTriggerExecute()
4300 *
4301 * Fetch the required tuples back from the heap and fire one
4302 * single trigger function.
4303 *
4304 * Frequently, this will be fired many times in a row for triggers of
4305 * a single relation. Therefore, we cache the open relation and provide
4306 * fmgr lookup cache space at the caller level. (For triggers fired at
4307 * the end of a query, we can even piggyback on the executor's state.)
4308 *
4309 * When fired for a cross-partition update of a partitioned table, the old
4310 * tuple is fetched using 'src_relInfo' (the source leaf partition) and
4311 * the new tuple using 'dst_relInfo' (the destination leaf partition), though
4312 * both are converted into the root partitioned table's format before passing
4313 * to the trigger function.
4314 *
4315 * event: event currently being fired.
4316 * relInfo: result relation for event.
4317 * src_relInfo: source partition of a cross-partition update
4318 * dst_relInfo: its destination partition
4319 * trigdesc: working copy of rel's trigger info.
4320 * finfo: array of fmgr lookup cache entries (one per trigger in trigdesc).
4321 * instr: array of EXPLAIN ANALYZE instrumentation nodes (one per trigger),
4322 * or NULL if no instrumentation is wanted.
4323 * per_tuple_context: memory context to call trigger function in.
4324 * trig_tuple_slot1: scratch slot for tg_trigtuple (foreign tables only)
4325 * trig_tuple_slot2: scratch slot for tg_newtuple (foreign tables only)
4326 * ----------
4327 */
4328static void
4330 AfterTriggerEvent event,
4334 TriggerDesc *trigdesc,
4335 FmgrInfo *finfo, Instrumentation *instr,
4339{
4340 Relation rel = relInfo->ri_RelationDesc;
4341 Relation src_rel = src_relInfo->ri_RelationDesc;
4342 Relation dst_rel = dst_relInfo->ri_RelationDesc;
4344 Oid tgoid = evtshared->ats_tgoid;
4347 int save_sec_context;
4349 int tgindx;
4350 bool should_free_trig = false;
4351 bool should_free_new = false;
4352
4353 /*
4354 * Locate trigger in trigdesc. It might not be present, and in fact the
4355 * trigdesc could be NULL, if the trigger was dropped since the event was
4356 * queued. In that case, silently do nothing.
4357 */
4358 if (trigdesc == NULL)
4359 return;
4360 for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4361 {
4362 if (trigdesc->triggers[tgindx].tgoid == tgoid)
4363 {
4364 LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4365 break;
4366 }
4367 }
4368 if (LocTriggerData.tg_trigger == NULL)
4369 return;
4370
4371 /*
4372 * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4373 * to include time spent re-fetching tuples in the trigger cost.
4374 */
4375 if (instr)
4376 InstrStartNode(instr + tgindx);
4377
4378 /*
4379 * Fetch the required tuple(s).
4380 */
4381 switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4382 {
4384 {
4385 Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4386
4387 if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4389 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4390
4391 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4393 !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4395 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4396 }
4399
4400 /*
4401 * Store tuple in the slot so that tg_trigtuple does not reference
4402 * tuplestore memory. (It is formally possible for the trigger
4403 * function to queue trigger events that add to the same
4404 * tuplestore, which can push other tuples out of memory.) The
4405 * distinction is academic, because we start with a minimal tuple
4406 * that is stored as a heap tuple, constructed in different memory
4407 * context, in the slot anyway.
4408 */
4409 LocTriggerData.tg_trigslot = trig_tuple_slot1;
4410 LocTriggerData.tg_trigtuple =
4412
4413 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4415 {
4416 LocTriggerData.tg_newslot = trig_tuple_slot2;
4417 LocTriggerData.tg_newtuple =
4419 }
4420 else
4421 {
4422 LocTriggerData.tg_newtuple = NULL;
4423 }
4424 break;
4425
4426 default:
4427 if (ItemPointerIsValid(&(event->ate_ctid1)))
4428 {
4430 src_relInfo);
4431
4433 &(event->ate_ctid1),
4435 src_slot))
4436 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4437
4438 /*
4439 * Store the tuple fetched from the source partition into the
4440 * target (root partitioned) table slot, converting if needed.
4441 */
4442 if (src_relInfo != relInfo)
4443 {
4445
4446 LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4447 if (map)
4448 {
4450 src_slot,
4451 LocTriggerData.tg_trigslot);
4452 }
4453 else
4454 ExecCopySlot(LocTriggerData.tg_trigslot, src_slot);
4455 }
4456 else
4457 LocTriggerData.tg_trigslot = src_slot;
4458 LocTriggerData.tg_trigtuple =
4460 }
4461 else
4462 {
4463 LocTriggerData.tg_trigtuple = NULL;
4464 }
4465
4466 /* don't touch ctid2 if not there */
4468 (event->ate_flags & AFTER_TRIGGER_CP_UPDATE)) &&
4469 ItemPointerIsValid(&(event->ate_ctid2)))
4470 {
4472 dst_relInfo);
4473
4475 &(event->ate_ctid2),
4477 dst_slot))
4478 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4479
4480 /*
4481 * Store the tuple fetched from the destination partition into
4482 * the target (root partitioned) table slot, converting if
4483 * needed.
4484 */
4485 if (dst_relInfo != relInfo)
4486 {
4488
4489 LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4490 if (map)
4491 {
4493 dst_slot,
4494 LocTriggerData.tg_newslot);
4495 }
4496 else
4498 }
4499 else
4500 LocTriggerData.tg_newslot = dst_slot;
4501 LocTriggerData.tg_newtuple =
4503 }
4504 else
4505 {
4506 LocTriggerData.tg_newtuple = NULL;
4507 }
4508 }
4509
4510 /*
4511 * Set up the tuplestore information to let the trigger have access to
4512 * transition tables. When we first make a transition table available to
4513 * a trigger, mark it "closed" so that it cannot change anymore. If any
4514 * additional events of the same type get queued in the current trigger
4515 * query level, they'll go into new transition tables.
4516 */
4517 LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4518 if (evtshared->ats_table)
4519 {
4520 if (LocTriggerData.tg_trigger->tgoldtable)
4521 {
4522 LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4523 evtshared->ats_table->closed = true;
4524 }
4525
4526 if (LocTriggerData.tg_trigger->tgnewtable)
4527 {
4528 LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4529 evtshared->ats_table->closed = true;
4530 }
4531 }
4532
4533 /*
4534 * Setup the remaining trigger information
4535 */
4537 LocTriggerData.tg_event =
4539 LocTriggerData.tg_relation = rel;
4540 if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4541 LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4542
4544
4545 /*
4546 * If necessary, become the role that was active when the trigger got
4547 * queued. Note that the role might have been dropped since the trigger
4548 * was queued, but if that is a problem, we will get an error later.
4549 * Checking here would still leave a race condition.
4550 */
4551 GetUserIdAndSecContext(&save_rolid, &save_sec_context);
4552 if (save_rolid != evtshared->ats_rolid)
4554 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
4555
4556 /*
4557 * Call the trigger and throw away any possibly returned updated tuple.
4558 * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4559 */
4561 tgindx,
4562 finfo,
4563 NULL,
4565 if (rettuple != NULL &&
4566 rettuple != LocTriggerData.tg_trigtuple &&
4567 rettuple != LocTriggerData.tg_newtuple)
4569
4570 /* Restore the current role if necessary */
4571 if (save_rolid != evtshared->ats_rolid)
4572 SetUserIdAndSecContext(save_rolid, save_sec_context);
4573
4574 /*
4575 * Release resources
4576 */
4577 if (should_free_trig)
4578 heap_freetuple(LocTriggerData.tg_trigtuple);
4579 if (should_free_new)
4580 heap_freetuple(LocTriggerData.tg_newtuple);
4581
4582 /* don't clear slots' contents if foreign table */
4583 if (trig_tuple_slot1 == NULL)
4584 {
4585 if (LocTriggerData.tg_trigslot)
4586 ExecClearTuple(LocTriggerData.tg_trigslot);
4587 if (LocTriggerData.tg_newslot)
4588 ExecClearTuple(LocTriggerData.tg_newslot);
4589 }
4590
4591 /*
4592 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4593 * one "tuple returned" (really the number of firings).
4594 */
4595 if (instr)
4596 InstrStopNode(instr + tgindx, 1);
4597}
4598
4599
4600/*
4601 * afterTriggerMarkEvents()
4602 *
4603 * Scan the given event list for not yet invoked events. Mark the ones
4604 * that can be invoked now with the current firing ID.
4605 *
4606 * If move_list isn't NULL, events that are not to be invoked now are
4607 * transferred to move_list.
4608 *
4609 * When immediate_only is true, do not invoke currently-deferred triggers.
4610 * (This will be false only at main transaction exit.)
4611 *
4612 * Returns true if any invokable events were found.
4613 */
4614static bool
4617 bool immediate_only)
4618{
4619 bool found = false;
4620 bool deferred_found = false;
4621 AfterTriggerEvent event;
4623
4624 for_each_event_chunk(event, chunk, *events)
4625 {
4627 bool defer_it = false;
4628
4629 if (!(event->ate_flags &
4631 {
4632 /*
4633 * This trigger hasn't been called or scheduled yet. Check if we
4634 * should call it now.
4635 */
4637 {
4638 defer_it = true;
4639 }
4640 else
4641 {
4642 /*
4643 * Mark it as to be fired in this firing cycle.
4644 */
4645 evtshared->ats_firing_id = afterTriggers.firing_counter;
4646 event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4647 found = true;
4648 }
4649 }
4650
4651 /*
4652 * If it's deferred, move it to move_list, if requested.
4653 */
4654 if (defer_it && move_list != NULL)
4655 {
4656 deferred_found = true;
4657 /* add it to move_list */
4659 /* mark original copy "done" so we don't do it again */
4660 event->ate_flags |= AFTER_TRIGGER_DONE;
4661 }
4662 }
4663
4664 /*
4665 * We could allow deferred triggers if, before the end of the
4666 * security-restricted operation, we were to verify that a SET CONSTRAINTS
4667 * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4668 */
4670 ereport(ERROR,
4672 errmsg("cannot fire deferred trigger within security-restricted operation")));
4673
4674 return found;
4675}
4676
4677/*
4678 * afterTriggerInvokeEvents()
4679 *
4680 * Scan the given event list for events that are marked as to be fired
4681 * in the current firing cycle, and fire them.
4682 *
4683 * If estate isn't NULL, we use its result relation info to avoid repeated
4684 * openings and closing of trigger target relations. If it is NULL, we
4685 * make one locally to cache the info in case there are multiple trigger
4686 * events per rel.
4687 *
4688 * When delete_ok is true, it's safe to delete fully-processed events.
4689 * (We are not very tense about that: we simply reset a chunk to be empty
4690 * if all its events got fired. The objective here is just to avoid useless
4691 * rescanning of events when a trigger queues new events during transaction
4692 * end, so it's not necessary to worry much about the case where only
4693 * some events are fired.)
4694 *
4695 * Returns true if no unfired events remain in the list (this allows us
4696 * to avoid repeating afterTriggerMarkEvents).
4697 */
4698static bool
4701 EState *estate,
4702 bool delete_ok)
4703{
4704 bool all_fired = true;
4707 bool local_estate = false;
4709 Relation rel = NULL;
4710 TriggerDesc *trigdesc = NULL;
4711 FmgrInfo *finfo = NULL;
4712 Instrumentation *instr = NULL;
4714 *slot2 = NULL;
4715
4716 /* Make a local EState if need be */
4717 if (estate == NULL)
4718 {
4719 estate = CreateExecutorState();
4720 local_estate = true;
4721 }
4722
4723 /* Make a per-tuple memory context for trigger function calls */
4726 "AfterTriggerTupleContext",
4728
4729 for_each_chunk(chunk, *events)
4730 {
4731 AfterTriggerEvent event;
4732 bool all_fired_in_chunk = true;
4733
4734 for_each_event(event, chunk)
4735 {
4737
4738 /*
4739 * Is it one for me to fire?
4740 */
4741 if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4742 evtshared->ats_firing_id == firing_id)
4743 {
4745 *dst_rInfo;
4746
4747 /*
4748 * So let's fire it... but first, find the correct relation if
4749 * this is not the same relation as before.
4750 */
4751 if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4752 {
4753 rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4754 NULL);
4755 rel = rInfo->ri_RelationDesc;
4756 /* Catch calls with insufficient relcache refcounting */
4758 trigdesc = rInfo->ri_TrigDesc;
4759 /* caution: trigdesc could be NULL here */
4760 finfo = rInfo->ri_TrigFunctions;
4761 instr = rInfo->ri_TrigInstrument;
4762 if (slot1 != NULL)
4763 {
4766 slot1 = slot2 = NULL;
4767 }
4768 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4769 {
4774 }
4775 }
4776
4777 /*
4778 * Look up source and destination partition result rels of a
4779 * cross-partition update event.
4780 */
4781 if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4783 {
4784 Assert(OidIsValid(event->ate_src_part) &&
4785 OidIsValid(event->ate_dst_part));
4787 event->ate_src_part,
4788 rInfo);
4790 event->ate_dst_part,
4791 rInfo);
4792 }
4793 else
4795
4796 /*
4797 * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4798 * still set, so recursive examinations of the event list
4799 * won't try to re-fire it.
4800 */
4801 AfterTriggerExecute(estate, event, rInfo,
4803 trigdesc, finfo, instr,
4805
4806 /*
4807 * Mark the event as done.
4808 */
4809 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4810 event->ate_flags |= AFTER_TRIGGER_DONE;
4811 }
4812 else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4813 {
4814 /* something remains to be done */
4815 all_fired = all_fired_in_chunk = false;
4816 }
4817 }
4818
4819 /* Clear the chunk if delete_ok and nothing left of interest */
4821 {
4822 chunk->freeptr = CHUNK_DATA_START(chunk);
4823 chunk->endfree = chunk->endptr;
4824
4825 /*
4826 * If it's last chunk, must sync event list's tailfree too. Note
4827 * that delete_ok must NOT be passed as true if there could be
4828 * additional AfterTriggerEventList values pointing at this event
4829 * list, since we'd fail to fix their copies of tailfree.
4830 */
4831 if (chunk == events->tail)
4832 events->tailfree = chunk->freeptr;
4833 }
4834 }
4835 if (slot1 != NULL)
4836 {
4839 }
4840
4841 /* Release working resources */
4843
4844 if (local_estate)
4845 {
4847 ExecResetTupleTable(estate->es_tupleTable, false);
4848 FreeExecutorState(estate);
4849 }
4850
4851 return all_fired;
4852}
4853
4854
4855/*
4856 * GetAfterTriggersTableData
4857 *
4858 * Find or create an AfterTriggersTableData struct for the specified
4859 * trigger event (relation + operation type). Ignore existing structs
4860 * marked "closed"; we don't want to put any additional tuples into them,
4861 * nor change their stmt-triggers-fired state.
4862 *
4863 * Note: the AfterTriggersTableData list is allocated in the current
4864 * (sub)transaction's CurTransactionContext. This is OK because
4865 * we don't need it to live past AfterTriggerEndQuery.
4866 */
4869{
4873 ListCell *lc;
4874
4875 /* At this level, cmdType should not be, eg, CMD_MERGE */
4876 Assert(cmdType == CMD_INSERT ||
4877 cmdType == CMD_UPDATE ||
4878 cmdType == CMD_DELETE);
4879
4880 /* Caller should have ensured query_depth is OK. */
4884
4885 foreach(lc, qs->tables)
4886 {
4888 if (table->relid == relid && table->cmdType == cmdType &&
4889 !table->closed)
4890 return table;
4891 }
4892
4894
4896 table->relid = relid;
4897 table->cmdType = cmdType;
4898 qs->tables = lappend(qs->tables, table);
4899
4901
4902 return table;
4903}
4904
4905/*
4906 * Returns a TupleTableSlot suitable for holding the tuples to be put
4907 * into AfterTriggersTableData's transition table tuplestores.
4908 */
4909static TupleTableSlot *
4911 TupleDesc tupdesc)
4912{
4913 /* Create it if not already done. */
4914 if (!table->storeslot)
4915 {
4917
4918 /*
4919 * We need this slot only until AfterTriggerEndQuery, but making it
4920 * last till end-of-subxact is good enough. It'll be freed by
4921 * AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4922 * a different lifespan, so we'd better make a copy of that.
4923 */
4925 tupdesc = CreateTupleDescCopy(tupdesc);
4926 table->storeslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
4928 }
4929
4930 return table->storeslot;
4931}
4932
4933/*
4934 * MakeTransitionCaptureState
4935 *
4936 * Make a TransitionCaptureState object for the given TriggerDesc, target
4937 * relation, and operation type. The TCS object holds all the state needed
4938 * to decide whether to capture tuples in transition tables.
4939 *
4940 * If there are no triggers in 'trigdesc' that request relevant transition
4941 * tables, then return NULL.
4942 *
4943 * The resulting object can be passed to the ExecAR* functions. When
4944 * dealing with child tables, the caller can set tcs_original_insert_tuple
4945 * to avoid having to reconstruct the original tuple in the root table's
4946 * format.
4947 *
4948 * Note that we copy the flags from a parent table into this struct (rather
4949 * than subsequently using the relation's TriggerDesc directly) so that we can
4950 * use it to control collection of transition tuples from child tables.
4951 *
4952 * Per SQL spec, all operations of the same kind (INSERT/UPDATE/DELETE)
4953 * on the same table during one query should share one transition table.
4954 * Therefore, the Tuplestores are owned by an AfterTriggersTableData struct
4955 * looked up using the table OID + CmdType, and are merely referenced by
4956 * the TransitionCaptureState objects we hand out to callers.
4957 */
4959MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
4960{
4962 bool need_old_upd,
4971
4972 if (trigdesc == NULL)
4973 return NULL;
4974
4975 /* Detect which table(s) we need. */
4976 switch (cmdType)
4977 {
4978 case CMD_INSERT:
4981 break;
4982 case CMD_UPDATE:
4985 need_old_del = need_new_ins = false;
4986 break;
4987 case CMD_DELETE:
4990 break;
4991 case CMD_MERGE:
4996 break;
4997 default:
4998 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
4999 /* keep compiler quiet */
5001 break;
5002 }
5004 return NULL;
5005
5006 /* Check state, like AfterTriggerSaveEvent. */
5007 if (afterTriggers.query_depth < 0)
5008 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
5009
5010 /* Be sure we have enough space to record events at this query depth. */
5013
5014 /*
5015 * Find or create AfterTriggersTableData struct(s) to hold the
5016 * tuplestore(s). If there's a matching struct but it's marked closed,
5017 * ignore it; we need a newer one.
5018 *
5019 * Note: MERGE must use the same AfterTriggersTableData structs as INSERT,
5020 * UPDATE, and DELETE, so that any MERGE'd tuples are added to the same
5021 * tuplestores as tuples from any INSERT, UPDATE, or DELETE commands
5022 * running in the same top-level command (e.g., in a writable CTE).
5023 *
5024 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
5025 * allocated in the current (sub)transaction's CurTransactionContext, and
5026 * the tuplestores are managed by the (sub)transaction's resource owner.
5027 * This is sufficient lifespan because we do not allow triggers using
5028 * transition tables to be deferrable; they will be fired during
5029 * AfterTriggerEndQuery, after which it's okay to delete the data.
5030 */
5031 if (need_new_ins)
5033 else
5034 ins_table = NULL;
5035
5038 else
5039 upd_table = NULL;
5040
5041 if (need_old_del)
5043 else
5044 del_table = NULL;
5045
5046 /* Now create required tuplestore(s), if we don't have them already. */
5050
5051 if (need_old_upd && upd_table->old_tuplestore == NULL)
5052 upd_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5053 if (need_new_upd && upd_table->new_tuplestore == NULL)
5054 upd_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5055 if (need_old_del && del_table->old_tuplestore == NULL)
5056 del_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5057 if (need_new_ins && ins_table->new_tuplestore == NULL)
5058 ins_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5059
5062
5063 /* Now build the TransitionCaptureState struct, in caller's context */
5065 state->tcs_delete_old_table = need_old_del;
5066 state->tcs_update_old_table = need_old_upd;
5067 state->tcs_update_new_table = need_new_upd;
5068 state->tcs_insert_new_table = need_new_ins;
5069 state->tcs_insert_private = ins_table;
5070 state->tcs_update_private = upd_table;
5071 state->tcs_delete_private = del_table;
5072
5073 return state;
5074}
5075
5076
5077/* ----------
5078 * AfterTriggerBeginXact()
5079 *
5080 * Called at transaction start (either BEGIN or implicit for single
5081 * statement outside of transaction block).
5082 * ----------
5083 */
5084void
5086{
5087 /*
5088 * Initialize after-trigger state structure to empty
5089 */
5090 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5092
5093 /*
5094 * Verify that there is no leftover state remaining. If these assertions
5095 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5096 * up properly.
5097 */
5105}
5106
5107
5108/* ----------
5109 * AfterTriggerBeginQuery()
5110 *
5111 * Called just before we start processing a single query within a
5112 * transaction (or subtransaction). Most of the real work gets deferred
5113 * until somebody actually tries to queue a trigger event.
5114 * ----------
5115 */
5116void
5118{
5119 /* Increase the query stack depth */
5121}
5122
5123
5124/* ----------
5125 * AfterTriggerEndQuery()
5126 *
5127 * Called after one query has been completely processed. At this time
5128 * we invoke all AFTER IMMEDIATE trigger events queued by the query, and
5129 * transfer deferred trigger events to the global deferred-trigger list.
5130 *
5131 * Note that this must be called BEFORE closing down the executor
5132 * with ExecutorEnd, because we make use of the EState's info about
5133 * target relations. Normally it is called from ExecutorFinish.
5134 * ----------
5135 */
5136void
5138{
5140
5141 /* Must be inside a query, too */
5143
5144 /*
5145 * If we never even got as far as initializing the event stack, there
5146 * certainly won't be any events, so exit quickly.
5147 */
5149 {
5151 return;
5152 }
5153
5154 /*
5155 * Process all immediate-mode triggers queued by the query, and move the
5156 * deferred ones to the main list of deferred events.
5157 *
5158 * Notice that we decide which ones will be fired, and put the deferred
5159 * ones on the main list, before anything is actually fired. This ensures
5160 * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5161 * IMMEDIATE: all events we have decided to defer will be available for it
5162 * to fire.
5163 *
5164 * We loop in case a trigger queues more events at the same query level.
5165 * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5166 * will instead fire any triggers in a dedicated query level. Foreign key
5167 * enforcement triggers do add to the current query level, thanks to their
5168 * passing fire_triggers = false to SPI_execute_snapshot(). Other
5169 * C-language triggers might do likewise.
5170 *
5171 * If we find no firable events, we don't have to increment
5172 * firing_counter.
5173 */
5175
5176 for (;;)
5177 {
5178 if (afterTriggerMarkEvents(&qs->events, &afterTriggers.events, true))
5179 {
5181 AfterTriggerEventChunk *oldtail = qs->events.tail;
5182
5183 if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5184 break; /* all fired */
5185
5186 /*
5187 * Firing a trigger could result in query_stack being repalloc'd,
5188 * so we must recalculate qs after each afterTriggerInvokeEvents
5189 * call. Furthermore, it's unsafe to pass delete_ok = true here,
5190 * because that could cause afterTriggerInvokeEvents to try to
5191 * access qs->events after the stack has been repalloc'd.
5192 */
5194
5195 /*
5196 * We'll need to scan the events list again. To reduce the cost
5197 * of doing so, get rid of completely-fired chunks. We know that
5198 * all events were marked IN_PROGRESS or DONE at the conclusion of
5199 * afterTriggerMarkEvents, so any still-interesting events must
5200 * have been added after that, and so must be in the chunk that
5201 * was then the tail chunk, or in later chunks. So, zap all
5202 * chunks before oldtail. This is approximately the same set of
5203 * events we would have gotten rid of by passing delete_ok = true.
5204 */
5205 Assert(oldtail != NULL);
5206 while (qs->events.head != oldtail)
5208 }
5209 else
5210 break;
5211 }
5212
5213 /* Release query-level-local storage, including tuplestores if any */
5215
5217}
5218
5219
5220/*
5221 * AfterTriggerFreeQuery
5222 * Release subsidiary storage for a trigger query level.
5223 * This includes closing down tuplestores.
5224 * Note: it's important for this to be safe if interrupted by an error
5225 * and then called again for the same query level.
5226 */
5227static void
5229{
5230 Tuplestorestate *ts;
5231 List *tables;
5232 ListCell *lc;
5233
5234 /* Drop the trigger events */
5235 afterTriggerFreeEventList(&qs->events);
5236
5237 /* Drop FDW tuplestore if any */
5238 ts = qs->fdw_tuplestore;
5239 qs->fdw_tuplestore = NULL;
5240 if (ts)
5241 tuplestore_end(ts);
5242
5243 /* Release per-table subsidiary storage */
5244 tables = qs->tables;
5245 foreach(lc, tables)
5246 {
5248
5249 ts = table->old_tuplestore;
5250 table->old_tuplestore = NULL;
5251 if (ts)
5252 tuplestore_end(ts);
5253 ts = table->new_tuplestore;
5254 table->new_tuplestore = NULL;
5255 if (ts)
5256 tuplestore_end(ts);
5257 if (table->storeslot)
5258 {
5259 TupleTableSlot *slot = table->storeslot;
5260
5261 table->storeslot = NULL;
5263 }
5264 }
5265
5266 /*
5267 * Now free the AfterTriggersTableData structs and list cells. Reset list
5268 * pointer first; if list_free_deep somehow gets an error, better to leak
5269 * that storage than have an infinite loop.
5270 */
5271 qs->tables = NIL;
5272 list_free_deep(tables);
5273}
5274
5275
5276/* ----------
5277 * AfterTriggerFireDeferred()
5278 *
5279 * Called just before the current transaction is committed. At this
5280 * time we invoke all pending DEFERRED triggers.
5281 *
5282 * It is possible for other modules to queue additional deferred triggers
5283 * during pre-commit processing; therefore xact.c may have to call this
5284 * multiple times.
5285 * ----------
5286 */
5287void
5289{
5290 AfterTriggerEventList *events;
5291 bool snap_pushed = false;
5292
5293 /* Must not be inside a query */
5295
5296 /*
5297 * If there are any triggers to fire, make sure we have set a snapshot for
5298 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5299 * can't assume ActiveSnapshot is valid on entry.)
5300 */
5301 events = &afterTriggers.events;
5302 if (events->head != NULL)
5303 {
5305 snap_pushed = true;
5306 }
5307
5308 /*
5309 * Run all the remaining triggers. Loop until they are all gone, in case
5310 * some trigger queues more for us to do.
5311 */
5312 while (afterTriggerMarkEvents(events, NULL, false))
5313 {
5315
5316 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5317 break; /* all fired */
5318 }
5319
5320 /*
5321 * We don't bother freeing the event list, since it will go away anyway
5322 * (and more efficiently than via pfree) in AfterTriggerEndXact.
5323 */
5324
5325 if (snap_pushed)
5327}
5328
5329
5330/* ----------
5331 * AfterTriggerEndXact()
5332 *
5333 * The current transaction is finishing.
5334 *
5335 * Any unfired triggers are canceled so we simply throw
5336 * away anything we know.
5337 *
5338 * Note: it is possible for this to be called repeatedly in case of
5339 * error during transaction abort; therefore, do not complain if
5340 * already closed down.
5341 * ----------
5342 */
5343void
5345{
5346 /*
5347 * Forget the pending-events list.
5348 *
5349 * Since all the info is in TopTransactionContext or children thereof, we
5350 * don't really need to do anything to reclaim memory. However, the
5351 * pending-events list could be large, and so it's useful to discard it as
5352 * soon as possible --- especially if we are aborting because we ran out
5353 * of memory for the list!
5354 */
5356 {
5362 }
5363
5364 /*
5365 * Forget any subtransaction state as well. Since this can't be very
5366 * large, we let the eventual reset of TopTransactionContext free the
5367 * memory instead of doing it here.
5368 */
5371
5372
5373 /*
5374 * Forget the query stack and constraint-related state information. As
5375 * with the subtransaction state information, we don't bother freeing the
5376 * memory here.
5377 */
5381
5382 /* No more afterTriggers manipulation until next transaction starts. */
5384}
5385
5386/*
5387 * AfterTriggerBeginSubXact()
5388 *
5389 * Start a subtransaction.
5390 */
5391void
5393{
5394 int my_level = GetCurrentTransactionNestLevel();
5395
5396 /*
5397 * Allocate more space in the trans_stack if needed. (Note: because the
5398 * minimum nest level of a subtransaction is 2, we waste the first couple
5399 * entries of the array; not worth the notational effort to avoid it.)
5400 */
5401 while (my_level >= afterTriggers.maxtransdepth)
5402 {
5404 {
5405 /* Arbitrarily initialize for max of 8 subtransaction levels */
5408 8 * sizeof(AfterTriggersTransData));
5410 }
5411 else
5412 {
5413 /* repalloc will keep the stack in the same context */
5415
5420 }
5421 }
5422
5423 /*
5424 * Push the current information into the stack. The SET CONSTRAINTS state
5425 * is not saved until/unless changed. Likewise, we don't make a
5426 * per-subtransaction event context until needed.
5427 */
5428 afterTriggers.trans_stack[my_level].state = NULL;
5432}
5433
5434/*
5435 * AfterTriggerEndSubXact()
5436 *
5437 * The current subtransaction is ending.
5438 */
5439void
5441{
5442 int my_level = GetCurrentTransactionNestLevel();
5444 AfterTriggerEvent event;
5447
5448 /*
5449 * Pop the prior state if needed.
5450 */
5451 if (isCommit)
5452 {
5454 /* If we saved a prior state, we don't need it anymore */
5456 if (state != NULL)
5457 pfree(state);
5458 /* this avoids double pfree if error later: */
5459 afterTriggers.trans_stack[my_level].state = NULL;
5462 }
5463 else
5464 {
5465 /*
5466 * Aborting. It is possible subxact start failed before calling
5467 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5468 * trans_stack levels that aren't there.
5469 */
5470 if (my_level >= afterTriggers.maxtransdepth)
5471 return;
5472
5473 /*
5474 * Release query-level storage for queries being aborted, and restore
5475 * query_depth to its pre-subxact value. This assumes that a
5476 * subtransaction will not add events to query levels started in a
5477 * earlier transaction state.
5478 */
5480 {
5484 }
5487
5488 /*
5489 * Restore the global deferred-event list to its former length,
5490 * discarding any events queued by the subxact.
5491 */
5493 &afterTriggers.trans_stack[my_level].events);
5494
5495 /*
5496 * Restore the trigger state. If the saved state is NULL, then this
5497 * subxact didn't save it, so it doesn't need restoring.
5498 */
5500 if (state != NULL)
5501 {
5504 }
5505 /* this avoids double pfree if error later: */
5506 afterTriggers.trans_stack[my_level].state = NULL;
5507
5508 /*
5509 * Scan for any remaining deferred events that were marked DONE or IN
5510 * PROGRESS by this subxact or a child, and un-mark them. We can
5511 * recognize such events because they have a firing ID greater than or
5512 * equal to the firing_counter value we saved at subtransaction start.
5513 * (This essentially assumes that the current subxact includes all
5514 * subxacts started after it.)
5515 */
5518 {
5520
5521 if (event->ate_flags &
5523 {
5524 if (evtshared->ats_firing_id >= subxact_firing_id)
5525 event->ate_flags &=
5527 }
5528 }
5529 }
5530}
5531
5532/*
5533 * Get the transition table for the given event and depending on whether we are
5534 * processing the old or the new tuple.
5535 */
5536static Tuplestorestate *
5540 TransitionCaptureState *transition_capture)
5541{
5542 Tuplestorestate *tuplestore = NULL;
5543 bool delete_old_table = transition_capture->tcs_delete_old_table;
5544 bool update_old_table = transition_capture->tcs_update_old_table;
5545 bool update_new_table = transition_capture->tcs_update_new_table;
5546 bool insert_new_table = transition_capture->tcs_insert_new_table;
5547
5548 /*
5549 * For INSERT events NEW should be non-NULL, for DELETE events OLD should
5550 * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
5551 * non-NULL. But for UPDATE events fired for capturing transition tuples
5552 * during UPDATE partition-key row movement, OLD is NULL when the event is
5553 * for a row being inserted, whereas NEW is NULL when the event is for a
5554 * row being deleted.
5555 */
5557 TupIsNull(oldslot)));
5559 TupIsNull(newslot)));
5560
5561 if (!TupIsNull(oldslot))
5562 {
5564 if (event == TRIGGER_EVENT_DELETE && delete_old_table)
5565 tuplestore = transition_capture->tcs_delete_private->old_tuplestore;
5566 else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
5567 tuplestore = transition_capture->tcs_update_private->old_tuplestore;
5568 }
5569 else if (!TupIsNull(newslot))
5570 {
5572 if (event == TRIGGER_EVENT_INSERT && insert_new_table)
5573 tuplestore = transition_capture->tcs_insert_private->new_tuplestore;
5574 else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
5575 tuplestore = transition_capture->tcs_update_private->new_tuplestore;
5576 }
5577
5578 return tuplestore;
5579}
5580
5581/*
5582 * Add the given heap tuple to the given tuplestore, applying the conversion
5583 * map if necessary.
5584 *
5585 * If original_insert_tuple is given, we can add that tuple without conversion.
5586 */
5587static void
5589 int event,
5590 TransitionCaptureState *transition_capture,
5592 TupleTableSlot *slot,
5594 Tuplestorestate *tuplestore)
5595{
5596 TupleConversionMap *map;
5597
5598 /*
5599 * Nothing needs to be done if we don't have a tuplestore.
5600 */
5601 if (tuplestore == NULL)
5602 return;
5603
5606 else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5607 {
5609 TupleTableSlot *storeslot;
5610
5611 switch (event)
5612 {
5614 table = transition_capture->tcs_insert_private;
5615 break;
5617 table = transition_capture->tcs_update_private;
5618 break;
5620 table = transition_capture->tcs_delete_private;
5621 break;
5622 default:
5623 elog(ERROR, "invalid after-trigger event code: %d", event);
5624 table = NULL; /* keep compiler quiet */
5625 break;
5626 }
5627
5628 storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5629 execute_attr_map_slot(map->attrMap, slot, storeslot);
5630 tuplestore_puttupleslot(tuplestore, storeslot);
5631 }
5632 else
5633 tuplestore_puttupleslot(tuplestore, slot);
5634}
5635
5636/* ----------
5637 * AfterTriggerEnlargeQueryState()
5638 *
5639 * Prepare the necessary state so that we can record AFTER trigger events
5640 * queued by a query. It is allowed to have nested queries within a
5641 * (sub)transaction, so we need to have separate state for each query
5642 * nesting level.
5643 * ----------
5644 */
5645static void
5647{
5649
5651
5653 {
5654 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5655
5660 }
5661 else
5662 {
5663 /* repalloc will keep the stack in the same context */
5666 old_alloc * 2);
5667
5672 }
5673
5674 /* Initialize new array entries to empty */
5676 {
5678
5679 qs->events.head = NULL;
5680 qs->events.tail = NULL;
5681 qs->events.tailfree = NULL;
5682 qs->fdw_tuplestore = NULL;
5683 qs->tables = NIL;
5684
5685 ++init_depth;
5686 }
5687}
5688
5689/*
5690 * Create an empty SetConstraintState with room for numalloc trigstates
5691 */
5692static SetConstraintState
5693SetConstraintStateCreate(int numalloc)
5694{
5696
5697 /* Behave sanely with numalloc == 0 */
5698 if (numalloc <= 0)
5699 numalloc = 1;
5700
5701 /*
5702 * We assume that zeroing will correctly initialize the state values.
5703 */
5706 offsetof(SetConstraintStateData, trigstates) +
5707 numalloc * sizeof(SetConstraintTriggerData));
5708
5709 state->numalloc = numalloc;
5710
5711 return state;
5712}
5713
5714/*
5715 * Copy a SetConstraintState
5716 */
5717static SetConstraintState
5719{
5721
5723
5724 state->all_isset = origstate->all_isset;
5725 state->all_isdeferred = origstate->all_isdeferred;
5726 state->numstates = origstate->numstates;
5727 memcpy(state->trigstates, origstate->trigstates,
5728 origstate->numstates * sizeof(SetConstraintTriggerData));
5729
5730 return state;
5731}
5732
5733/*
5734 * Add a per-trigger item to a SetConstraintState. Returns possibly-changed
5735 * pointer to the state object (it will change if we have to repalloc).
5736 */
5737static SetConstraintState
5739 Oid tgoid, bool tgisdeferred)
5740{
5741 if (state->numstates >= state->numalloc)
5742 {
5743 int newalloc = state->numalloc * 2;
5744
5745 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5748 offsetof(SetConstraintStateData, trigstates) +
5750 state->numalloc = newalloc;
5751 Assert(state->numstates < state->numalloc);
5752 }
5753
5754 state->trigstates[state->numstates].sct_tgoid = tgoid;
5755 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5756 state->numstates++;
5757
5758 return state;
5759}
5760
5761/* ----------
5762 * AfterTriggerSetState()
5763 *
5764 * Execute the SET CONSTRAINTS ... utility command.
5765 * ----------
5766 */
5767void
5769{
5770 int my_level = GetCurrentTransactionNestLevel();
5771
5772 /* If we haven't already done so, initialize our state. */
5773 if (afterTriggers.state == NULL)
5775
5776 /*
5777 * If in a subtransaction, and we didn't save the current state already,
5778 * save it so it can be restored if the subtransaction aborts.
5779 */
5780 if (my_level > 1 &&
5781 afterTriggers.trans_stack[my_level].state == NULL)
5782 {
5783 afterTriggers.trans_stack[my_level].state =
5785 }
5786
5787 /*
5788 * Handle SET CONSTRAINTS ALL ...
5789 */
5790 if (stmt->constraints == NIL)
5791 {
5792 /*
5793 * Forget any previous SET CONSTRAINTS commands in this transaction.
5794 */
5796
5797 /*
5798 * Set the per-transaction ALL state to known.
5799 */
5802 }
5803 else
5804 {
5807 List *conoidlist = NIL;
5808 List *tgoidlist = NIL;
5809 ListCell *lc;
5810
5811 /*
5812 * Handle SET CONSTRAINTS constraint-name [, ...]
5813 *
5814 * First, identify all the named constraints and make a list of their
5815 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5816 * the same name within a schema, the specifications are not
5817 * necessarily unique. Our strategy is to target all matching
5818 * constraints within the first search-path schema that has any
5819 * matches, but disregard matches in schemas beyond the first match.
5820 * (This is a bit odd but it's the historical behavior.)
5821 *
5822 * A constraint in a partitioned table may have corresponding
5823 * constraints in the partitions. Grab those too.
5824 */
5826
5827 foreach(lc, stmt->constraints)
5828 {
5829 RangeVar *constraint = lfirst(lc);
5830 bool found;
5832 ListCell *nslc;
5833
5834 if (constraint->catalogname)
5835 {
5836 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5837 ereport(ERROR,
5839 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5840 constraint->catalogname, constraint->schemaname,
5841 constraint->relname)));
5842 }
5843
5844 /*
5845 * If we're given the schema name with the constraint, look only
5846 * in that schema. If given a bare constraint name, use the
5847 * search path to find the first matching constraint.
5848 */
5849 if (constraint->schemaname)
5850 {
5852 false);
5853
5855 }
5856 else
5857 {
5859 }
5860
5861 found = false;
5862 foreach(nslc, namespacelist)
5863 {
5866 ScanKeyData skey[2];
5867 HeapTuple tup;
5868
5869 ScanKeyInit(&skey[0],
5872 CStringGetDatum(constraint->relname));
5873 ScanKeyInit(&skey[1],
5877
5879 true, NULL, 2, skey);
5880
5882 {
5884
5885 if (con->condeferrable)
5886 conoidlist = lappend_oid(conoidlist, con->oid);
5887 else if (stmt->deferred)
5888 ereport(ERROR,
5890 errmsg("constraint \"%s\" is not deferrable",
5891 constraint->relname)));
5892 found = true;
5893 }
5894
5896
5897 /*
5898 * Once we've found a matching constraint we do not search
5899 * later parts of the search path.
5900 */
5901 if (found)
5902 break;
5903 }
5904
5906
5907 /*
5908 * Not found ?
5909 */
5910 if (!found)
5911 ereport(ERROR,
5913 errmsg("constraint \"%s\" does not exist",
5914 constraint->relname)));
5915 }
5916
5917 /*
5918 * Scan for any possible descendants of the constraints. We append
5919 * whatever we find to the same list that we're scanning; this has the
5920 * effect that we create new scans for those, too, so if there are
5921 * further descendents, we'll also catch them.
5922 */
5923 foreach(lc, conoidlist)
5924 {
5925 Oid parent = lfirst_oid(lc);
5927 SysScanDesc scan;
5928 HeapTuple tuple;
5929
5930 ScanKeyInit(&key,
5933 ObjectIdGetDatum(parent));
5934
5935 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5936
5937 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5938 {
5940
5941 conoidlist = lappend_oid(conoidlist, con->oid);
5942 }
5943
5944 systable_endscan(scan);
5945 }
5946
5948
5949 /*
5950 * Now, locate the trigger(s) implementing each of these constraints,
5951 * and make a list of their OIDs.
5952 */
5954
5955 foreach(lc, conoidlist)
5956 {
5957 Oid conoid = lfirst_oid(lc);
5960 HeapTuple htup;
5961
5965 ObjectIdGetDatum(conoid));
5966
5968 NULL, 1, &skey);
5969
5971 {
5973
5974 /*
5975 * Silently skip triggers that are marked as non-deferrable in
5976 * pg_trigger. This is not an error condition, since a
5977 * deferrable RI constraint may have some non-deferrable
5978 * actions.
5979 */
5980 if (pg_trigger->tgdeferrable)
5982 }
5983
5985 }
5986
5988
5989 /*
5990 * Now we can set the trigger states of individual triggers for this
5991 * xact.
5992 */
5993 foreach(lc, tgoidlist)
5994 {
5995 Oid tgoid = lfirst_oid(lc);
5997 bool found = false;
5998 int i;
5999
6000 for (i = 0; i < state->numstates; i++)
6001 {
6002 if (state->trigstates[i].sct_tgoid == tgoid)
6003 {
6004 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
6005 found = true;
6006 break;
6007 }
6008 }
6009 if (!found)
6010 {
6012 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
6013 }
6014 }
6015 }
6016
6017 /*
6018 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
6019 * checks against that constraint must be made when the SET CONSTRAINTS
6020 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
6021 * apply retroactively. We've updated the constraints state, so scan the
6022 * list of previously deferred events to fire any that have now become
6023 * immediate.
6024 *
6025 * Obviously, if this was SET ... DEFERRED then it can't have converted
6026 * any unfired events to immediate, so we need do nothing in that case.
6027 */
6028 if (!stmt->deferred)
6029 {
6031 bool snapshot_set = false;
6032
6033 while (afterTriggerMarkEvents(events, NULL, true))
6034 {
6036
6037 /*
6038 * Make sure a snapshot has been established in case trigger
6039 * functions need one. Note that we avoid setting a snapshot if
6040 * we don't find at least one trigger that has to be fired now.
6041 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6042 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6043 * at the start of a transaction it's not possible for any trigger
6044 * events to be queued yet.)
6045 */
6046 if (!snapshot_set)
6047 {
6049 snapshot_set = true;
6050 }
6051
6052 /*
6053 * We can delete fired events if we are at top transaction level,
6054 * but we'd better not if inside a subtransaction, since the
6055 * subtransaction could later get rolled back.
6056 */
6058 !IsSubTransaction()))
6059 break; /* all fired */
6060 }
6061
6062 if (snapshot_set)
6064 }
6065}
6066
6067/* ----------
6068 * AfterTriggerPendingOnRel()
6069 * Test to see if there are any pending after-trigger events for rel.
6070 *
6071 * This is used by TRUNCATE, CLUSTER, ALTER TABLE, etc to detect whether
6072 * it is unsafe to perform major surgery on a relation. Note that only
6073 * local pending events are examined. We assume that having exclusive lock
6074 * on a rel guarantees there are no unserviced events in other backends ---
6075 * but having a lock does not prevent there being such events in our own.
6076 *
6077 * In some scenarios it'd be reasonable to remove pending events (more
6078 * specifically, mark them DONE by the current subxact) but without a lot
6079 * of knowledge of the trigger semantics we can't do this in general.
6080 * ----------
6081 */
6082bool
6084{
6085 AfterTriggerEvent event;
6087 int depth;
6088
6089 /* Scan queued events */
6091 {
6093
6094 /*
6095 * We can ignore completed events. (Even if a DONE flag is rolled
6096 * back by subxact abort, it's OK because the effects of the TRUNCATE
6097 * or whatever must get rolled back too.)
6098 */
6099 if (event->ate_flags & AFTER_TRIGGER_DONE)
6100 continue;
6101
6102 if (evtshared->ats_relid == relid)
6103 return true;
6104 }
6105
6106 /*
6107 * Also scan events queued by incomplete queries. This could only matter
6108 * if TRUNCATE/etc is executed by a function or trigger within an updating
6109 * query on the same relation, which is pretty perverse, but let's check.
6110 */
6111 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6112 {
6114 {
6116
6117 if (event->ate_flags & AFTER_TRIGGER_DONE)
6118 continue;
6119
6120 if (evtshared->ats_relid == relid)
6121 return true;
6122 }
6123 }
6124
6125 return false;
6126}
6127
6128/* ----------
6129 * AfterTriggerSaveEvent()
6130 *
6131 * Called by ExecA[RS]...Triggers() to queue up the triggers that should
6132 * be fired for an event.
6133 *
6134 * NOTE: this is called whenever there are any triggers associated with
6135 * the event (even if they are disabled). This function decides which
6136 * triggers actually need to be queued. It is also called after each row,
6137 * even if there are no triggers for that event, if there are any AFTER
6138 * STATEMENT triggers for the statement which use transition tables, so that
6139 * the transition tuplestores can be built. Furthermore, if the transition
6140 * capture is happening for UPDATEd rows being moved to another partition due
6141 * to the partition-key being changed, then this function is called once when
6142 * the row is deleted (to capture OLD row), and once when the row is inserted
6143 * into another partition (to capture NEW row). This is done separately because
6144 * DELETE and INSERT happen on different tables.
6145 *
6146 * Transition tuplestores are built now, rather than when events are pulled
6147 * off of the queue because AFTER ROW triggers are allowed to select from the
6148 * transition tables for the statement.
6149 *
6150 * This contains special support to queue the update events for the case where
6151 * a partitioned table undergoing a cross-partition update may have foreign
6152 * keys pointing into it. Normally, a partitioned table's row triggers are
6153 * not fired because the leaf partition(s) which are modified as a result of
6154 * the operation on the partitioned table contain the same triggers which are
6155 * fired instead. But that general scheme can cause problematic behavior with
6156 * foreign key triggers during cross-partition updates, which are implemented
6157 * as DELETE on the source partition followed by INSERT into the destination
6158 * partition. Specifically, firing DELETE triggers would lead to the wrong
6159 * foreign key action to be enforced considering that the original command is
6160 * UPDATE; in this case, this function is called with relinfo as the
6161 * partitioned table, and src_partinfo and dst_partinfo referring to the
6162 * source and target leaf partitions, respectively.
6163 *
6164 * is_crosspart_update is true either when a DELETE event is fired on the
6165 * source partition (which is to be ignored) or an UPDATE event is fired on
6166 * the root partitioned table.
6167 * ----------
6168 */
6169static void
6173 int event, bool row_trigger,
6176 TransitionCaptureState *transition_capture,
6178{
6179 Relation rel = relinfo->ri_RelationDesc;
6180 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
6183 char relkind = rel->rd_rel->relkind;
6184 int tgtype_event;
6185 int tgtype_level;
6186 int i;
6187 Tuplestorestate *fdw_tuplestore = NULL;
6188
6189 /*
6190 * Check state. We use a normal test not Assert because it is possible to
6191 * reach here in the wrong state given misconfigured RI triggers, in
6192 * particular deferring a cascade action trigger.
6193 */
6194 if (afterTriggers.query_depth < 0)
6195 elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
6196
6197 /* Be sure we have enough space to record events at this query depth. */
6200
6201 /*
6202 * If the directly named relation has any triggers with transition tables,
6203 * then we need to capture transition tuples.
6204 */
6205 if (row_trigger && transition_capture != NULL)
6206 {
6208
6209 /*
6210 * Capture the old tuple in the appropriate transition table based on
6211 * the event.
6212 */
6213 if (!TupIsNull(oldslot))
6214 {
6215 Tuplestorestate *old_tuplestore;
6216
6217 old_tuplestore = GetAfterTriggersTransitionTable(event,
6218 oldslot,
6219 NULL,
6220 transition_capture);
6221 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6222 oldslot, NULL, old_tuplestore);
6223 }
6224
6225 /*
6226 * Capture the new tuple in the appropriate transition table based on
6227 * the event.
6228 */
6229 if (!TupIsNull(newslot))
6230 {
6231 Tuplestorestate *new_tuplestore;
6232
6233 new_tuplestore = GetAfterTriggersTransitionTable(event,
6234 NULL,
6235 newslot,
6236 transition_capture);
6237 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6238 newslot, original_insert_tuple, new_tuplestore);
6239 }
6240
6241 /*
6242 * If transition tables are the only reason we're here, return. As
6243 * mentioned above, we can also be here during update tuple routing in
6244 * presence of transition tables, in which case this function is
6245 * called separately for OLD and NEW, so we expect exactly one of them
6246 * to be NULL.
6247 */
6248 if (trigdesc == NULL ||
6249 (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
6250 (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
6251 (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
6253 return;
6254 }
6255
6256 /*
6257 * We normally don't see partitioned tables here for row level triggers
6258 * except in the special case of a cross-partition update. In that case,
6259 * nodeModifyTable.c:ExecCrossPartitionUpdateForeignKey() calls here to
6260 * queue an update event on the root target partitioned table, also
6261 * passing the source and destination partitions and their tuples.
6262 */
6264 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
6266 TRIGGER_FIRED_BY_UPDATE(event) &&
6268
6269 /*
6270 * Validate the event code and collect the associated tuple CTIDs.
6271 *
6272 * The event code will be used both as a bitmask and an array offset, so
6273 * validation is important to make sure we don't walk off the edge of our
6274 * arrays.
6275 *
6276 * Also, if we're considering statement-level triggers, check whether we
6277 * already queued a set of them for this event, and cancel the prior set
6278 * if so. This preserves the behavior that statement-level triggers fire
6279 * just once per statement and fire after row-level triggers.
6280 */
6281 switch (event)
6282 {
6285 if (row_trigger)
6286 {
6287 Assert(oldslot == NULL);
6288 Assert(newslot != NULL);
6289 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
6290 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6291 }
6292 else
6293 {
6294 Assert(oldslot == NULL);
6295 Assert(newslot == NULL);
6296 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6297 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6299 CMD_INSERT, event);
6300 }
6301 break;
6304 if (row_trigger)
6305 {
6306 Assert(oldslot != NULL);
6307 Assert(newslot == NULL);
6308 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6309 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6310 }
6311 else
6312 {
6313 Assert(oldslot == NULL);
6314 Assert(newslot == NULL);
6315 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6316 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6318 CMD_DELETE, event);
6319 }
6320 break;
6323 if (row_trigger)
6324 {
6325 Assert(oldslot != NULL);
6326 Assert(newslot != NULL);
6327 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6328 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
6329
6330 /*
6331 * Also remember the OIDs of partitions to fetch these tuples
6332 * out of later in AfterTriggerExecute().
6333 */
6334 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6335 {
6337 new_event.ate_src_part =
6338 RelationGetRelid(src_partinfo->ri_RelationDesc);
6339 new_event.ate_dst_part =
6340 RelationGetRelid(dst_partinfo->ri_RelationDesc);
6341 }
6342 }
6343 else
6344 {
6345 Assert(oldslot == NULL);
6346 Assert(newslot == NULL);
6347 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6348 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6350 CMD_UPDATE, event);
6351 }
6352 break;
6355 Assert(oldslot == NULL);
6356 Assert(newslot == NULL);
6357 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6358 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6359 break;
6360 default:
6361 elog(ERROR, "invalid after-trigger event code: %d", event);
6362 tgtype_event = 0; /* keep compiler quiet */
6363 break;
6364 }
6365
6366 /* Determine flags */
6367 if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
6368 {
6369 if (row_trigger && event == TRIGGER_EVENT_UPDATE)
6370 {
6371 if (relkind == RELKIND_PARTITIONED_TABLE)
6373 else
6374 new_event.ate_flags = AFTER_TRIGGER_2CTID;
6375 }
6376 else
6377 new_event.ate_flags = AFTER_TRIGGER_1CTID;
6378 }
6379
6380 /* else, we'll initialize ate_flags for each trigger */
6381
6383
6384 /*
6385 * Must convert/copy the source and destination partition tuples into the
6386 * root partitioned table's format/slot, because the processing in the
6387 * loop below expects both oldslot and newslot tuples to be in that form.
6388 */
6389 if (row_trigger && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6390 {
6392 TupleConversionMap *map;
6393
6396 if (map)
6398 oldslot,
6399 rootslot);
6400 else
6402
6405 if (map)
6407 newslot,
6408 rootslot);
6409 else
6411 }
6412
6413 for (i = 0; i < trigdesc->numtriggers; i++)
6414 {
6415 Trigger *trigger = &trigdesc->triggers[i];
6416
6417 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
6420 tgtype_event))
6421 continue;
6422 if (!TriggerEnabled(estate, relinfo, trigger, event,
6424 continue;
6425
6426 if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
6427 {
6428 if (fdw_tuplestore == NULL)
6429 {
6430 fdw_tuplestore = GetCurrentFDWTuplestore();
6432 }
6433 else
6434 /* subsequent event for the same tuple */
6436 }
6437
6438 /*
6439 * If the trigger is a foreign key enforcement trigger, there are
6440 * certain cases where we can skip queueing the event because we can
6441 * tell by inspection that the FK constraint will still pass. There
6442 * are also some cases during cross-partition updates of a partitioned
6443 * table where queuing the event can be skipped.
6444 */
6446 {
6447 switch (RI_FKey_trigger_type(trigger->tgfoid))
6448 {
6449 case RI_TRIGGER_PK:
6450
6451 /*
6452 * For cross-partitioned updates of partitioned PK table,
6453 * skip the event fired by the component delete on the
6454 * source leaf partition unless the constraint originates
6455 * in the partition itself (!tgisclone), because the
6456 * update event that will be fired on the root
6457 * (partitioned) target table will be used to perform the
6458 * necessary foreign key enforcement action.
6459 */
6460 if (is_crosspart_update &&
6461 TRIGGER_FIRED_BY_DELETE(event) &&
6462 trigger->tgisclone)
6463 continue;
6464
6465 /* Update or delete on trigger's PK table */
6467 oldslot, newslot))
6468 {
6469 /* skip queuing this event */
6470 continue;
6471 }
6472 break;
6473
6474 case RI_TRIGGER_FK:
6475
6476 /*
6477 * Update on trigger's FK table. We can skip the update
6478 * event fired on a partitioned table during a
6479 * cross-partition of that table, because the insert event
6480 * that is fired on the destination leaf partition would
6481 * suffice to perform the necessary foreign key check.
6482 * Moreover, RI_FKey_fk_upd_check_required() expects to be
6483 * passed a tuple that contains system attributes, most of
6484 * which are not present in the virtual slot belonging to
6485 * a partitioned table.
6486 */
6487 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
6489 oldslot, newslot))
6490 {
6491 /* skip queuing this event */
6492 continue;
6493 }
6494 break;
6495
6496 case RI_TRIGGER_NONE:
6497
6498 /*
6499 * Not an FK trigger. No need to queue the update event
6500 * fired during a cross-partitioned update of a
6501 * partitioned table, because the same row trigger must be
6502 * present in the leaf partition(s) that are affected as
6503 * part of this update and the events fired on them are
6504 * queued instead.
6505 */
6506 if (row_trigger &&
6507 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6508 continue;
6509 break;
6510 }
6511 }
6512
6513 /*
6514 * If the trigger is a deferred unique constraint check trigger, only
6515 * queue it if the unique constraint was potentially violated, which
6516 * we know from index insertion time.
6517 */
6518 if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
6519 {
6520 if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
6521 continue; /* Uniqueness definitely not violated */
6522 }
6523
6524 /*
6525 * Fill in event structure and add it to the current query's queue.
6526 * Note we set ats_table to NULL whenever this trigger doesn't use
6527 * transition tables, to improve sharability of the shared event data.
6528 */
6529 new_shared.ats_event =
6530 (event & TRIGGER_EVENT_OPMASK) |
6532 (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
6533 (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
6534 new_shared.ats_tgoid = trigger->tgoid;
6535 new_shared.ats_relid = RelationGetRelid(rel);
6536 new_shared.ats_rolid = GetUserId();
6537 new_shared.ats_firing_id = 0;
6538 if ((trigger->tgoldtable || trigger->tgnewtable) &&
6539 transition_capture != NULL)
6540 {
6541 switch (event)
6542 {
6544 new_shared.ats_table = transition_capture->tcs_insert_private;
6545 break;
6547 new_shared.ats_table = transition_capture->tcs_update_private;
6548 break;
6550 new_shared.ats_table = transition_capture->tcs_delete_private;
6551 break;
6552 default:
6553 /* Must be TRUNCATE, see switch above */
6554 new_shared.ats_table = NULL;
6555 break;
6556 }
6557 }
6558 else
6559 new_shared.ats_table = NULL;
6560 new_shared.ats_modifiedcols = modifiedCols;
6561
6564 }
6565
6566 /*
6567 * Finally, spool any foreign tuple(s). The tuplestore squashes them to
6568 * minimal tuples, so this loses any system columns. The executor lost
6569 * those columns before us, for an unrelated reason, so this is fine.
6570 */
6571 if (fdw_tuplestore)
6572 {
6573 if (oldslot != NULL)
6574 tuplestore_puttupleslot(fdw_tuplestore, oldslot);
6575 if (newslot != NULL)
6576 tuplestore_puttupleslot(fdw_tuplestore, newslot);
6577 }
6578}
6579
6580/*
6581 * Detect whether we already queued BEFORE STATEMENT triggers for the given
6582 * relation + operation, and set the flag so the next call will report "true".
6583 */
6584static bool
6586{
6587 bool result;
6589
6590 /* Check state, like AfterTriggerSaveEvent. */
6591 if (afterTriggers.query_depth < 0)
6592 elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6593
6594 /* Be sure we have enough space to record events at this query depth. */
6597
6598 /*
6599 * We keep this state in the AfterTriggersTableData that also holds
6600 * transition tables for the relation + operation. In this way, if we are
6601 * forced to make a new set of transition tables because more tuples get
6602 * entered after we've already fired triggers, we will allow a new set of
6603 * statement triggers to get queued.
6604 */
6605 table = GetAfterTriggersTableData(relid, cmdType);
6606 result = table->before_trig_done;
6607 table->before_trig_done = true;
6608 return result;
6609}
6610
6611/*
6612 * If we previously queued a set of AFTER STATEMENT triggers for the given
6613 * relation + operation, and they've not been fired yet, cancel them. The
6614 * caller will queue a fresh set that's after any row-level triggers that may
6615 * have been queued by the current sub-statement, preserving (as much as
6616 * possible) the property that AFTER ROW triggers fire before AFTER STATEMENT
6617 * triggers, and that the latter only fire once. This deals with the
6618 * situation where several FK enforcement triggers sequentially queue triggers
6619 * for the same table into the same trigger query level. We can't fully
6620 * prevent odd behavior though: if there are AFTER ROW triggers taking
6621 * transition tables, we don't want to change the transition tables once the
6622 * first such trigger has seen them. In such a case, any additional events
6623 * will result in creating new transition tables and allowing new firings of
6624 * statement triggers.
6625 *
6626 * This also saves the current event list location so that a later invocation
6627 * of this function can cheaply find the triggers we're about to queue and
6628 * cancel them.
6629 */
6630static void
6631cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
6632{
6635
6636 /*
6637 * We keep this state in the AfterTriggersTableData that also holds
6638 * transition tables for the relation + operation. In this way, if we are
6639 * forced to make a new set of transition tables because more tuples get
6640 * entered after we've already fired triggers, we will allow a new set of
6641 * statement triggers to get queued without canceling the old ones.
6642 */
6643 table = GetAfterTriggersTableData(relid, cmdType);
6644
6645 if (table->after_trig_done)
6646 {
6647 /*
6648 * We want to start scanning from the tail location that existed just
6649 * before we inserted any statement triggers. But the events list
6650 * might've been entirely empty then, in which case scan from the
6651 * current head.
6652 */
6653 AfterTriggerEvent event;
6655
6656 if (table->after_trig_events.tail)
6657 {
6658 chunk = table->after_trig_events.tail;
6659 event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6660 }
6661 else
6662 {
6663 chunk = qs->events.head;
6664 event = NULL;
6665 }
6666
6667 for_each_chunk_from(chunk)
6668 {
6669 if (event == NULL)
6670 event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6671 for_each_event_from(event, chunk)
6672 {
6674
6675 /*
6676 * Exit loop when we reach events that aren't AS triggers for
6677 * the target relation.
6678 */
6679 if (evtshared->ats_relid != relid)
6680 goto done;
6681 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6682 goto done;
6683 if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6684 goto done;
6685 if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6686 goto done;
6687 /* OK, mark it DONE */
6688 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6689 event->ate_flags |= AFTER_TRIGGER_DONE;
6690 }
6691 /* signal we must reinitialize event ptr for next chunk */
6692 event = NULL;
6693 }
6694 }
6695done:
6696
6697 /* In any case, save current insertion point for next time */
6698 table->after_trig_done = true;
6699 table->after_trig_events = qs->events;
6700}
6701
6702/*
6703 * GUC assign_hook for session_replication_role
6704 */
6705void
6707{
6708 /*
6709 * Must flush the plan cache when changing replication role; but don't
6710 * flush unnecessarily.
6711 */
6714}
6715
6716/*
6717 * SQL function pg_trigger_depth()
6718 */
6719Datum
6721{
6723}
6724
6725/*
6726 * Check whether a trigger modified a virtual generated column and replace the
6727 * value with null if so.
6728 *
6729 * We need to check this so that we don't end up storing a non-null value in a
6730 * virtual generated column.
6731 *
6732 * We don't need to check for stored generated columns, since those will be
6733 * overwritten later anyway.
6734 */
6735static HeapTuple
6737{
6738 if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
6739 return tuple;
6740
6741 for (int i = 0; i < tupdesc->natts; i++)
6742 {
6743 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6744 {
6745 if (!heap_attisnull(tuple, i + 1, tupdesc))
6746 {
6747 int replCol = i + 1;
6748 Datum replValue = 0;
6749 bool replIsnull = true;
6750
6751 tuple = heap_modify_tuple_by_cols(tuple, tupdesc, 1, &replCol, &replValue, &replIsnull);
6752 }
6753 }
6754 }
6755
6756 return tuple;
6757}
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:1093
#define Max(x, y)
Definition c.h:1087
#define Assert(condition)
Definition c.h:945
#define pg_fallthrough
Definition c.h:152
uint32 CommandId
Definition c.h:752
#define OidIsValid(objectId)
Definition c.h:860
size_t Size
Definition c.h:691
int errcode(int sqlerrcode)
Definition elog.c:874
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid, ResultRelInfo *rootRelInfo)
Definition execMain.c:1362
void ExecCloseResultRelations(EState *estate)
Definition execMain.c:1594
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:1231
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition execUtils.c:1305
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition execUtils.c:1209
void FreeExecutorState(EState *estate)
Definition execUtils.c:197
EState * CreateExecutorState(void)
Definition execUtils.c:90
#define palloc0_object(type)
Definition fe_memutils.h:75
#define PG_RETURN_INT32(x)
Definition fmgr.h:355
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:514
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:131
Oid MyDatabaseId
Definition globals.c:94
#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:1198
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition heaptuple.c:456
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1384
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
#define stmt
void InstrStartNode(Instrumentation *instr)
Definition instrument.c:68
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition instrument.c:88
int i
Definition isn.c:77
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition itemptr.h:184
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition itemptr.h:172
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition itemptr.h:83
List * lappend(List *list, void *datum)
Definition list.c:339
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
void list_free(List *list)
Definition list.c:1546
bool list_member_oid(const List *list, Oid datum)
Definition list.c:722
void list_free_deep(List *list)
Definition list.c:1560
#define AccessShareLock
Definition lockdefs.h:36
char * get_database_name(Oid dbid)
Definition lsyscache.c:1312
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1232
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:403
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1266
MemoryContext TopTransactionContext
Definition mcxt.c:171
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
void pfree(void *pointer)
Definition mcxt.c:1616
MemoryContext CurTransactionContext
Definition mcxt.c:172
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define SECURITY_LOCAL_USERID_CHANGE
Definition miscadmin.h:318
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:613
bool InSecurityRestrictedOperation(void)
Definition miscinit.c:640
Oid GetUserId(void)
Definition miscinit.c:470
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:620
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition namespace.c:3457
List * fetch_search_path(bool includeImplicit)
Definition namespace.c:4891
CmdType
Definition nodes.h:273
@ CMD_MERGE
Definition nodes.h:279
@ CMD_INSERT
Definition nodes.h:277
@ CMD_DELETE
Definition nodes.h:278
@ CMD_UPDATE
Definition nodes.h:276
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
#define list_make1_oid(x1)
Definition pg_list.h:242
#define lfirst_oid(lc)
Definition pg_list.h:174
static const struct lconv_member_info table[]
END_CATALOG_STRUCT typedef FormData_pg_trigger * Form_pg_trigger
Definition pg_trigger.h:84
void ResetPlanCache(void)
Definition plancache.c:2328
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Datum CStringGetDatum(const char *X)
Definition postgres.h:370
unsigned int Oid
static int fb(int x)
#define RelationHasReferenceCountZero(relation)
Definition rel.h:498
#define RelationGetRelid(relation)
Definition rel.h:514
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
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
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:3901
AfterTriggerEventList events
Definition trigger.c:3900
Tuplestorestate * new_tuplestore
Definition trigger.c:3927
TupleTableSlot * storeslot
Definition trigger.c:3929
Tuplestorestate * old_tuplestore
Definition trigger.c:3925
AfterTriggerEventList after_trig_events
Definition trigger.c:3922
AfterTriggerEventList events
Definition trigger.c:3909
SetConstraintState state
Definition trigger.c:3908
CommandId firing_counter
Definition trigger.c:3911
List * es_tupleTable
Definition execnodes.h:724
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:1274
static SetConstraintState SetConstraintStateCopy(SetConstraintState origstate)
Definition trigger.c:5718
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition trigger.c:6631
static AfterTriggersData afterTriggers
Definition trigger.c:3932
#define AFTER_TRIGGER_FDW_FETCH
Definition trigger.c:3688
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition trigger.c:5738
#define AFTER_TRIGGER_IN_PROGRESS
Definition trigger.c:3685
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition trigger.c:4959
void AfterTriggerBeginXact(void)
Definition trigger.c:5085
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition trigger.c:4267
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition trigger.c:4868
static Bitmapset * afterTriggerCopyBitmap(Bitmapset *src)
Definition trigger.c:4054
#define CHUNK_DATA_START(cptr)
Definition trigger.c:3772
void AfterTriggerEndSubXact(bool isCommit)
Definition trigger.c:5440
#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
Datum pg_trigger_depth(PG_FUNCTION_ARGS)
Definition trigger.c:6720
#define MAX_CHUNK_SIZE
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition trigger.c:5228
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition trigger.c:2311
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition trigger.c:4206
#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:4615
static Tuplestorestate * GetAfterTriggersTransitionTable(int event, TupleTableSlot *oldslot, TupleTableSlot *newslot, TransitionCaptureState *transition_capture)
Definition trigger.c:5537
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition trigger.c:4699
struct AfterTriggerSharedData * AfterTriggerShared
Definition trigger.c:3693
void AfterTriggerSetState(ConstraintsSetStmt *stmt)
Definition trigger.c:5768
static bool before_stmt_triggers_fired(Oid relid, CmdType cmdType)
Definition trigger.c:6585
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition trigger.c:4079
#define AFTER_TRIGGER_2CTID
Definition trigger.c:3690
#define SizeofTriggerEvent(evt)
Definition trigger.c:3744
int SessionReplicationRole
Definition trigger.c:65
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition trigger.c:4009
static void AfterTriggerExecute(EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, ResultRelInfo *src_relInfo, ResultRelInfo *dst_relInfo, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
Definition trigger.c:4329
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition trigger.c:5693
void AfterTriggerFireDeferred(void)
Definition trigger.c:5288
static void TransitionTableAddTuple(EState *estate, int event, TransitionCaptureState *transition_capture, ResultRelInfo *relinfo, TupleTableSlot *slot, TupleTableSlot *original_insert_tuple, Tuplestorestate *tuplestore)
Definition trigger.c:5588
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition trigger.c:4227
#define for_each_event_from(eptr, cptr)
Definition trigger.c:3796
#define MIN_CHUNK_SIZE
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition trigger.c:3973
void assign_session_replication_role(int newval, void *extra)
Definition trigger.c:6706
void AfterTriggerEndXact(bool isCommit)
Definition trigger.c:5344
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6083
#define AFTER_TRIGGER_FDW_REUSE
Definition trigger.c:3687
void AfterTriggerBeginSubXact(void)
Definition trigger.c:5392
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:6170
static HeapTuple check_modified_virtual_generated(TupleDesc tupdesc, HeapTuple tuple)
Definition trigger.c:6736
static TupleTableSlot * GetAfterTriggersStoreSlot(AfterTriggersTableData *table, TupleDesc tupdesc)
Definition trigger.c:4910
#define AFTER_TRIGGER_CP_UPDATE
Definition trigger.c:3691
void AfterTriggerEndQuery(EState *estate)
Definition trigger.c:5137
#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:5646
#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:5117
#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
#define TRIGGER_EVENT_INSERT
Definition trigger.h:94
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition trigger.h:118
#define TRIGGER_EVENT_TRUNCATE
Definition trigger.h:97
#define RI_TRIGGER_PK
Definition trigger.h:286
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition tupconvert.c:193
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition tupdesc.c:242
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition tuplestore.c:743
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition tuplestore.c:331
void tuplestore_end(Tuplestorestate *state)
Definition tuplestore.c:493
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
#define TupIsNull(slot)
Definition tuptable.h:325
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:543
int GetCurrentTransactionNestLevel(void)
Definition xact.c:931
bool IsSubTransaction(void)
Definition xact.c:5067

◆ 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

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

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

References afterTriggerCopyBitmap(), afterTriggers, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, bms_equal(), CHUNK_DATA_START, AfterTriggersData::event_cxt, fb(), AfterTriggerEventList::head, MAX_CHUNK_SIZE, 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 5117 of file trigger.c.

5118{
5119 /* Increase the query stack depth */
5121}

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5392 of file trigger.c.

5393{
5394 int my_level = GetCurrentTransactionNestLevel();
5395
5396 /*
5397 * Allocate more space in the trans_stack if needed. (Note: because the
5398 * minimum nest level of a subtransaction is 2, we waste the first couple
5399 * entries of the array; not worth the notational effort to avoid it.)
5400 */
5401 while (my_level >= afterTriggers.maxtransdepth)
5402 {
5404 {
5405 /* Arbitrarily initialize for max of 8 subtransaction levels */
5408 8 * sizeof(AfterTriggersTransData));
5410 }
5411 else
5412 {
5413 /* repalloc will keep the stack in the same context */
5415
5420 }
5421 }
5422
5423 /*
5424 * Push the current information into the stack. The SET CONSTRAINTS state
5425 * is not saved until/unless changed. Likewise, we don't make a
5426 * per-subtransaction event context until needed.
5427 */
5428 afterTriggers.trans_stack[my_level].state = NULL;
5432}

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

5086{
5087 /*
5088 * Initialize after-trigger state structure to empty
5089 */
5090 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5092
5093 /*
5094 * Verify that there is no leftover state remaining. If these assertions
5095 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5096 * up properly.
5097 */
5105}

References afterTriggers, Assert, AfterTriggersData::event_cxt, AfterTriggersData::events, fb(), AfterTriggersData::firing_counter, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, 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 4009 of file trigger.c.

4010{
4011 Oid tgoid = evtshared->ats_tgoid;
4013 int i;
4014
4015 /*
4016 * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
4017 * constraints declared NOT DEFERRABLE), the state is always false.
4018 */
4019 if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
4020 return false;
4021
4022 /*
4023 * If constraint state exists, SET CONSTRAINTS might have been executed
4024 * either for this trigger or for all triggers.
4025 */
4026 if (state != NULL)
4027 {
4028 /* Check for SET CONSTRAINTS for this specific trigger. */
4029 for (i = 0; i < state->numstates; i++)
4030 {
4031 if (state->trigstates[i].sct_tgoid == tgoid)
4032 return state->trigstates[i].sct_tgisdeferred;
4033 }
4034
4035 /* Check for SET CONSTRAINTS ALL. */
4036 if (state->all_isset)
4037 return state->all_isdeferred;
4038 }
4039
4040 /*
4041 * Otherwise return the default state for the trigger.
4042 */
4043 return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
4044}

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

4055{
4056 Bitmapset *dst;
4058
4059 if (src == NULL)
4060 return NULL;
4061
4063
4064 dst = bms_copy(src);
4065
4067
4068 return dst;
4069}

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

Referenced by afterTriggerAddEvent().

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 4267 of file trigger.c.

4268{
4269 AfterTriggerEventChunk *target = qs->events.head;
4270 ListCell *lc;
4271
4272 Assert(target && target->next);
4273
4274 /*
4275 * First, update any pointers in the per-table data, so that they won't be
4276 * dangling. Resetting obsoleted pointers to NULL will make
4277 * cancel_prior_stmt_triggers start from the list head, which is fine.
4278 */
4279 foreach(lc, qs->tables)
4280 {
4282
4283 if (table->after_trig_done &&
4284 table->after_trig_events.tail == target)
4285 {
4286 table->after_trig_events.head = NULL;
4287 table->after_trig_events.tail = NULL;
4288 table->after_trig_events.tailfree = NULL;
4289 }
4290 }
4291
4292 /* Now we can flush the head chunk */
4293 qs->events.head = target->next;
4294 pfree(target);
4295}

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

Referenced by AfterTriggerEndQuery().

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 5137 of file trigger.c.

5138{
5140
5141 /* Must be inside a query, too */
5143
5144 /*
5145 * If we never even got as far as initializing the event stack, there
5146 * certainly won't be any events, so exit quickly.
5147 */
5149 {
5151 return;
5152 }
5153
5154 /*
5155 * Process all immediate-mode triggers queued by the query, and move the
5156 * deferred ones to the main list of deferred events.
5157 *
5158 * Notice that we decide which ones will be fired, and put the deferred
5159 * ones on the main list, before anything is actually fired. This ensures
5160 * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5161 * IMMEDIATE: all events we have decided to defer will be available for it
5162 * to fire.
5163 *
5164 * We loop in case a trigger queues more events at the same query level.
5165 * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5166 * will instead fire any triggers in a dedicated query level. Foreign key
5167 * enforcement triggers do add to the current query level, thanks to their
5168 * passing fire_triggers = false to SPI_execute_snapshot(). Other
5169 * C-language triggers might do likewise.
5170 *
5171 * If we find no firable events, we don't have to increment
5172 * firing_counter.
5173 */
5175
5176 for (;;)
5177 {
5178 if (afterTriggerMarkEvents(&qs->events, &afterTriggers.events, true))
5179 {
5181 AfterTriggerEventChunk *oldtail = qs->events.tail;
5182
5183 if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5184 break; /* all fired */
5185
5186 /*
5187 * Firing a trigger could result in query_stack being repalloc'd,
5188 * so we must recalculate qs after each afterTriggerInvokeEvents
5189 * call. Furthermore, it's unsafe to pass delete_ok = true here,
5190 * because that could cause afterTriggerInvokeEvents to try to
5191 * access qs->events after the stack has been repalloc'd.
5192 */
5194
5195 /*
5196 * We'll need to scan the events list again. To reduce the cost
5197 * of doing so, get rid of completely-fired chunks. We know that
5198 * all events were marked IN_PROGRESS or DONE at the conclusion of
5199 * afterTriggerMarkEvents, so any still-interesting events must
5200 * have been added after that, and so must be in the chunk that
5201 * was then the tail chunk, or in later chunks. So, zap all
5202 * chunks before oldtail. This is approximately the same set of
5203 * events we would have gotten rid of by passing delete_ok = true.
5204 */
5205 Assert(oldtail != NULL);
5206 while (qs->events.head != oldtail)
5208 }
5209 else
5210 break;
5211 }
5212
5213 /* Release query-level-local storage, including tuplestores if any */
5215
5217}

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

Definition at line 5440 of file trigger.c.

5441{
5442 int my_level = GetCurrentTransactionNestLevel();
5444 AfterTriggerEvent event;
5447
5448 /*
5449 * Pop the prior state if needed.
5450 */
5451 if (isCommit)
5452 {
5454 /* If we saved a prior state, we don't need it anymore */
5456 if (state != NULL)
5457 pfree(state);
5458 /* this avoids double pfree if error later: */
5459 afterTriggers.trans_stack[my_level].state = NULL;
5462 }
5463 else
5464 {
5465 /*
5466 * Aborting. It is possible subxact start failed before calling
5467 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5468 * trans_stack levels that aren't there.
5469 */
5470 if (my_level >= afterTriggers.maxtransdepth)
5471 return;
5472
5473 /*
5474 * Release query-level storage for queries being aborted, and restore
5475 * query_depth to its pre-subxact value. This assumes that a
5476 * subtransaction will not add events to query levels started in a
5477 * earlier transaction state.
5478 */
5480 {
5484 }
5487
5488 /*
5489 * Restore the global deferred-event list to its former length,
5490 * discarding any events queued by the subxact.
5491 */
5493 &afterTriggers.trans_stack[my_level].events);
5494
5495 /*
5496 * Restore the trigger state. If the saved state is NULL, then this
5497 * subxact didn't save it, so it doesn't need restoring.
5498 */
5500 if (state != NULL)
5501 {
5504 }
5505 /* this avoids double pfree if error later: */
5506 afterTriggers.trans_stack[my_level].state = NULL;
5507
5508 /*
5509 * Scan for any remaining deferred events that were marked DONE or IN
5510 * PROGRESS by this subxact or a child, and un-mark them. We can
5511 * recognize such events because they have a firing ID greater than or
5512 * equal to the firing_counter value we saved at subtransaction start.
5513 * (This essentially assumes that the current subxact includes all
5514 * subxacts started after it.)
5515 */
5518 {
5520
5521 if (event->ate_flags &
5523 {
5524 if (evtshared->ats_firing_id >= subxact_firing_id)
5525 event->ate_flags &=
5527 }
5528 }
5529 }
5530}

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AfterTriggerFreeQuery(), afterTriggerRestoreEventList(), afterTriggers, Assert, AfterTriggerEventData::ate_flags, AfterTriggersData::events, AfterTriggersTransData::events, fb(), 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 5344 of file trigger.c.

5345{
5346 /*
5347 * Forget the pending-events list.
5348 *
5349 * Since all the info is in TopTransactionContext or children thereof, we
5350 * don't really need to do anything to reclaim memory. However, the
5351 * pending-events list could be large, and so it's useful to discard it as
5352 * soon as possible --- especially if we are aborting because we ran out
5353 * of memory for the list!
5354 */
5356 {
5362 }
5363
5364 /*
5365 * Forget any subtransaction state as well. Since this can't be very
5366 * large, we let the eventual reset of TopTransactionContext free the
5367 * memory instead of doing it here.
5368 */
5371
5372
5373 /*
5374 * Forget the query stack and constraint-related state information. As
5375 * with the subtransaction state information, we don't bother freeing the
5376 * memory here.
5377 */
5381
5382 /* No more afterTriggers manipulation until next transaction starts. */
5384}

References afterTriggers, AfterTriggersData::event_cxt, AfterTriggersData::events, fb(), AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, MemoryContextDelete(), 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 5646 of file trigger.c.

5647{
5649
5651
5653 {
5654 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5655
5660 }
5661 else
5662 {
5663 /* repalloc will keep the stack in the same context */
5666 old_alloc * 2);
5667
5672 }
5673
5674 /* Initialize new array entries to empty */
5676 {
5678
5679 qs->events.head = NULL;
5680 qs->events.tail = NULL;
5681 qs->events.tailfree = NULL;
5682 qs->fdw_tuplestore = NULL;
5683 qs->tables = NIL;
5684
5685 ++init_depth;
5686 }
5687}

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,
Instrumentation instr,
MemoryContext  per_tuple_context,
TupleTableSlot trig_tuple_slot1,
TupleTableSlot trig_tuple_slot2 
)
static

Definition at line 4329 of file trigger.c.

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

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(), InstrStartNode(), InstrStopNode(), 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 5288 of file trigger.c.

5289{
5290 AfterTriggerEventList *events;
5291 bool snap_pushed = false;
5292
5293 /* Must not be inside a query */
5295
5296 /*
5297 * If there are any triggers to fire, make sure we have set a snapshot for
5298 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5299 * can't assume ActiveSnapshot is valid on entry.)
5300 */
5301 events = &afterTriggers.events;
5302 if (events->head != NULL)
5303 {
5305 snap_pushed = true;
5306 }
5307
5308 /*
5309 * Run all the remaining triggers. Loop until they are all gone, in case
5310 * some trigger queues more for us to do.
5311 */
5312 while (afterTriggerMarkEvents(events, NULL, false))
5313 {
5315
5316 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5317 break; /* all fired */
5318 }
5319
5320 /*
5321 * We don't bother freeing the event list, since it will go away anyway
5322 * (and more efficiently than via pfree) in AfterTriggerEndXact.
5323 */
5324
5325 if (snap_pushed)
5327}

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

Referenced by CommitTransaction(), and PrepareTransaction().

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4206 of file trigger.c.

4207{
4209
4210 while ((chunk = events->head) != NULL)
4211 {
4212 events->head = chunk->next;
4213 pfree(chunk);
4214 }
4215 events->tail = NULL;
4216 events->tailfree = NULL;
4217}

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

5229{
5230 Tuplestorestate *ts;
5231 List *tables;
5232 ListCell *lc;
5233
5234 /* Drop the trigger events */
5235 afterTriggerFreeEventList(&qs->events);
5236
5237 /* Drop FDW tuplestore if any */
5238 ts = qs->fdw_tuplestore;
5239 qs->fdw_tuplestore = NULL;
5240 if (ts)
5241 tuplestore_end(ts);
5242
5243 /* Release per-table subsidiary storage */
5244 tables = qs->tables;
5245 foreach(lc, tables)
5246 {
5248
5249 ts = table->old_tuplestore;
5250 table->old_tuplestore = NULL;
5251 if (ts)
5252 tuplestore_end(ts);
5253 ts = table->new_tuplestore;
5254 table->new_tuplestore = NULL;
5255 if (ts)
5256 tuplestore_end(ts);
5257 if (table->storeslot)
5258 {
5259 TupleTableSlot *slot = table->storeslot;
5260
5261 table->storeslot = NULL;
5263 }
5264 }
5265
5266 /*
5267 * Now free the AfterTriggersTableData structs and list cells. Reset list
5268 * pointer first; if list_free_deep somehow gets an error, better to leak
5269 * that storage than have an infinite loop.
5270 */
5271 qs->tables = NIL;
5272 list_free_deep(tables);
5273}

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

4703{
4704 bool all_fired = true;
4707 bool local_estate = false;
4709 Relation rel = NULL;
4710 TriggerDesc *trigdesc = NULL;
4711 FmgrInfo *finfo = NULL;
4712 Instrumentation *instr = NULL;
4714 *slot2 = NULL;
4715
4716 /* Make a local EState if need be */
4717 if (estate == NULL)
4718 {
4719 estate = CreateExecutorState();
4720 local_estate = true;
4721 }
4722
4723 /* Make a per-tuple memory context for trigger function calls */
4726 "AfterTriggerTupleContext",
4728
4729 for_each_chunk(chunk, *events)
4730 {
4731 AfterTriggerEvent event;
4732 bool all_fired_in_chunk = true;
4733
4734 for_each_event(event, chunk)
4735 {
4737
4738 /*
4739 * Is it one for me to fire?
4740 */
4741 if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4742 evtshared->ats_firing_id == firing_id)
4743 {
4745 *dst_rInfo;
4746
4747 /*
4748 * So let's fire it... but first, find the correct relation if
4749 * this is not the same relation as before.
4750 */
4751 if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4752 {
4753 rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4754 NULL);
4755 rel = rInfo->ri_RelationDesc;
4756 /* Catch calls with insufficient relcache refcounting */
4758 trigdesc = rInfo->ri_TrigDesc;
4759 /* caution: trigdesc could be NULL here */
4760 finfo = rInfo->ri_TrigFunctions;
4761 instr = rInfo->ri_TrigInstrument;
4762 if (slot1 != NULL)
4763 {
4766 slot1 = slot2 = NULL;
4767 }
4768 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4769 {
4774 }
4775 }
4776
4777 /*
4778 * Look up source and destination partition result rels of a
4779 * cross-partition update event.
4780 */
4781 if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4783 {
4784 Assert(OidIsValid(event->ate_src_part) &&
4785 OidIsValid(event->ate_dst_part));
4787 event->ate_src_part,
4788 rInfo);
4790 event->ate_dst_part,
4791 rInfo);
4792 }
4793 else
4795
4796 /*
4797 * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4798 * still set, so recursive examinations of the event list
4799 * won't try to re-fire it.
4800 */
4801 AfterTriggerExecute(estate, event, rInfo,
4803 trigdesc, finfo, instr,
4805
4806 /*
4807 * Mark the event as done.
4808 */
4809 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4810 event->ate_flags |= AFTER_TRIGGER_DONE;
4811 }
4812 else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4813 {
4814 /* something remains to be done */
4815 all_fired = all_fired_in_chunk = false;
4816 }
4817 }
4818
4819 /* Clear the chunk if delete_ok and nothing left of interest */
4821 {
4822 chunk->freeptr = CHUNK_DATA_START(chunk);
4823 chunk->endfree = chunk->endptr;
4824
4825 /*
4826 * If it's last chunk, must sync event list's tailfree too. Note
4827 * that delete_ok must NOT be passed as true if there could be
4828 * additional AfterTriggerEventList values pointing at this event
4829 * list, since we'd fail to fix their copies of tailfree.
4830 */
4831 if (chunk == events->tail)
4832 events->tailfree = chunk->freeptr;
4833 }
4834 }
4835 if (slot1 != NULL)
4836 {
4839 }
4840
4841 /* Release working resources */
4843
4844 if (local_estate)
4845 {
4847 ExecResetTupleTable(estate->es_tupleTable, false);
4848 FreeExecutorState(estate);
4849 }
4850
4851 return all_fired;
4852}

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().

◆ afterTriggerMarkEvents()

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

Definition at line 4615 of file trigger.c.

4618{
4619 bool found = false;
4620 bool deferred_found = false;
4621 AfterTriggerEvent event;
4623
4624 for_each_event_chunk(event, chunk, *events)
4625 {
4627 bool defer_it = false;
4628
4629 if (!(event->ate_flags &
4631 {
4632 /*
4633 * This trigger hasn't been called or scheduled yet. Check if we
4634 * should call it now.
4635 */
4637 {
4638 defer_it = true;
4639 }
4640 else
4641 {
4642 /*
4643 * Mark it as to be fired in this firing cycle.
4644 */
4645 evtshared->ats_firing_id = afterTriggers.firing_counter;
4646 event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4647 found = true;
4648 }
4649 }
4650
4651 /*
4652 * If it's deferred, move it to move_list, if requested.
4653 */
4654 if (defer_it && move_list != NULL)
4655 {
4656 deferred_found = true;
4657 /* add it to move_list */
4659 /* mark original copy "done" so we don't do it again */
4660 event->ate_flags |= AFTER_TRIGGER_DONE;
4661 }
4662 }
4663
4664 /*
4665 * We could allow deferred triggers if, before the end of the
4666 * security-restricted operation, we were to verify that a SET CONSTRAINTS
4667 * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4668 */
4670 ereport(ERROR,
4672 errmsg("cannot fire deferred trigger within security-restricted operation")));
4673
4674 return found;
4675}

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

6084{
6085 AfterTriggerEvent event;
6087 int depth;
6088
6089 /* Scan queued events */
6091 {
6093
6094 /*
6095 * We can ignore completed events. (Even if a DONE flag is rolled
6096 * back by subxact abort, it's OK because the effects of the TRUNCATE
6097 * or whatever must get rolled back too.)
6098 */
6099 if (event->ate_flags & AFTER_TRIGGER_DONE)
6100 continue;
6101
6102 if (evtshared->ats_relid == relid)
6103 return true;
6104 }
6105
6106 /*
6107 * Also scan events queued by incomplete queries. This could only matter
6108 * if TRUNCATE/etc is executed by a function or trigger within an updating
6109 * query on the same relation, which is pretty perverse, but let's check.
6110 */
6111 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6112 {
6114 {
6116
6117 if (event->ate_flags & AFTER_TRIGGER_DONE)
6118 continue;
6119
6120 if (evtshared->ats_relid == relid)
6121 return true;
6122 }
6123 }
6124
6125 return false;
6126}

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

4229{
4232
4233 if (old_events->tail == NULL)
4234 {
4235 /* restoring to a completely empty state, so free everything */
4237 }
4238 else
4239 {
4240 *events = *old_events;
4241 /* free any chunks after the last one we want to keep */
4242 for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4243 {
4244 next_chunk = chunk->next;
4245 pfree(chunk);
4246 }
4247 /* and clean up the tail chunk to be the right length */
4248 events->tail->next = NULL;
4249 events->tail->freeptr = events->tailfree;
4250
4251 /*
4252 * We don't make any effort to remove now-unused shared data records.
4253 * They might still be useful, anyway.
4254 */
4255 }
4256}

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

6178{
6179 Relation rel = relinfo->ri_RelationDesc;
6180 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
6183 char relkind = rel->rd_rel->relkind;
6184 int tgtype_event;
6185 int tgtype_level;
6186 int i;
6187 Tuplestorestate *fdw_tuplestore = NULL;
6188
6189 /*
6190 * Check state. We use a normal test not Assert because it is possible to
6191 * reach here in the wrong state given misconfigured RI triggers, in
6192 * particular deferring a cascade action trigger.
6193 */
6194 if (afterTriggers.query_depth < 0)
6195 elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
6196
6197 /* Be sure we have enough space to record events at this query depth. */
6200
6201 /*
6202 * If the directly named relation has any triggers with transition tables,
6203 * then we need to capture transition tuples.
6204 */
6205 if (row_trigger && transition_capture != NULL)
6206 {
6208
6209 /*
6210 * Capture the old tuple in the appropriate transition table based on
6211 * the event.
6212 */
6213 if (!TupIsNull(oldslot))
6214 {
6215 Tuplestorestate *old_tuplestore;
6216
6217 old_tuplestore = GetAfterTriggersTransitionTable(event,
6218 oldslot,
6219 NULL,
6220 transition_capture);
6221 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6222 oldslot, NULL, old_tuplestore);
6223 }
6224
6225 /*
6226 * Capture the new tuple in the appropriate transition table based on
6227 * the event.
6228 */
6229 if (!TupIsNull(newslot))
6230 {
6231 Tuplestorestate *new_tuplestore;
6232
6233 new_tuplestore = GetAfterTriggersTransitionTable(event,
6234 NULL,
6235 newslot,
6236 transition_capture);
6237 TransitionTableAddTuple(estate, event, transition_capture, relinfo,
6238 newslot, original_insert_tuple, new_tuplestore);
6239 }
6240
6241 /*
6242 * If transition tables are the only reason we're here, return. As
6243 * mentioned above, we can also be here during update tuple routing in
6244 * presence of transition tables, in which case this function is
6245 * called separately for OLD and NEW, so we expect exactly one of them
6246 * to be NULL.
6247 */
6248 if (trigdesc == NULL ||
6249 (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
6250 (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
6251 (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
6253 return;
6254 }
6255
6256 /*
6257 * We normally don't see partitioned tables here for row level triggers
6258 * except in the special case of a cross-partition update. In that case,
6259 * nodeModifyTable.c:ExecCrossPartitionUpdateForeignKey() calls here to
6260 * queue an update event on the root target partitioned table, also
6261 * passing the source and destination partitions and their tuples.
6262 */
6264 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
6266 TRIGGER_FIRED_BY_UPDATE(event) &&
6268
6269 /*
6270 * Validate the event code and collect the associated tuple CTIDs.
6271 *
6272 * The event code will be used both as a bitmask and an array offset, so
6273 * validation is important to make sure we don't walk off the edge of our
6274 * arrays.
6275 *
6276 * Also, if we're considering statement-level triggers, check whether we
6277 * already queued a set of them for this event, and cancel the prior set
6278 * if so. This preserves the behavior that statement-level triggers fire
6279 * just once per statement and fire after row-level triggers.
6280 */
6281 switch (event)
6282 {
6285 if (row_trigger)
6286 {
6287 Assert(oldslot == NULL);
6288 Assert(newslot != NULL);
6289 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
6290 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6291 }
6292 else
6293 {
6294 Assert(oldslot == NULL);
6295 Assert(newslot == NULL);
6296 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6297 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6299 CMD_INSERT, event);
6300 }
6301 break;
6304 if (row_trigger)
6305 {
6306 Assert(oldslot != NULL);
6307 Assert(newslot == NULL);
6308 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6309 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6310 }
6311 else
6312 {
6313 Assert(oldslot == NULL);
6314 Assert(newslot == NULL);
6315 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6316 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6318 CMD_DELETE, event);
6319 }
6320 break;
6323 if (row_trigger)
6324 {
6325 Assert(oldslot != NULL);
6326 Assert(newslot != NULL);
6327 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6328 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
6329
6330 /*
6331 * Also remember the OIDs of partitions to fetch these tuples
6332 * out of later in AfterTriggerExecute().
6333 */
6334 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6335 {
6337 new_event.ate_src_part =
6338 RelationGetRelid(src_partinfo->ri_RelationDesc);
6339 new_event.ate_dst_part =
6340 RelationGetRelid(dst_partinfo->ri_RelationDesc);
6341 }
6342 }
6343 else
6344 {
6345 Assert(oldslot == NULL);
6346 Assert(newslot == NULL);
6347 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6348 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6350 CMD_UPDATE, event);
6351 }
6352 break;
6355 Assert(oldslot == NULL);
6356 Assert(newslot == NULL);
6357 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6358 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6359 break;
6360 default:
6361 elog(ERROR, "invalid after-trigger event code: %d", event);
6362 tgtype_event = 0; /* keep compiler quiet */
6363 break;
6364 }
6365
6366 /* Determine flags */
6367 if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
6368 {
6369 if (row_trigger && event == TRIGGER_EVENT_UPDATE)
6370 {
6371 if (relkind == RELKIND_PARTITIONED_TABLE)
6373 else
6374 new_event.ate_flags = AFTER_TRIGGER_2CTID;
6375 }
6376 else
6377 new_event.ate_flags = AFTER_TRIGGER_1CTID;
6378 }
6379
6380 /* else, we'll initialize ate_flags for each trigger */
6381
6383
6384 /*
6385 * Must convert/copy the source and destination partition tuples into the
6386 * root partitioned table's format/slot, because the processing in the
6387 * loop below expects both oldslot and newslot tuples to be in that form.
6388 */
6389 if (row_trigger && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6390 {
6392 TupleConversionMap *map;
6393
6396 if (map)
6398 oldslot,
6399 rootslot);
6400 else
6402
6405 if (map)
6407 newslot,
6408 rootslot);
6409 else
6411 }
6412
6413 for (i = 0; i < trigdesc->numtriggers; i++)
6414 {
6415 Trigger *trigger = &trigdesc->triggers[i];
6416
6417 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
6420 tgtype_event))
6421 continue;
6422 if (!TriggerEnabled(estate, relinfo, trigger, event,
6424 continue;
6425
6426 if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
6427 {
6428 if (fdw_tuplestore == NULL)
6429 {
6430 fdw_tuplestore = GetCurrentFDWTuplestore();
6432 }
6433 else
6434 /* subsequent event for the same tuple */
6436 }
6437
6438 /*
6439 * If the trigger is a foreign key enforcement trigger, there are
6440 * certain cases where we can skip queueing the event because we can
6441 * tell by inspection that the FK constraint will still pass. There
6442 * are also some cases during cross-partition updates of a partitioned
6443 * table where queuing the event can be skipped.
6444 */
6446 {
6447 switch (RI_FKey_trigger_type(trigger->tgfoid))
6448 {
6449 case RI_TRIGGER_PK:
6450
6451 /*
6452 * For cross-partitioned updates of partitioned PK table,
6453 * skip the event fired by the component delete on the
6454 * source leaf partition unless the constraint originates
6455 * in the partition itself (!tgisclone), because the
6456 * update event that will be fired on the root
6457 * (partitioned) target table will be used to perform the
6458 * necessary foreign key enforcement action.
6459 */
6460 if (is_crosspart_update &&
6461 TRIGGER_FIRED_BY_DELETE(event) &&
6462 trigger->tgisclone)
6463 continue;
6464
6465 /* Update or delete on trigger's PK table */
6467 oldslot, newslot))
6468 {
6469 /* skip queuing this event */
6470 continue;
6471 }
6472 break;
6473
6474 case RI_TRIGGER_FK:
6475
6476 /*
6477 * Update on trigger's FK table. We can skip the update
6478 * event fired on a partitioned table during a
6479 * cross-partition of that table, because the insert event
6480 * that is fired on the destination leaf partition would
6481 * suffice to perform the necessary foreign key check.
6482 * Moreover, RI_FKey_fk_upd_check_required() expects to be
6483 * passed a tuple that contains system attributes, most of
6484 * which are not present in the virtual slot belonging to
6485 * a partitioned table.
6486 */
6487 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
6489 oldslot, newslot))
6490 {
6491 /* skip queuing this event */
6492 continue;
6493 }
6494 break;
6495
6496 case RI_TRIGGER_NONE:
6497
6498 /*
6499 * Not an FK trigger. No need to queue the update event
6500 * fired during a cross-partitioned update of a
6501 * partitioned table, because the same row trigger must be
6502 * present in the leaf partition(s) that are affected as
6503 * part of this update and the events fired on them are
6504 * queued instead.
6505 */
6506 if (row_trigger &&
6507 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6508 continue;
6509 break;
6510 }
6511 }
6512
6513 /*
6514 * If the trigger is a deferred unique constraint check trigger, only
6515 * queue it if the unique constraint was potentially violated, which
6516 * we know from index insertion time.
6517 */
6518 if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
6519 {
6520 if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
6521 continue; /* Uniqueness definitely not violated */
6522 }
6523
6524 /*
6525 * Fill in event structure and add it to the current query's queue.
6526 * Note we set ats_table to NULL whenever this trigger doesn't use
6527 * transition tables, to improve sharability of the shared event data.
6528 */
6529 new_shared.ats_event =
6530 (event & TRIGGER_EVENT_OPMASK) |
6532 (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
6533 (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
6534 new_shared.ats_tgoid = trigger->tgoid;
6535 new_shared.ats_relid = RelationGetRelid(rel);
6536 new_shared.ats_rolid = GetUserId();
6537 new_shared.ats_firing_id = 0;
6538 if ((trigger->tgoldtable || trigger->tgnewtable) &&
6539 transition_capture != NULL)
6540 {
6541 switch (event)
6542 {
6544 new_shared.ats_table = transition_capture->tcs_insert_private;
6545 break;
6547 new_shared.ats_table = transition_capture->tcs_update_private;
6548 break;
6550 new_shared.ats_table = transition_capture->tcs_delete_private;
6551 break;
6552 default:
6553 /* Must be TRUNCATE, see switch above */
6554 new_shared.ats_table = NULL;
6555 break;
6556 }
6557 }
6558 else
6559 new_shared.ats_table = NULL;
6560 new_shared.ats_modifiedcols = modifiedCols;
6561
6564 }
6565
6566 /*
6567 * Finally, spool any foreign tuple(s). The tuplestore squashes them to
6568 * minimal tuples, so this loses any system columns. The executor lost
6569 * those columns before us, for an unrelated reason, so this is fine.
6570 */
6571 if (fdw_tuplestore)
6572 {
6573 if (oldslot != NULL)
6574 tuplestore_puttupleslot(fdw_tuplestore, oldslot);
6575 if (newslot != NULL)
6576 tuplestore_puttupleslot(fdw_tuplestore, newslot);
6577 }
6578}

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

5769{
5770 int my_level = GetCurrentTransactionNestLevel();
5771
5772 /* If we haven't already done so, initialize our state. */
5773 if (afterTriggers.state == NULL)
5775
5776 /*
5777 * If in a subtransaction, and we didn't save the current state already,
5778 * save it so it can be restored if the subtransaction aborts.
5779 */
5780 if (my_level > 1 &&
5781 afterTriggers.trans_stack[my_level].state == NULL)
5782 {
5783 afterTriggers.trans_stack[my_level].state =
5785 }
5786
5787 /*
5788 * Handle SET CONSTRAINTS ALL ...
5789 */
5790 if (stmt->constraints == NIL)
5791 {
5792 /*
5793 * Forget any previous SET CONSTRAINTS commands in this transaction.
5794 */
5796
5797 /*
5798 * Set the per-transaction ALL state to known.
5799 */
5802 }
5803 else
5804 {
5807 List *conoidlist = NIL;
5808 List *tgoidlist = NIL;
5809 ListCell *lc;
5810
5811 /*
5812 * Handle SET CONSTRAINTS constraint-name [, ...]
5813 *
5814 * First, identify all the named constraints and make a list of their
5815 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5816 * the same name within a schema, the specifications are not
5817 * necessarily unique. Our strategy is to target all matching
5818 * constraints within the first search-path schema that has any
5819 * matches, but disregard matches in schemas beyond the first match.
5820 * (This is a bit odd but it's the historical behavior.)
5821 *
5822 * A constraint in a partitioned table may have corresponding
5823 * constraints in the partitions. Grab those too.
5824 */
5826
5827 foreach(lc, stmt->constraints)
5828 {
5829 RangeVar *constraint = lfirst(lc);
5830 bool found;
5832 ListCell *nslc;
5833
5834 if (constraint->catalogname)
5835 {
5836 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5837 ereport(ERROR,
5839 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5840 constraint->catalogname, constraint->schemaname,
5841 constraint->relname)));
5842 }
5843
5844 /*
5845 * If we're given the schema name with the constraint, look only
5846 * in that schema. If given a bare constraint name, use the
5847 * search path to find the first matching constraint.
5848 */
5849 if (constraint->schemaname)
5850 {
5852 false);
5853
5855 }
5856 else
5857 {
5859 }
5860
5861 found = false;
5862 foreach(nslc, namespacelist)
5863 {
5866 ScanKeyData skey[2];
5867 HeapTuple tup;
5868
5869 ScanKeyInit(&skey[0],
5872 CStringGetDatum(constraint->relname));
5873 ScanKeyInit(&skey[1],
5877
5879 true, NULL, 2, skey);
5880
5882 {
5884
5885 if (con->condeferrable)
5886 conoidlist = lappend_oid(conoidlist, con->oid);
5887 else if (stmt->deferred)
5888 ereport(ERROR,
5890 errmsg("constraint \"%s\" is not deferrable",
5891 constraint->relname)));
5892 found = true;
5893 }
5894
5896
5897 /*
5898 * Once we've found a matching constraint we do not search
5899 * later parts of the search path.
5900 */
5901 if (found)
5902 break;
5903 }
5904
5906
5907 /*
5908 * Not found ?
5909 */
5910 if (!found)
5911 ereport(ERROR,
5913 errmsg("constraint \"%s\" does not exist",
5914 constraint->relname)));
5915 }
5916
5917 /*
5918 * Scan for any possible descendants of the constraints. We append
5919 * whatever we find to the same list that we're scanning; this has the
5920 * effect that we create new scans for those, too, so if there are
5921 * further descendents, we'll also catch them.
5922 */
5923 foreach(lc, conoidlist)
5924 {
5925 Oid parent = lfirst_oid(lc);
5927 SysScanDesc scan;
5928 HeapTuple tuple;
5929
5930 ScanKeyInit(&key,
5933 ObjectIdGetDatum(parent));
5934
5935 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5936
5937 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5938 {
5940
5941 conoidlist = lappend_oid(conoidlist, con->oid);
5942 }
5943
5944 systable_endscan(scan);
5945 }
5946
5948
5949 /*
5950 * Now, locate the trigger(s) implementing each of these constraints,
5951 * and make a list of their OIDs.
5952 */
5954
5955 foreach(lc, conoidlist)
5956 {
5957 Oid conoid = lfirst_oid(lc);
5960 HeapTuple htup;
5961
5965 ObjectIdGetDatum(conoid));
5966
5968 NULL, 1, &skey);
5969
5971 {
5973
5974 /*
5975 * Silently skip triggers that are marked as non-deferrable in
5976 * pg_trigger. This is not an error condition, since a
5977 * deferrable RI constraint may have some non-deferrable
5978 * actions.
5979 */
5980 if (pg_trigger->tgdeferrable)
5982 }
5983
5985 }
5986
5988
5989 /*
5990 * Now we can set the trigger states of individual triggers for this
5991 * xact.
5992 */
5993 foreach(lc, tgoidlist)
5994 {
5995 Oid tgoid = lfirst_oid(lc);
5997 bool found = false;
5998 int i;
5999
6000 for (i = 0; i < state->numstates; i++)
6001 {
6002 if (state->trigstates[i].sct_tgoid == tgoid)
6003 {
6004 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
6005 found = true;
6006 break;
6007 }
6008 }
6009 if (!found)
6010 {
6012 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
6013 }
6014 }
6015 }
6016
6017 /*
6018 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
6019 * checks against that constraint must be made when the SET CONSTRAINTS
6020 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
6021 * apply retroactively. We've updated the constraints state, so scan the
6022 * list of previously deferred events to fire any that have now become
6023 * immediate.
6024 *
6025 * Obviously, if this was SET ... DEFERRED then it can't have converted
6026 * any unfired events to immediate, so we need do nothing in that case.
6027 */
6028 if (!stmt->deferred)
6029 {
6031 bool snapshot_set = false;
6032
6033 while (afterTriggerMarkEvents(events, NULL, true))
6034 {
6036
6037 /*
6038 * Make sure a snapshot has been established in case trigger
6039 * functions need one. Note that we avoid setting a snapshot if
6040 * we don't find at least one trigger that has to be fired now.
6041 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6042 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6043 * at the start of a transaction it's not possible for any trigger
6044 * events to be queued yet.)
6045 */
6046 if (!snapshot_set)
6047 {
6049 snapshot_set = true;
6050 }
6051
6052 /*
6053 * We can delete fired events if we are at top transaction level,
6054 * but we'd better not if inside a subtransaction, since the
6055 * subtransaction could later get rolled back.
6056 */
6058 !IsSubTransaction()))
6059 break; /* all fired */
6060 }
6061
6062 if (snapshot_set)
6064 }
6065}

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, BTEqualStrategyNumber, RangeVar::catalogname, CStringGetDatum(), ereport, errcode(), errmsg, ERROR, AfterTriggersData::events, fb(), fetch_search_path(), AfterTriggersData::firing_counter, Form_pg_constraint, Form_pg_trigger, get_database_name(), GetCurrentTransactionNestLevel(), GETSTRUCT(), GetTransactionSnapshot(), HeapTupleIsValid, i, IsSubTransaction(), lappend_oid(), lfirst, lfirst_oid, list_free(), 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 6706 of file trigger.c.

6707{
6708 /*
6709 * Must flush the plan cache when changing replication role; but don't
6710 * flush unnecessarily.
6711 */
6714}

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6585 of file trigger.c.

6586{
6587 bool result;
6589
6590 /* Check state, like AfterTriggerSaveEvent. */
6591 if (afterTriggers.query_depth < 0)
6592 elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6593
6594 /* Be sure we have enough space to record events at this query depth. */
6597
6598 /*
6599 * We keep this state in the AfterTriggersTableData that also holds
6600 * transition tables for the relation + operation. In this way, if we are
6601 * forced to make a new set of transition tables because more tuples get
6602 * entered after we've already fired triggers, we will allow a new set of
6603 * statement triggers to get queued.
6604 */
6605 table = GetAfterTriggersTableData(relid, cmdType);
6606 result = table->before_trig_done;
6607 table->before_trig_done = true;
6608 return result;
6609}

References AfterTriggerEnlargeQueryState(), afterTriggers, elog, ERROR, GetAfterTriggersTableData(), AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, 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 6631 of file trigger.c.

6632{
6635
6636 /*
6637 * We keep this state in the AfterTriggersTableData that also holds
6638 * transition tables for the relation + operation. In this way, if we are
6639 * forced to make a new set of transition tables because more tuples get
6640 * entered after we've already fired triggers, we will allow a new set of
6641 * statement triggers to get queued without canceling the old ones.
6642 */
6643 table = GetAfterTriggersTableData(relid, cmdType);
6644
6645 if (table->after_trig_done)
6646 {
6647 /*
6648 * We want to start scanning from the tail location that existed just
6649 * before we inserted any statement triggers. But the events list
6650 * might've been entirely empty then, in which case scan from the
6651 * current head.
6652 */
6653 AfterTriggerEvent event;
6655
6656 if (table->after_trig_events.tail)
6657 {
6658 chunk = table->after_trig_events.tail;
6659 event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6660 }
6661 else
6662 {
6663 chunk = qs->events.head;
6664 event = NULL;
6665 }
6666
6667 for_each_chunk_from(chunk)
6668 {
6669 if (event == NULL)
6670 event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6671 for_each_event_from(event, chunk)
6672 {
6674
6675 /*
6676 * Exit loop when we reach events that aren't AS triggers for
6677 * the target relation.
6678 */
6679 if (evtshared->ats_relid != relid)
6680 goto done;
6681 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6682 goto done;
6683 if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6684 goto done;
6685 if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6686 goto done;
6687 /* OK, mark it DONE */
6688 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6689 event->ate_flags |= AFTER_TRIGGER_DONE;
6690 }
6691 /* signal we must reinitialize event ptr for next chunk */
6692 event = NULL;
6693 }
6694 }
6695done:
6696
6697 /* In any case, save current insertion point for next time */
6698 table->after_trig_done = true;
6699 table->after_trig_events = qs->events;
6700}

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

6737{
6738 if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
6739 return tuple;
6740
6741 for (int i = 0; i < tupdesc->natts; i++)
6742 {
6743 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6744 {
6745 if (!heap_attisnull(tuple, i + 1, tupdesc))
6746 {
6747 int replCol = i + 1;
6748 Datum replValue = 0;
6749 bool replIsnull = true;
6750
6751 tuple = heap_modify_tuple_by_cols(tuple, tupdesc, 1, &replCol, &replValue, &replIsnull);
6752 }
6753 }
6754 }
6755
6756 return tuple;
6757}

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:613
#define palloc_object(type)
Definition fe_memutils.h:74
int j
Definition isn.c:78
char * pstrdup(const char *in)
Definition mcxt.c:1781
void * palloc(Size size)
Definition mcxt.c:1387

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

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

◆ CreateTrigger()

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

Definition at line 162 of file trigger.c.

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

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

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

◆ CreateTriggerFiringOn()

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

Definition at line 179 of file trigger.c.

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

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

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

◆ EnableDisableTrigger()

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

Definition at line 1728 of file trigger.c.

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

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:665
#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, 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:1875
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3588
NodeTag type
Definition trigger.h:33

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

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

◆ ExecBRUpdateTriggers()

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

Definition at line 2973 of file trigger.c.

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

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

Referenced by ExecSimpleRelationUpdate(), and ExecUpdatePrologue().

◆ ExecBSDeleteTriggers()

void ExecBSDeleteTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2632 of file trigger.c.

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

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

Referenced by fireBSTriggers().

◆ ExecBSInsertTriggers()

void ExecBSInsertTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2403 of file trigger.c.

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

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

Referenced by CopyFrom(), and fireBSTriggers().

◆ ExecBSTruncateTriggers()

void ExecBSTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 3282 of file trigger.c.

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

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

Referenced by ExecuteTruncateGuts().

◆ ExecBSUpdateTriggers()

void ExecBSUpdateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2897 of file trigger.c.

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

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

Referenced by fireBSTriggers().

◆ ExecCallTriggerFunc()

static HeapTuple ExecCallTriggerFunc ( TriggerData trigdata,
int  tgindx,
FmgrInfo finfo,
Instrumentation 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 InstrStartNode(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 * one "tuple returned" (really the number of firings).
2395 */
2396 if (instr)
2397 InstrStopNode(instr + tgindx, 1);
2398
2399 return (HeapTuple) DatumGetPointer(result);
2400}
#define PG_TRY(...)
Definition elog.h:372
#define PG_END_TRY(...)
Definition elog.h:397
#define PG_FINALLY(...)
Definition elog.h:389
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, InstrStartNode(), InstrStopNode(), InvalidOid, LOCAL_FCINFO, MemoryContextSwitchTo(), MyTriggerDepth, PG_END_TRY, PG_FINALLY, PG_TRY, pgstat_end_function_usage(), pgstat_init_function_usage(), 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().

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

4912{
4913 /* Create it if not already done. */
4914 if (!table->storeslot)
4915 {
4917
4918 /*
4919 * We need this slot only until AfterTriggerEndQuery, but making it
4920 * last till end-of-subxact is good enough. It'll be freed by
4921 * AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4922 * a different lifespan, so we'd better make a copy of that.
4923 */
4925 tupdesc = CreateTupleDescCopy(tupdesc);
4926 table->storeslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
4928 }
4929
4930 return table->storeslot;
4931}

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

Referenced by TransitionTableAddTuple().

◆ GetAfterTriggersTableData()

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

Definition at line 4868 of file trigger.c.

4869{
4873 ListCell *lc;
4874
4875 /* At this level, cmdType should not be, eg, CMD_MERGE */
4876 Assert(cmdType == CMD_INSERT ||
4877 cmdType == CMD_UPDATE ||
4878 cmdType == CMD_DELETE);
4879
4880 /* Caller should have ensured query_depth is OK. */
4884
4885 foreach(lc, qs->tables)
4886 {
4888 if (table->relid == relid && table->cmdType == cmdType &&
4889 !table->closed)
4890 return table;
4891 }
4892
4894
4896 table->relid = relid;
4897 table->cmdType = cmdType;
4898 qs->tables = lappend(qs->tables, table);
4899
4901
4902 return table;
4903}

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

5541{
5542 Tuplestorestate *tuplestore = NULL;
5543 bool delete_old_table = transition_capture->tcs_delete_old_table;
5544 bool update_old_table = transition_capture->tcs_update_old_table;
5545 bool update_new_table = transition_capture->tcs_update_new_table;
5546 bool insert_new_table = transition_capture->tcs_insert_new_table;
5547
5548 /*
5549 * For INSERT events NEW should be non-NULL, for DELETE events OLD should
5550 * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
5551 * non-NULL. But for UPDATE events fired for capturing transition tuples
5552 * during UPDATE partition-key row movement, OLD is NULL when the event is
5553 * for a row being inserted, whereas NEW is NULL when the event is for a
5554 * row being deleted.
5555 */
5557 TupIsNull(oldslot)));
5559 TupIsNull(newslot)));
5560
5561 if (!TupIsNull(oldslot))
5562 {
5564 if (event == TRIGGER_EVENT_DELETE && delete_old_table)
5565 tuplestore = transition_capture->tcs_delete_private->old_tuplestore;
5566 else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
5567 tuplestore = transition_capture->tcs_update_private->old_tuplestore;
5568 }
5569 else if (!TupIsNull(newslot))
5570 {
5572 if (event == TRIGGER_EVENT_INSERT && insert_new_table)
5573 tuplestore = transition_capture->tcs_insert_private->new_tuplestore;
5574 else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
5575 tuplestore = transition_capture->tcs_update_private->new_tuplestore;
5576 }
5577
5578 return tuplestore;
5579}

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:2668
@ 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:694
Snapshot es_snapshot
Definition execnodes.h:672
CommandId cmax
Definition tableam.h:151
TM_Result
Definition tableam.h:73
@ TM_Ok
Definition tableam.h:78
@ TM_Deleted
Definition tableam.h:93
@ TM_Updated
Definition tableam.h:90
@ TM_SelfModified
Definition tableam.h:84
@ TM_Invisible
Definition tableam.h:81
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:1570
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition tableam.h:267
#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 4959 of file trigger.c.

4960{
4962 bool need_old_upd,
4971
4972 if (trigdesc == NULL)
4973 return NULL;
4974
4975 /* Detect which table(s) we need. */
4976 switch (cmdType)
4977 {
4978 case CMD_INSERT:
4981 break;
4982 case CMD_UPDATE:
4985 need_old_del = need_new_ins = false;
4986 break;
4987 case CMD_DELETE:
4990 break;
4991 case CMD_MERGE:
4996 break;
4997 default:
4998 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
4999 /* keep compiler quiet */
5001 break;
5002 }
5004 return NULL;
5005
5006 /* Check state, like AfterTriggerSaveEvent. */
5007 if (afterTriggers.query_depth < 0)
5008 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
5009
5010 /* Be sure we have enough space to record events at this query depth. */
5013
5014 /*
5015 * Find or create AfterTriggersTableData struct(s) to hold the
5016 * tuplestore(s). If there's a matching struct but it's marked closed,
5017 * ignore it; we need a newer one.
5018 *
5019 * Note: MERGE must use the same AfterTriggersTableData structs as INSERT,
5020 * UPDATE, and DELETE, so that any MERGE'd tuples are added to the same
5021 * tuplestores as tuples from any INSERT, UPDATE, or DELETE commands
5022 * running in the same top-level command (e.g., in a writable CTE).
5023 *
5024 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
5025 * allocated in the current (sub)transaction's CurTransactionContext, and
5026 * the tuplestores are managed by the (sub)transaction's resource owner.
5027 * This is sufficient lifespan because we do not allow triggers using
5028 * transition tables to be deferrable; they will be fired during
5029 * AfterTriggerEndQuery, after which it's okay to delete the data.
5030 */
5031 if (need_new_ins)
5033 else
5034 ins_table = NULL;
5035
5038 else
5039 upd_table = NULL;
5040
5041 if (need_old_del)
5043 else
5044 del_table = NULL;
5045
5046 /* Now create required tuplestore(s), if we don't have them already. */
5050
5051 if (need_old_upd && upd_table->old_tuplestore == NULL)
5052 upd_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5053 if (need_new_upd && upd_table->new_tuplestore == NULL)
5054 upd_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5055 if (need_old_del && del_table->old_tuplestore == NULL)
5056 del_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5057 if (need_new_ins && ins_table->new_tuplestore == NULL)
5058 ins_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5059
5062
5063 /* Now build the TransitionCaptureState struct, in caller's context */
5065 state->tcs_delete_old_table = need_old_del;
5066 state->tcs_update_old_table = need_old_upd;
5067 state->tcs_update_new_table = need_new_upd;
5068 state->tcs_insert_new_table = need_new_ins;
5069 state->tcs_insert_private = ins_table;
5070 state->tcs_update_private = upd_table;
5071 state->tcs_delete_private = del_table;
5072
5073 return state;
5074}

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

6721{
6723}

References MyTriggerDepth, and PG_RETURN_INT32.

◆ RangeVarCallbackForRenameTrigger()

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

Definition at line 1422 of file trigger.c.

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

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().

◆ RelationBuildTriggers()

void RelationBuildTriggers ( Relation  relation)

Definition at line 1863 of file trigger.c.

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

5740{
5741 if (state->numstates >= state->numalloc)
5742 {
5743 int newalloc = state->numalloc * 2;
5744
5745 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5748 offsetof(SetConstraintStateData, trigstates) +
5750 state->numalloc = newalloc;
5751 Assert(state->numstates < state->numalloc);
5752 }
5753
5754 state->trigstates[state->numstates].sct_tgoid = tgoid;
5755 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5756 state->numstates++;
5757
5758 return state;
5759}

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCopy()

static SetConstraintState SetConstraintStateCopy ( SetConstraintState  origstate)
static

Definition at line 5718 of file trigger.c.

5719{
5721
5723
5724 state->all_isset = origstate->all_isset;
5725 state->all_isdeferred = origstate->all_isdeferred;
5726 state->numstates = origstate->numstates;
5727 memcpy(state->trigstates, origstate->trigstates,
5728 origstate->numstates * sizeof(SetConstraintTriggerData));
5729
5730 return state;
5731}

References fb(), and SetConstraintStateCreate().

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCreate()

static SetConstraintState SetConstraintStateCreate ( int  numalloc)
static

Definition at line 5693 of file trigger.c.

5694{
5696
5697 /* Behave sanely with numalloc == 0 */
5698 if (numalloc <= 0)
5699 numalloc = 1;
5700
5701 /*
5702 * We assume that zeroing will correctly initialize the state values.
5703 */
5706 offsetof(SetConstraintStateData, trigstates) +
5707 numalloc * sizeof(SetConstraintTriggerData));
5708
5709 state->numalloc = numalloc;
5710
5711 return state;
5712}

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

5595{
5596 TupleConversionMap *map;
5597
5598 /*
5599 * Nothing needs to be done if we don't have a tuplestore.
5600 */
5601 if (tuplestore == NULL)
5602 return;
5603
5606 else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5607 {
5609 TupleTableSlot *storeslot;
5610
5611 switch (event)
5612 {
5614 table = transition_capture->tcs_insert_private;
5615 break;
5617 table = transition_capture->tcs_update_private;
5618 break;
5620 table = transition_capture->tcs_delete_private;
5621 break;
5622 default:
5623 elog(ERROR, "invalid after-trigger event code: %d", event);
5624 table = NULL; /* keep compiler quiet */
5625 break;
5626 }
5627
5628 storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5629 execute_attr_map_slot(map->attrMap, slot, storeslot);
5630 tuplestore_puttupleslot(tuplestore, storeslot);
5631 }
5632 else
5633 tuplestore_puttupleslot(tuplestore, slot);
5634}

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:660
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition executor.h:522
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:722
TupleTableSlot * ecxt_innertuple
Definition execnodes.h:286
TupleTableSlot * ecxt_outertuple
Definition execnodes.h:288
#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