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/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 "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 (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 (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 3687 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3688 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3689 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3682 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3686 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3685 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3683 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3681 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3690 of file trigger.c.

◆ CHUNK_DATA_START

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

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

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

◆ for_each_chunk_from

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

Definition at line 3792 of file trigger.c.

◆ for_each_event

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

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

◆ for_each_event_from

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

Definition at line 3794 of file trigger.c.

◆ GetTriggerSharedData

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

Definition at line 3751 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

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

◆ AfterTriggersTableData

◆ AfterTriggersTransData

◆ SetConstraintState

◆ SetConstraintStateData

◆ SetConstraintTrigger

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3679 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 4077 of file trigger.c.

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

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

5116{
5117 /* Increase the query stack depth */
5119}

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5390 of file trigger.c.

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

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

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

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

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

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

4053{
4054 Bitmapset *dst;
4056
4057 if (src == NULL)
4058 return NULL;
4059
4061
4062 dst = bms_copy(src);
4063
4065
4066 return dst;
4067}

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

Referenced by afterTriggerAddEvent().

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 4265 of file trigger.c.

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

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

Referenced by AfterTriggerEndQuery().

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 5135 of file trigger.c.

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

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

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

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

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

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

5645{
5647
5649
5651 {
5652 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5653
5658 }
5659 else
5660 {
5661 /* repalloc will keep the stack in the same context */
5664 old_alloc * 2);
5665
5670 }
5671
5672 /* Initialize new array entries to empty */
5674 {
5676
5677 qs->events.head = NULL;
5678 qs->events.tail = NULL;
5679 qs->events.tailfree = NULL;
5680 qs->fdw_tuplestore = NULL;
5681 qs->tables = NIL;
5682
5683 ++init_depth;
5684 }
5685}

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

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

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

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

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

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

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

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

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

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

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, EState::es_tupleTable, ExecCloseResultRelations(), ExecDropSingleTupleTableSlot(), ExecGetTriggerResultRel(), ExecResetTupleTable(), fb(), for_each_chunk, for_each_event, FreeExecutorState(), 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 4613 of file trigger.c.

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

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

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

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

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

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

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

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

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

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

6705{
6706 /*
6707 * Must flush the plan cache when changing replication role; but don't
6708 * flush unnecessarily.
6709 */
6712}

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6583 of file trigger.c.

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

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

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

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

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

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

2091{
2094 int i;
2095
2096 if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2097 return NULL;
2098
2100 memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2101
2102 trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2103 memcpy(trigger, trigdesc->triggers,
2104 trigdesc->numtriggers * sizeof(Trigger));
2105 newdesc->triggers = trigger;
2106
2107 for (i = 0; i < trigdesc->numtriggers; i++)
2108 {
2109 trigger->tgname = pstrdup(trigger->tgname);
2110 if (trigger->tgnattr > 0)
2111 {
2112 int16 *newattr;
2113
2114 newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2115 memcpy(newattr, trigger->tgattr,
2116 trigger->tgnattr * sizeof(int16));
2117 trigger->tgattr = newattr;
2118 }
2119 if (trigger->tgnargs > 0)
2120 {
2121 char **newargs;
2122 int16 j;
2123
2124 newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2125 for (j = 0; j < trigger->tgnargs; j++)
2126 newargs[j] = pstrdup(trigger->tgargs[j]);
2127 trigger->tgargs = newargs;
2128 }
2129 if (trigger->tgqual)
2130 trigger->tgqual = pstrdup(trigger->tgqual);
2131 if (trigger->tgoldtable)
2132 trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2133 if (trigger->tgnewtable)
2134 trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2135 trigger++;
2136 }
2137
2138 return newdesc;
2139}
int16_t int16
Definition c.h:541
#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 ( 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 160 of file trigger.c.

164{
165 return
166 CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
167 constraintOid, indexOid, funcoid,
168 parentTriggerOid, whenClause, isInternal,
170}
ObjectAddress CreateTriggerFiringOn(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:177
#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 ( 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 177 of file trigger.c.

182{
183 int16 tgtype;
184 int ncolumns;
185 int16 *columns;
186 int2vector *tgattr;
188 char *qual;
190 bool nulls[Natts_pg_trigger];
191 Relation rel;
195 HeapTuple tuple = NULL;
199 char *trigname;
203 char *oldtablename = NULL;
204 char *newtablename = NULL;
206 bool trigger_exists = false;
208 bool existing_isInternal = false;
209 bool existing_isClone = false;
210
211 if (OidIsValid(relOid))
212 rel = table_open(relOid, ShareRowExclusiveLock);
213 else
214 rel = table_openrv(stmt->relation, ShareRowExclusiveLock);
215
216 /*
217 * Triggers must be on tables or views, and there are additional
218 * relation-type-specific restrictions.
219 */
220 if (rel->rd_rel->relkind == RELKIND_RELATION)
221 {
222 /* Tables can't have INSTEAD OF triggers */
223 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
224 stmt->timing != TRIGGER_TYPE_AFTER)
227 errmsg("\"%s\" is a table",
229 errdetail("Tables cannot have INSTEAD OF triggers.")));
230 }
231 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
232 {
233 /* Partitioned tables can't have INSTEAD OF triggers */
234 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
235 stmt->timing != TRIGGER_TYPE_AFTER)
238 errmsg("\"%s\" is a table",
240 errdetail("Tables cannot have INSTEAD OF triggers.")));
241
242 /*
243 * FOR EACH ROW triggers have further restrictions
244 */
245 if (stmt->row)
246 {
247 /*
248 * Disallow use of transition tables.
249 *
250 * Note that we have another restriction about transition tables
251 * in partitions; search for 'has_superclass' below for an
252 * explanation. The check here is just to protect from the fact
253 * that if we allowed it here, the creation would succeed for a
254 * partitioned table with no partitions, but would be blocked by
255 * the other restriction when the first partition was created,
256 * which is very unfriendly behavior.
257 */
258 if (stmt->transitionRels != NIL)
261 errmsg("\"%s\" is a partitioned table",
263 errdetail("ROW triggers with transition tables are not supported on partitioned tables.")));
264 }
265 }
266 else if (rel->rd_rel->relkind == RELKIND_VIEW)
267 {
268 /*
269 * Views can have INSTEAD OF triggers (which we check below are
270 * row-level), or statement-level BEFORE/AFTER triggers.
271 */
272 if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row)
275 errmsg("\"%s\" is a view",
277 errdetail("Views cannot have row-level BEFORE or AFTER triggers.")));
278 /* Disallow TRUNCATE triggers on VIEWs */
279 if (TRIGGER_FOR_TRUNCATE(stmt->events))
282 errmsg("\"%s\" is a view",
284 errdetail("Views cannot have TRUNCATE triggers.")));
285 }
286 else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
287 {
288 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
289 stmt->timing != TRIGGER_TYPE_AFTER)
292 errmsg("\"%s\" is a foreign table",
294 errdetail("Foreign tables cannot have INSTEAD OF triggers.")));
295
296 /*
297 * We disallow constraint triggers to protect the assumption that
298 * triggers on FKs can't be deferred. See notes with AfterTriggers
299 * data structures, below.
300 */
301 if (stmt->isconstraint)
304 errmsg("\"%s\" is a foreign table",
306 errdetail("Foreign tables cannot have constraint triggers.")));
307 }
308 else
311 errmsg("relation \"%s\" cannot have triggers",
314
318 errmsg("permission denied: \"%s\" is a system catalog",
320
321 if (stmt->isconstraint)
322 {
323 /*
324 * We must take a lock on the target relation to protect against
325 * concurrent drop. It's not clear that AccessShareLock is strong
326 * enough, but we certainly need at least that much... otherwise, we
327 * might end up creating a pg_constraint entry referencing a
328 * nonexistent table.
329 */
331 {
334 }
335 else if (stmt->constrrel != NULL)
337 false);
338 }
339
340 /* permission checks */
341 if (!isInternal)
342 {
345 if (aclresult != ACLCHECK_OK)
348
350 {
353 if (aclresult != ACLCHECK_OK)
356 }
357 }
358
359 /*
360 * When called on a partitioned table to create a FOR EACH ROW trigger
361 * that's not internal, we create one trigger for each partition, too.
362 *
363 * For that, we'd better hold lock on all of them ahead of time.
364 */
365 partition_recurse = !isInternal && stmt->row &&
366 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
370
371 /* Compute tgtype */
372 TRIGGER_CLEAR_TYPE(tgtype);
373 if (stmt->row)
374 TRIGGER_SETT_ROW(tgtype);
375 tgtype |= stmt->timing;
376 tgtype |= stmt->events;
377
378 /* Disallow ROW-level TRUNCATE triggers */
379 if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
382 errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
383
384 /* INSTEAD triggers must be row-level, and can't have WHEN or columns */
385 if (TRIGGER_FOR_INSTEAD(tgtype))
386 {
387 if (!TRIGGER_FOR_ROW(tgtype))
390 errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
391 if (stmt->whenClause)
394 errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
395 if (stmt->columns != NIL)
398 errmsg("INSTEAD OF triggers cannot have column lists")));
399 }
400
401 /*
402 * We don't yet support naming ROW transition variables, but the parser
403 * recognizes the syntax so we can give a nicer message here.
404 *
405 * Per standard, REFERENCING TABLE names are only allowed on AFTER
406 * triggers. Per standard, REFERENCING ROW names are not allowed with FOR
407 * EACH STATEMENT. Per standard, each OLD/NEW, ROW/TABLE permutation is
408 * only allowed once. Per standard, OLD may not be specified when
409 * creating a trigger only for INSERT, and NEW may not be specified when
410 * creating a trigger only for DELETE.
411 *
412 * Notice that the standard allows an AFTER ... FOR EACH ROW trigger to
413 * reference both ROW and TABLE transition data.
414 */
415 if (stmt->transitionRels != NIL)
416 {
417 List *varList = stmt->transitionRels;
418 ListCell *lc;
419
420 foreach(lc, varList)
421 {
423
424 if (!(tt->isTable))
427 errmsg("ROW variable naming in the REFERENCING clause is not supported"),
428 errhint("Use OLD TABLE or NEW TABLE for naming transition tables.")));
429
430 /*
431 * Because of the above test, we omit further ROW-related testing
432 * below. If we later allow naming OLD and NEW ROW variables,
433 * adjustments will be needed below.
434 */
435
436 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
439 errmsg("\"%s\" is a foreign table",
441 errdetail("Triggers on foreign tables cannot have transition tables.")));
442
443 if (rel->rd_rel->relkind == RELKIND_VIEW)
446 errmsg("\"%s\" is a view",
448 errdetail("Triggers on views cannot have transition tables.")));
449
450 /*
451 * We currently don't allow row-level triggers with transition
452 * tables on partition or inheritance children. Such triggers
453 * would somehow need to see tuples converted to the format of the
454 * table they're attached to, and it's not clear which subset of
455 * tuples each child should see. See also the prohibitions in
456 * ATExecAttachPartition() and ATExecAddInherit().
457 */
458 if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id))
459 {
460 /* Use appropriate error message. */
461 if (rel->rd_rel->relispartition)
464 errmsg("ROW triggers with transition tables are not supported on partitions")));
465 else
468 errmsg("ROW triggers with transition tables are not supported on inheritance children")));
469 }
470
471 if (stmt->timing != TRIGGER_TYPE_AFTER)
474 errmsg("transition table name can only be specified for an AFTER trigger")));
475
476 if (TRIGGER_FOR_TRUNCATE(tgtype))
479 errmsg("TRUNCATE triggers with transition tables are not supported")));
480
481 /*
482 * We currently don't allow multi-event triggers ("INSERT OR
483 * UPDATE") with transition tables, because it's not clear how to
484 * handle INSERT ... ON CONFLICT statements which can fire both
485 * INSERT and UPDATE triggers. We show the inserted tuples to
486 * INSERT triggers and the updated tuples to UPDATE triggers, but
487 * it's not yet clear what INSERT OR UPDATE trigger should see.
488 * This restriction could be lifted if we can decide on the right
489 * semantics in a later release.
490 */
491 if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) +
492 (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) +
493 (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1)
496 errmsg("transition tables cannot be specified for triggers with more than one event")));
497
498 /*
499 * We currently don't allow column-specific triggers with
500 * transition tables. Per spec, that seems to require
501 * accumulating separate transition tables for each combination of
502 * columns, which is a lot of work for a rather marginal feature.
503 */
504 if (stmt->columns != NIL)
507 errmsg("transition tables cannot be specified for triggers with column lists")));
508
509 /*
510 * We disallow constraint triggers with transition tables, to
511 * protect the assumption that such triggers can't be deferred.
512 * See notes with AfterTriggers data structures, below.
513 *
514 * Currently this is enforced by the grammar, so just Assert here.
515 */
516 Assert(!stmt->isconstraint);
517
518 if (tt->isNew)
519 {
520 if (!(TRIGGER_FOR_INSERT(tgtype) ||
521 TRIGGER_FOR_UPDATE(tgtype)))
524 errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger")));
525
526 if (newtablename != NULL)
529 errmsg("NEW TABLE cannot be specified multiple times")));
530
531 newtablename = tt->name;
532 }
533 else
534 {
535 if (!(TRIGGER_FOR_DELETE(tgtype) ||
536 TRIGGER_FOR_UPDATE(tgtype)))
539 errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger")));
540
541 if (oldtablename != NULL)
544 errmsg("OLD TABLE cannot be specified multiple times")));
545
546 oldtablename = tt->name;
547 }
548 }
549
550 if (newtablename != NULL && oldtablename != NULL &&
554 errmsg("OLD TABLE name and NEW TABLE name cannot be the same")));
555 }
556
557 /*
558 * Parse the WHEN clause, if any and we weren't passed an already
559 * transformed one.
560 *
561 * Note that as a side effect, we fill whenRtable when parsing. If we got
562 * an already parsed clause, this does not occur, which is what we want --
563 * no point in adding redundant dependencies below.
564 */
565 if (!whenClause && stmt->whenClause)
566 {
567 ParseState *pstate;
569 List *varList;
570 ListCell *lc;
571
572 /* Set up a pstate to parse with */
573 pstate = make_parsestate(NULL);
574 pstate->p_sourcetext = queryString;
575
576 /*
577 * Set up nsitems for OLD and NEW references.
578 *
579 * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
580 */
583 makeAlias("old", NIL),
584 false, false);
585 addNSItemToQuery(pstate, nsitem, false, true, true);
588 makeAlias("new", NIL),
589 false, false);
590 addNSItemToQuery(pstate, nsitem, false, true, true);
591
592 /* Transform expression. Copy to be sure we don't modify original */
593 whenClause = transformWhereClause(pstate,
594 copyObject(stmt->whenClause),
596 "WHEN");
597 /* we have to fix its collations too */
598 assign_expr_collations(pstate, whenClause);
599
600 /*
601 * Check for disallowed references to OLD/NEW.
602 *
603 * NB: pull_var_clause is okay here only because we don't allow
604 * subselects in WHEN clauses; it would fail to examine the contents
605 * of subselects.
606 */
607 varList = pull_var_clause(whenClause, 0);
608 foreach(lc, varList)
609 {
610 Var *var = (Var *) lfirst(lc);
611
612 switch (var->varno)
613 {
614 case PRS2_OLD_VARNO:
615 if (!TRIGGER_FOR_ROW(tgtype))
618 errmsg("statement trigger's WHEN condition cannot reference column values"),
619 parser_errposition(pstate, var->location)));
620 if (TRIGGER_FOR_INSERT(tgtype))
623 errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
624 parser_errposition(pstate, var->location)));
625 /* system columns are okay here */
626 break;
627 case PRS2_NEW_VARNO:
628 if (!TRIGGER_FOR_ROW(tgtype))
631 errmsg("statement trigger's WHEN condition cannot reference column values"),
632 parser_errposition(pstate, var->location)));
633 if (TRIGGER_FOR_DELETE(tgtype))
636 errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
637 parser_errposition(pstate, var->location)));
638 if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
641 errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
642 parser_errposition(pstate, var->location)));
643 if (TRIGGER_FOR_BEFORE(tgtype) &&
644 var->varattno == 0 &&
645 RelationGetDescr(rel)->constr &&
646 (RelationGetDescr(rel)->constr->has_generated_stored ||
647 RelationGetDescr(rel)->constr->has_generated_virtual))
650 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
651 errdetail("A whole-row reference is used and the table contains generated columns."),
652 parser_errposition(pstate, var->location)));
653 if (TRIGGER_FOR_BEFORE(tgtype) &&
654 var->varattno > 0 &&
655 TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attgenerated)
658 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
659 errdetail("Column \"%s\" is a generated column.",
660 NameStr(TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attname)),
661 parser_errposition(pstate, var->location)));
662 break;
663 default:
664 /* can't happen without add_missing_from, so just elog */
665 elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
666 break;
667 }
668 }
669
670 /* we'll need the rtable for recordDependencyOnExpr */
671 whenRtable = pstate->p_rtable;
672
673 qual = nodeToString(whenClause);
674
675 free_parsestate(pstate);
676 }
677 else if (!whenClause)
678 {
679 whenClause = NULL;
680 whenRtable = NIL;
681 qual = NULL;
682 }
683 else
684 {
685 qual = nodeToString(whenClause);
686 whenRtable = NIL;
687 }
688
689 /*
690 * Find and validate the trigger function.
691 */
692 if (!OidIsValid(funcoid))
693 funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
694 if (!isInternal)
695 {
697 if (aclresult != ACLCHECK_OK)
699 NameListToString(stmt->funcname));
700 }
702 if (funcrettype != TRIGGEROID)
705 errmsg("function %s must return type %s",
706 NameListToString(stmt->funcname), "trigger")));
707
708 /*
709 * Scan pg_trigger to see if there is already a trigger of the same name.
710 * Skip this for internally generated triggers, since we'll modify the
711 * name to be unique below.
712 *
713 * NOTE that this is cool only because we have ShareRowExclusiveLock on
714 * the relation, so the trigger set won't be changing underneath us.
715 */
717 if (!isInternal)
718 {
721
722 ScanKeyInit(&skeys[0],
726
727 ScanKeyInit(&skeys[1],
730 CStringGetDatum(stmt->trigname));
731
733 NULL, 2, skeys);
734
735 /* There should be at most one matching tuple */
737 {
739
740 trigoid = oldtrigger->oid;
741 existing_constraint_oid = oldtrigger->tgconstraint;
742 existing_isInternal = oldtrigger->tgisinternal;
744 trigger_exists = true;
745 /* copy the tuple to use in CatalogTupleUpdate() */
746 tuple = heap_copytuple(tuple);
747 }
749 }
750
751 if (!trigger_exists)
752 {
753 /* Generate the OID for the new trigger. */
756 }
757 else
758 {
759 /*
760 * If OR REPLACE was specified, we'll replace the old trigger;
761 * otherwise complain about the duplicate name.
762 */
763 if (!stmt->replace)
766 errmsg("trigger \"%s\" for relation \"%s\" already exists",
767 stmt->trigname, RelationGetRelationName(rel))));
768
769 /*
770 * An internal trigger or a child trigger (isClone) cannot be replaced
771 * by a user-defined trigger. However, skip this test when
772 * in_partition, because then we're recursing from a partitioned table
773 * and the check was made at the parent level.
774 */
779 errmsg("trigger \"%s\" for relation \"%s\" is an internal or a child trigger",
780 stmt->trigname, RelationGetRelationName(rel))));
781
782 /*
783 * It is not allowed to replace with a constraint trigger; gram.y
784 * should have enforced this already.
785 */
786 Assert(!stmt->isconstraint);
787
788 /*
789 * It is not allowed to replace an existing constraint trigger,
790 * either. (The reason for these restrictions is partly that it seems
791 * difficult to deal with pending trigger events in such cases, and
792 * partly that the command might imply changing the constraint's
793 * properties as well, which doesn't seem nice.)
794 */
798 errmsg("trigger \"%s\" for relation \"%s\" is a constraint trigger",
799 stmt->trigname, RelationGetRelationName(rel))));
800 }
801
802 /*
803 * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
804 * corresponding pg_constraint entry.
805 */
806 if (stmt->isconstraint && !OidIsValid(constraintOid))
807 {
808 /* Internal callers should have made their own constraints */
813 stmt->deferrable,
814 stmt->initdeferred,
815 true, /* Is Enforced */
816 true,
817 InvalidOid, /* no parent */
818 RelationGetRelid(rel),
819 NULL, /* no conkey */
820 0,
821 0,
822 InvalidOid, /* no domain */
823 InvalidOid, /* no index */
824 InvalidOid, /* no foreign key */
825 NULL,
826 NULL,
827 NULL,
828 NULL,
829 0,
830 ' ',
831 ' ',
832 NULL,
833 0,
834 ' ',
835 NULL, /* no exclusion */
836 NULL, /* no check constraint */
837 NULL,
838 true, /* islocal */
839 0, /* inhcount */
840 true, /* noinherit */
841 false, /* conperiod */
842 isInternal); /* is_internal */
843 }
844
845 /*
846 * If trigger is internally generated, modify the provided trigger name to
847 * ensure uniqueness by appending the trigger OID. (Callers will usually
848 * supply a simple constant trigger name in these cases.)
849 */
850 if (isInternal)
851 {
853 "%s_%u", stmt->trigname, trigoid);
854 trigname = internaltrigname;
855 }
856 else
857 {
858 /* user-defined trigger; use the specified trigger name as-is */
859 trigname = stmt->trigname;
860 }
861
862 /*
863 * Build the new pg_trigger tuple.
864 */
865 memset(nulls, false, sizeof(nulls));
866
871 CStringGetDatum(trigname));
881
882 if (stmt->args)
883 {
884 ListCell *le;
885 char *args;
886 int16 nargs = list_length(stmt->args);
887 int len = 0;
888
889 foreach(le, stmt->args)
890 {
891 char *ar = strVal(lfirst(le));
892
893 len += strlen(ar) + 4;
894 for (; *ar; ar++)
895 {
896 if (*ar == '\\')
897 len++;
898 }
899 }
900 args = (char *) palloc(len + 1);
901 args[0] = '\0';
902 foreach(le, stmt->args)
903 {
904 char *s = strVal(lfirst(le));
905 char *d = args + strlen(args);
906
907 while (*s)
908 {
909 if (*s == '\\')
910 *d++ = '\\';
911 *d++ = *s++;
912 }
913 strcpy(d, "\\000");
914 }
917 CStringGetDatum(args));
918 }
919 else
920 {
923 CStringGetDatum(""));
924 }
925
926 /* build column number array if it's a column-specific trigger */
927 ncolumns = list_length(stmt->columns);
928 if (ncolumns == 0)
929 columns = NULL;
930 else
931 {
932 ListCell *cell;
933 int i = 0;
934
935 columns = (int16 *) palloc(ncolumns * sizeof(int16));
936 foreach(cell, stmt->columns)
937 {
938 char *name = strVal(lfirst(cell));
940 int j;
941
942 /* Lookup column name. System columns are not allowed */
943 attnum = attnameAttNum(rel, name, false);
947 errmsg("column \"%s\" of relation \"%s\" does not exist",
949
950 /* Check for duplicates */
951 for (j = i - 1; j >= 0; j--)
952 {
953 if (columns[j] == attnum)
956 errmsg("column \"%s\" specified more than once",
957 name)));
958 }
959
960 columns[i++] = attnum;
961 }
962 }
963 tgattr = buildint2vector(columns, ncolumns);
965
966 /* set tgqual if trigger has WHEN clause */
967 if (qual)
969 else
970 nulls[Anum_pg_trigger_tgqual - 1] = true;
971
972 if (oldtablename)
975 else
976 nulls[Anum_pg_trigger_tgoldtable - 1] = true;
977 if (newtablename)
980 else
981 nulls[Anum_pg_trigger_tgnewtable - 1] = true;
982
983 /*
984 * Insert or replace tuple in pg_trigger.
985 */
986 if (!trigger_exists)
987 {
988 tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
990 }
991 else
992 {
994
995 newtup = heap_form_tuple(tgrel->rd_att, values, nulls);
998 }
999
1000 heap_freetuple(tuple); /* free either original or new tuple */
1002
1006 if (oldtablename)
1008 if (newtablename)
1010
1011 /*
1012 * Update relation's pg_class entry; if necessary; and if not, send an SI
1013 * message to make other backends (and this one) rebuild relcache entries.
1014 */
1018 if (!HeapTupleIsValid(tuple))
1019 elog(ERROR, "cache lookup failed for relation %u",
1020 RelationGetRelid(rel));
1021 if (!((Form_pg_class) GETSTRUCT(tuple))->relhastriggers)
1022 {
1023 ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
1024
1025 CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
1026
1028 }
1029 else
1031
1032 heap_freetuple(tuple);
1034
1035 /*
1036 * If we're replacing a trigger, flush all the old dependencies before
1037 * recording new ones.
1038 */
1039 if (trigger_exists)
1041
1042 /*
1043 * Record dependencies for trigger. Always place a normal dependency on
1044 * the function.
1045 */
1046 myself.classId = TriggerRelationId;
1047 myself.objectId = trigoid;
1048 myself.objectSubId = 0;
1049
1051 referenced.objectId = funcoid;
1052 referenced.objectSubId = 0;
1054
1056 {
1057 /*
1058 * Internally-generated trigger for a constraint, so make it an
1059 * internal dependency of the constraint. We can skip depending on
1060 * the relation(s), as there'll be an indirect dependency via the
1061 * constraint.
1062 */
1064 referenced.objectId = constraintOid;
1065 referenced.objectSubId = 0;
1067 }
1068 else
1069 {
1070 /*
1071 * User CREATE TRIGGER, so place dependencies. We make trigger be
1072 * auto-dropped if its relation is dropped or if the FK relation is
1073 * dropped. (Auto drop is compatible with our pre-7.3 behavior.)
1074 */
1076 referenced.objectId = RelationGetRelid(rel);
1077 referenced.objectSubId = 0;
1079
1081 {
1083 referenced.objectId = constrrelid;
1084 referenced.objectSubId = 0;
1086 }
1087 /* Not possible to have an index dependency in this case */
1088 Assert(!OidIsValid(indexOid));
1089
1090 /*
1091 * If it's a user-specified constraint trigger, make the constraint
1092 * internally dependent on the trigger instead of vice versa.
1093 */
1095 {
1097 referenced.objectId = constraintOid;
1098 referenced.objectSubId = 0;
1100 }
1101
1102 /*
1103 * If it's a partition trigger, create the partition dependencies.
1104 */
1106 {
1111 }
1112 }
1113
1114 /* If column-specific trigger, add normal dependencies on columns */
1115 if (columns != NULL)
1116 {
1117 int i;
1118
1120 referenced.objectId = RelationGetRelid(rel);
1121 for (i = 0; i < ncolumns; i++)
1122 {
1123 referenced.objectSubId = columns[i];
1125 }
1126 }
1127
1128 /*
1129 * If it has a WHEN clause, add dependencies on objects mentioned in the
1130 * expression (eg, functions, as well as any columns used).
1131 */
1132 if (whenRtable != NIL)
1135
1136 /* Post creation hook for new trigger */
1138 isInternal);
1139
1140 /*
1141 * Lastly, create the trigger on child relations, if needed.
1142 */
1144 {
1145 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1146 int i;
1149
1151 "part trig clone",
1153
1154 /*
1155 * We don't currently expect to be called with a valid indexOid. If
1156 * that ever changes then we'll need to write code here to find the
1157 * corresponding child index.
1158 */
1159 Assert(!OidIsValid(indexOid));
1160
1162
1163 /* Iterate to create the trigger on each existing partition */
1164 for (i = 0; i < partdesc->nparts; i++)
1165 {
1168 Node *qual;
1169
1171
1172 /*
1173 * Initialize our fabricated parse node by copying the original
1174 * one, then resetting fields that we pass separately.
1175 */
1177 childStmt->funcname = NIL;
1178 childStmt->whenClause = NULL;
1179
1180 /* If there is a WHEN clause, create a modified copy of it */
1181 qual = copyObject(whenClause);
1182 qual = (Node *)
1184 childTbl, rel);
1185 qual = (Node *)
1187 childTbl, rel);
1188
1189 CreateTriggerFiringOn(childStmt, queryString,
1190 partdesc->oids[i], refRelOid,
1192 funcoid, trigoid, qual,
1194
1196
1198 }
1199
1202 }
1203
1204 /* Keep lock on target rel until end of xact */
1205 table_close(rel, NoLock);
1206
1207 return myself;
1208}
AclResult
Definition acl.h:182
@ ACLCHECK_OK
Definition acl.h:183
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2654
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3836
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4039
#define InvalidAttrNumber
Definition attnum.h:23
static Datum values[MAXATTR]
Definition bootstrap.c:155
#define CStringGetTextDatum(s)
Definition builtins.h:97
Datum byteain(PG_FUNCTION_ARGS)
Definition bytea.c:201
#define NameStr(name)
Definition c.h:765
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 errdetail(const char *fmt,...)
Definition elog.c:1216
int errhint(const char *fmt,...)
Definition elog.c:1330
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:684
bool allowSystemTableMods
Definition globals.c:130
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:778
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1117
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:2078
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2153
Oid get_func_rettype(Oid funcid)
Definition lsyscache.c:1805
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:3664
#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:802
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
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
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:156
#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:45
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition pg_depend.c:301
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:352
static Datum Int16GetDatum(int16 X)
Definition postgres.h:182
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:342
static Datum CharGetDatum(char X)
Definition postgres.h:132
#define InvalidOid
#define PRS2_OLD_VARNO
Definition primnodes.h:250
#define PRS2_NEW_VARNO
Definition primnodes.h:251
#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:195
List * p_rtable
Definition parse_node.h:196
Oid rd_id
Definition rel.h:113
ParseLoc location
Definition primnodes.h:310
AttrNumber varattno
Definition primnodes.h:274
int varno
Definition primnodes.h:269
#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:1101

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

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

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), CStringGetDatum(), EnableDisableTrigger(), ereport, errcode(), errmsg(), ERROR, fb(), 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 2801 of file trigger.c.

2807{
2808 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2809
2810 if (relinfo->ri_FdwRoutine && transition_capture &&
2811 transition_capture->tcs_delete_old_table)
2812 {
2813 Assert(relinfo->ri_RootResultRelInfo);
2814 ereport(ERROR,
2816 errmsg("cannot collect transition tuples from child foreign tables")));
2817 }
2818
2819 if ((trigdesc && trigdesc->trig_delete_after_row) ||
2820 (transition_capture && transition_capture->tcs_delete_old_table))
2821 {
2823
2825 if (fdw_trigtuple == NULL)
2826 GetTupleForTrigger(estate,
2827 NULL,
2828 relinfo,
2829 tupleid,
2831 slot,
2832 false,
2833 NULL,
2834 NULL,
2835 NULL);
2836 else
2838
2841 true, slot, NULL, NIL, NULL,
2842 transition_capture,
2844 }
2845}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
@ LockTupleExclusive
Definition lockoptions.h:58
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:3344

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

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

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

3153{
3154 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3155
3156 if (relinfo->ri_FdwRoutine && transition_capture &&
3157 (transition_capture->tcs_update_old_table ||
3158 transition_capture->tcs_update_new_table))
3159 {
3160 Assert(relinfo->ri_RootResultRelInfo);
3161 ereport(ERROR,
3163 errmsg("cannot collect transition tuples from child foreign tables")));
3164 }
3165
3166 if ((trigdesc && trigdesc->trig_update_after_row) ||
3167 (transition_capture &&
3168 (transition_capture->tcs_update_old_table ||
3169 transition_capture->tcs_update_new_table)))
3170 {
3171 /*
3172 * Note: if the UPDATE is converted into a DELETE+INSERT as part of
3173 * update-partition-key operation, then this function is also called
3174 * separately for DELETE and INSERT to capture transition table rows.
3175 * In such case, either old tuple or new tuple can be NULL.
3176 */
3179
3182
3185
3187 GetTupleForTrigger(estate,
3188 NULL,
3189 tupsrc,
3190 tupleid,
3192 oldslot,
3193 false,
3194 NULL,
3195 NULL,
3196 NULL);
3197 else if (fdw_trigtuple != NULL)
3199 else
3201
3205 true,
3208 transition_capture,
3210 }
3211}
Bitmapset * ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition execUtils.c:1418

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

2683{
2684 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2685
2686 if (trigdesc && trigdesc->trig_delete_after_statement)
2689 false, NULL, NULL, NIL, NULL, transition_capture,
2690 false);
2691}
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 2452 of file trigger.c.

2454{
2455 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2456
2457 if (trigdesc && trigdesc->trig_insert_after_statement)
2460 false, NULL, NULL, NIL, NULL, transition_capture,
2461 false);
2462}
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 3327 of file trigger.c.

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

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

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

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

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

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

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

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

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

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

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

2850{
2851 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2854 int i;
2855
2860 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2861
2862 ExecForceStoreHeapTuple(trigtuple, slot, false);
2863
2864 for (i = 0; i < trigdesc->numtriggers; i++)
2865 {
2867 Trigger *trigger = &trigdesc->triggers[i];
2868
2869 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2873 continue;
2874 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2875 NULL, slot, NULL))
2876 continue;
2877
2878 LocTriggerData.tg_trigslot = slot;
2879 LocTriggerData.tg_trigtuple = trigtuple;
2880 LocTriggerData.tg_trigger = trigger;
2882 i,
2883 relinfo->ri_TrigFunctions,
2884 relinfo->ri_TrigInstrument,
2885 GetPerTupleMemoryContext(estate));
2886 if (rettuple == NULL)
2887 return false; /* Delete was suppressed */
2888 if (rettuple != trigtuple)
2890 }
2891 return true;
2892}
#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 2569 of file trigger.c.

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

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

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

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

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

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

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

1371{
1373 ScanKeyData skey[2];
1375 HeapTuple tup;
1376 Oid oid;
1377
1378 /*
1379 * Find the trigger, verify permissions, set up object address
1380 */
1382
1383 ScanKeyInit(&skey[0],
1386 ObjectIdGetDatum(relid));
1387 ScanKeyInit(&skey[1],
1390 CStringGetDatum(trigname));
1391
1393 NULL, 2, skey);
1394
1396
1397 if (!HeapTupleIsValid(tup))
1398 {
1399 if (!missing_ok)
1400 ereport(ERROR,
1402 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1403 trigname, get_rel_name(relid))));
1404 oid = InvalidOid;
1405 }
1406 else
1407 {
1408 oid = ((Form_pg_trigger) GETSTRUCT(tup))->oid;
1409 }
1410
1413 return oid;
1414}

References AccessShareLock, BTEqualStrategyNumber, CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, fb(), 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 4908 of file trigger.c.

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

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

Referenced by TransitionTableAddTuple().

◆ GetAfterTriggersTableData()

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

Definition at line 4866 of file trigger.c.

4867{
4871 ListCell *lc;
4872
4873 /* At this level, cmdType should not be, eg, CMD_MERGE */
4874 Assert(cmdType == CMD_INSERT ||
4875 cmdType == CMD_UPDATE ||
4876 cmdType == CMD_DELETE);
4877
4878 /* Caller should have ensured query_depth is OK. */
4882
4883 foreach(lc, qs->tables)
4884 {
4886 if (table->relid == relid && table->cmdType == cmdType &&
4887 !table->closed)
4888 return table;
4889 }
4890
4892
4894 table->relid = relid;
4895 table->cmdType = cmdType;
4896 qs->tables = lappend(qs->tables, table);
4897
4899
4900 return table;
4901}

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

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

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

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

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

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

6719{
6721}

References MyTriggerDepth, and PG_RETURN_INT32.

◆ RangeVarCallbackForRenameTrigger()

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

Definition at line 1420 of file trigger.c.

1422{
1423 HeapTuple tuple;
1425
1426 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1427 if (!HeapTupleIsValid(tuple))
1428 return; /* concurrently dropped */
1429 form = (Form_pg_class) GETSTRUCT(tuple);
1430
1431 /* only tables and views can have triggers */
1432 if (form->relkind != RELKIND_RELATION && form->relkind != RELKIND_VIEW &&
1433 form->relkind != RELKIND_FOREIGN_TABLE &&
1434 form->relkind != RELKIND_PARTITIONED_TABLE)
1435 ereport(ERROR,
1437 errmsg("relation \"%s\" cannot have triggers",
1438 rv->relname),
1440
1441 /* you must own the table to rename one of its triggers */
1444 if (!allowSystemTableMods && IsSystemClass(relid, form))
1445 ereport(ERROR,
1447 errmsg("permission denied: \"%s\" is a system catalog",
1448 rv->relname)));
1449
1450 ReleaseSysCache(tuple);
1451}
@ ACLCHECK_NOT_OWNER
Definition acl.h:185
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4090
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition catalog.c:86
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:264
HeapTuple SearchSysCache1(int 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 1861 of file trigger.c.

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

References AccessShareLock, BTEqualStrategyNumber, CacheMemoryContext, CopyTriggerDesc(), DatumGetByteaPP, DatumGetCString(), DirectFunctionCall1, elog, ERROR, fastgetattr(), fb(), 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 1291 of file trigger.c.

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

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

References AccessExclusiveLock, BTEqualStrategyNumber, ereport, errcode(), errhint(), errmsg(), ERROR, fb(), find_all_inheritors(), 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 1582 of file trigger.c.

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

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

References BTEqualStrategyNumber, fb(), 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 5736 of file trigger.c.

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

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCopy()

static SetConstraintState SetConstraintStateCopy ( SetConstraintState  origstate)
static

Definition at line 5716 of file trigger.c.

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

References fb(), and SetConstraintStateCreate().

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCreate()

static SetConstraintState SetConstraintStateCreate ( int  numalloc)
static

Definition at line 5691 of file trigger.c.

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

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

Referenced by AfterTriggerSetState(), and SetConstraintStateCopy().

◆ SetTriggerFlags()

static void SetTriggerFlags ( TriggerDesc trigdesc,
Trigger trigger 
)
static

Definition at line 2013 of file trigger.c.

2014{
2015 int16 tgtype = trigger->tgtype;
2016
2017 trigdesc->trig_insert_before_row |=
2020 trigdesc->trig_insert_after_row |=
2023 trigdesc->trig_insert_instead_row |=
2026 trigdesc->trig_insert_before_statement |=
2029 trigdesc->trig_insert_after_statement |=
2032 trigdesc->trig_update_before_row |=
2035 trigdesc->trig_update_after_row |=
2038 trigdesc->trig_update_instead_row |=
2041 trigdesc->trig_update_before_statement |=
2044 trigdesc->trig_update_after_statement |=
2047 trigdesc->trig_delete_before_row |=
2050 trigdesc->trig_delete_after_row |=
2053 trigdesc->trig_delete_instead_row |=
2056 trigdesc->trig_delete_before_statement |=
2059 trigdesc->trig_delete_after_statement |=
2062 /* there are no row-level truncate triggers */
2069
2070 trigdesc->trig_insert_new_table |=
2071 (TRIGGER_FOR_INSERT(tgtype) &&
2073 trigdesc->trig_update_old_table |=
2074 (TRIGGER_FOR_UPDATE(tgtype) &&
2076 trigdesc->trig_update_new_table |=
2077 (TRIGGER_FOR_UPDATE(tgtype) &&
2079 trigdesc->trig_delete_old_table |=
2080 (TRIGGER_FOR_DELETE(tgtype) &&
2082}
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 5586 of file trigger.c.

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

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

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

1224{
1226 ScanKeyData skey[1];
1228 HeapTuple tuple,
1229 newtup;
1232
1233 /*
1234 * Find the trigger to delete.
1235 */
1236 ScanKeyInit(&skey[0],
1240
1242 NULL, 1, skey);
1243
1244 tuple = systable_getnext(tgscan);
1245 if (!HeapTupleIsValid(tuple))
1246 elog(ERROR, "could not find tuple for trigger %u", childTrigId);
1247 newtup = heap_copytuple(tuple);
1250 {
1251 /* don't allow setting parent for a constraint that already has one */
1252 if (OidIsValid(trigForm->tgparentid))
1253 elog(ERROR, "trigger %u already has a parent trigger",
1254 childTrigId);
1255
1256 trigForm->tgparentid = parentTrigId;
1257
1259
1261
1264
1267 }
1268 else
1269 {
1270 trigForm->tgparentid = InvalidOid;
1271
1273
1280 }
1281
1284}
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:351

References BTEqualStrategyNumber, CatalogTupleUpdate(), deleteDependencyRecordsForClass(), DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, elog, ERROR, fb(), 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 66 of file trigger.c.

Referenced by ExecCallTriggerFunc(), and pg_trigger_depth().

◆ SessionReplicationRole