Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

PostgreSQL Source Code git master
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 char * FindTriggerIncompatibleWithInheritance (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, 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.

◆ 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:
for (eptr = (AfterTriggerEvent) CHUNK_DATA_START(cptr); \
(char *) eptr < (cptr)->freeptr; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3770
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3742
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3704

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; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))

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

#define SizeofTriggerEvent (   evt)
Value:
(((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_CP_UPDATE ? \
(((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_2CTID ? \
(((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_1CTID ? \
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3690
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3687
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3688
#define AFTER_TRIGGER_CP_UPDATE
Definition: trigger.c:3689

Definition at line 3742 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3704 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3691 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3875 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3877 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3876 of file trigger.c.

◆ SetConstraintState

Definition at line 3634 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3613 of file trigger.c.

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

4089{
4090 Size eventsize = SizeofTriggerEvent(event);
4091 Size needed = eventsize + sizeof(AfterTriggerSharedData);
4093 AfterTriggerShared newshared;
4094 AfterTriggerEvent newevent;
4095
4096 /*
4097 * If empty list or not enough room in the tail chunk, make a new chunk.
4098 * We assume here that a new shared record will always be needed.
4099 */
4100 chunk = events->tail;
4101 if (chunk == NULL ||
4102 chunk->endfree - chunk->freeptr < needed)
4103 {
4104 Size chunksize;
4105
4106 /* Create event context if we didn't already */
4107 if (afterTriggers.event_cxt == NULL)
4110 "AfterTriggerEvents",
4112
4113 /*
4114 * Chunk size starts at 1KB and is allowed to increase up to 1MB.
4115 * These numbers are fairly arbitrary, though there is a hard limit at
4116 * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
4117 * shared records using the available space in ate_flags. Another
4118 * constraint is that if the chunk size gets too huge, the search loop
4119 * below would get slow given a (not too common) usage pattern with
4120 * many distinct event types in a chunk. Therefore, we double the
4121 * preceding chunk size only if there weren't too many shared records
4122 * in the preceding chunk; otherwise we halve it. This gives us some
4123 * ability to adapt to the actual usage pattern of the current query
4124 * while still having large chunk sizes in typical usage. All chunk
4125 * sizes used should be MAXALIGN multiples, to ensure that the shared
4126 * records will be aligned safely.
4127 */
4128#define MIN_CHUNK_SIZE 1024
4129#define MAX_CHUNK_SIZE (1024*1024)
4130
4131#if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
4132#error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
4133#endif
4134
4135 if (chunk == NULL)
4136 chunksize = MIN_CHUNK_SIZE;
4137 else
4138 {
4139 /* preceding chunk size... */
4140 chunksize = chunk->endptr - (char *) chunk;
4141 /* check number of shared records in preceding chunk */
4142 if ((chunk->endptr - chunk->endfree) <=
4143 (100 * sizeof(AfterTriggerSharedData)))
4144 chunksize *= 2; /* okay, double it */
4145 else
4146 chunksize /= 2; /* too many shared records */
4147 chunksize = Min(chunksize, MAX_CHUNK_SIZE);
4148 }
4149 chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize);
4150 chunk->next = NULL;
4151 chunk->freeptr = CHUNK_DATA_START(chunk);
4152 chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
4153 Assert(chunk->endfree - chunk->freeptr >= needed);
4154
4155 if (events->tail == NULL)
4156 {
4157 Assert(events->head == NULL);
4158 events->head = chunk;
4159 }
4160 else
4161 events->tail->next = chunk;
4162 events->tail = chunk;
4163 /* events->tailfree is now out of sync, but we'll fix it below */
4164 }
4165
4166 /*
4167 * Try to locate a matching shared-data record already in the chunk. If
4168 * none, make a new one. The search begins with the most recently added
4169 * record, since newer ones are most likely to match.
4170 */
4171 for (newshared = (AfterTriggerShared) chunk->endfree;
4172 (char *) newshared < chunk->endptr;
4173 newshared++)
4174 {
4175 /* compare fields roughly by probability of them being different */
4176 if (newshared->ats_tgoid == evtshared->ats_tgoid &&
4177 newshared->ats_event == evtshared->ats_event &&
4178 newshared->ats_firing_id == 0 &&
4179 newshared->ats_table == evtshared->ats_table &&
4180 newshared->ats_relid == evtshared->ats_relid &&
4181 newshared->ats_rolid == evtshared->ats_rolid &&
4182 bms_equal(newshared->ats_modifiedcols,
4183 evtshared->ats_modifiedcols))
4184 break;
4185 }
4186 if ((char *) newshared >= chunk->endptr)
4187 {
4188 newshared = ((AfterTriggerShared) chunk->endfree) - 1;
4189 *newshared = *evtshared;
4190 /* now we must make a suitably-long-lived copy of the bitmap */
4192 newshared->ats_firing_id = 0; /* just to be sure */
4193 chunk->endfree = (char *) newshared;
4194 }
4195
4196 /* Insert the data */
4197 newevent = (AfterTriggerEvent) chunk->freeptr;
4198 memcpy(newevent, event, eventsize);
4199 /* ... and link the new event to its shared record */
4200 newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
4201 newevent->ate_flags |= (char *) newshared - (char *) newevent;
4202
4203 chunk->freeptr += eventsize;
4204 events->tailfree = chunk->freeptr;
4205}
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:142
#define Min(x, y)
Definition: c.h:1004
size_t Size
Definition: c.h:611
Assert(PointerIsAligned(start, uint64))
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1229
MemoryContext TopTransactionContext
Definition: mcxt.c:171
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
struct AfterTriggerEventChunk * next
Definition: trigger.c:3763
TriggerFlags ate_flags
Definition: trigger.c:3708
AfterTriggerEventChunk * head
Definition: trigger.c:3775
AfterTriggerEventChunk * tail
Definition: trigger.c:3776
TriggerEvent ats_event
Definition: trigger.c:3695
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3700
CommandId ats_firing_id
Definition: trigger.c:3699
Bitmapset * ats_modifiedcols
Definition: trigger.c:3701
MemoryContext event_cxt
Definition: trigger.c:3884
struct AfterTriggerSharedData AfterTriggerSharedData
static AfterTriggersData afterTriggers
Definition: trigger.c:3941
static Bitmapset * afterTriggerCopyBitmap(Bitmapset *src)
Definition: trigger.c:4062
#define MAX_CHUNK_SIZE
struct AfterTriggerSharedData * AfterTriggerShared
Definition: trigger.c:3691
#define MIN_CHUNK_SIZE

References afterTriggerCopyBitmap(), afterTriggers, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_modifiedcols, AfterTriggerSharedData::ats_relid, AfterTriggerSharedData::ats_rolid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, bms_equal(), CHUNK_DATA_START, AfterTriggersData::event_cxt, 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 5104 of file trigger.c.

5105{
5106 /* Increase the query stack depth */
5108}

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5387 of file trigger.c.

5388{
5389 int my_level = GetCurrentTransactionNestLevel();
5390
5391 /*
5392 * Allocate more space in the trans_stack if needed. (Note: because the
5393 * minimum nest level of a subtransaction is 2, we waste the first couple
5394 * entries of the array; not worth the notational effort to avoid it.)
5395 */
5396 while (my_level >= afterTriggers.maxtransdepth)
5397 {
5399 {
5400 /* Arbitrarily initialize for max of 8 subtransaction levels */
5403 8 * sizeof(AfterTriggersTransData));
5405 }
5406 else
5407 {
5408 /* repalloc will keep the stack in the same context */
5409 int new_alloc = afterTriggers.maxtransdepth * 2;
5410
5413 new_alloc * sizeof(AfterTriggersTransData));
5414 afterTriggers.maxtransdepth = new_alloc;
5415 }
5416 }
5417
5418 /*
5419 * Push the current information into the stack. The SET CONSTRAINTS state
5420 * is not saved until/unless changed. Likewise, we don't make a
5421 * per-subtransaction event context until needed.
5422 */
5423 afterTriggers.trans_stack[my_level].state = NULL;
5427}
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1610
CommandId firing_counter
Definition: trigger.c:3881
AfterTriggersTransData * trans_stack
Definition: trigger.c:3892
AfterTriggerEventList events
Definition: trigger.c:3883
AfterTriggerEventList events
Definition: trigger.c:3907
SetConstraintState state
Definition: trigger.c:3906
CommandId firing_counter
Definition: trigger.c:3909
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:929

References afterTriggers, AfterTriggersData::events, AfterTriggersTransData::events, 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 5072 of file trigger.c.

5073{
5074 /*
5075 * Initialize after-trigger state structure to empty
5076 */
5077 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5079
5080 /*
5081 * Verify that there is no leftover state remaining. If these assertions
5082 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5083 * up properly.
5084 */
5085 Assert(afterTriggers.state == NULL);
5092}
uint32 CommandId
Definition: c.h:672
SetConstraintState state
Definition: trigger.c:3882
AfterTriggersQueryData * query_stack
Definition: trigger.c:3887

References afterTriggers, Assert(), AfterTriggersData::event_cxt, AfterTriggersData::events, 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 4017 of file trigger.c.

4018{
4019 Oid tgoid = evtshared->ats_tgoid;
4021 int i;
4022
4023 /*
4024 * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
4025 * constraints declared NOT DEFERRABLE), the state is always false.
4026 */
4027 if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
4028 return false;
4029
4030 /*
4031 * If constraint state exists, SET CONSTRAINTS might have been executed
4032 * either for this trigger or for all triggers.
4033 */
4034 if (state != NULL)
4035 {
4036 /* Check for SET CONSTRAINTS for this specific trigger. */
4037 for (i = 0; i < state->numstates; i++)
4038 {
4039 if (state->trigstates[i].sct_tgoid == tgoid)
4040 return state->trigstates[i].sct_tgisdeferred;
4041 }
4042
4043 /* Check for SET CONSTRAINTS ALL. */
4044 if (state->all_isset)
4045 return state->all_isdeferred;
4046 }
4047
4048 /*
4049 * Otherwise return the default state for the trigger.
4050 */
4051 return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
4052}
int i
Definition: isn.c:77
unsigned int Oid
Definition: postgres_ext.h:32
Definition: regguts.h:323
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:107
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:108

References AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_INITDEFERRED, afterTriggers, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_tgoid, i, and AfterTriggersData::state.

Referenced by afterTriggerMarkEvents().

◆ afterTriggerCopyBitmap()

static Bitmapset * afterTriggerCopyBitmap ( Bitmapset src)
static

Definition at line 4062 of file trigger.c.

4063{
4064 Bitmapset *dst;
4065 MemoryContext oldcxt;
4066
4067 if (src == NULL)
4068 return NULL;
4069
4071
4072 dst = bms_copy(src);
4073
4074 MemoryContextSwitchTo(oldcxt);
4075
4076 return dst;
4077}
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:122
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124

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

Referenced by afterTriggerAddEvent().

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 4275 of file trigger.c.

4276{
4277 AfterTriggerEventChunk *target = qs->events.head;
4278 ListCell *lc;
4279
4280 Assert(target && target->next);
4281
4282 /*
4283 * First, update any pointers in the per-table data, so that they won't be
4284 * dangling. Resetting obsoleted pointers to NULL will make
4285 * cancel_prior_stmt_triggers start from the list head, which is fine.
4286 */
4287 foreach(lc, qs->tables)
4288 {
4290
4291 if (table->after_trig_done &&
4292 table->after_trig_events.tail == target)
4293 {
4294 table->after_trig_events.head = NULL;
4295 table->after_trig_events.tail = NULL;
4296 table->after_trig_events.tailfree = NULL;
4297 }
4298 }
4299
4300 /* Now we can flush the head chunk */
4301 qs->events.head = target->next;
4302 pfree(target);
4303}
void pfree(void *pointer)
Definition: mcxt.c:1594
#define lfirst(lc)
Definition: pg_list.h:172
static const struct lconv_member_info table[]
AfterTriggerEventList events
Definition: trigger.c:3898

References Assert(), AfterTriggersQueryData::events, AfterTriggerEventList::head, lfirst, AfterTriggerEventChunk::next, pfree(), table, and AfterTriggersQueryData::tables.

Referenced by AfterTriggerEndQuery().

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 5124 of file trigger.c.

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

References afterTriggerDeleteHeadEventChunk(), AfterTriggerFreeQuery(), afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, Assert(), AfterTriggersData::events, AfterTriggersQueryData::events, AfterTriggersData::firing_counter, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, AfterTriggersData::query_stack, and AfterTriggerEventList::tail.

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

Definition at line 5435 of file trigger.c.

5436{
5437 int my_level = GetCurrentTransactionNestLevel();
5439 AfterTriggerEvent event;
5441 CommandId subxact_firing_id;
5442
5443 /*
5444 * Pop the prior state if needed.
5445 */
5446 if (isCommit)
5447 {
5449 /* If we saved a prior state, we don't need it anymore */
5451 if (state != NULL)
5452 pfree(state);
5453 /* this avoids double pfree if error later: */
5454 afterTriggers.trans_stack[my_level].state = NULL;
5457 }
5458 else
5459 {
5460 /*
5461 * Aborting. It is possible subxact start failed before calling
5462 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5463 * trans_stack levels that aren't there.
5464 */
5465 if (my_level >= afterTriggers.maxtransdepth)
5466 return;
5467
5468 /*
5469 * Release query-level storage for queries being aborted, and restore
5470 * query_depth to its pre-subxact value. This assumes that a
5471 * subtransaction will not add events to query levels started in a
5472 * earlier transaction state.
5473 */
5475 {
5479 }
5482
5483 /*
5484 * Restore the global deferred-event list to its former length,
5485 * discarding any events queued by the subxact.
5486 */
5488 &afterTriggers.trans_stack[my_level].events);
5489
5490 /*
5491 * Restore the trigger state. If the saved state is NULL, then this
5492 * subxact didn't save it, so it doesn't need restoring.
5493 */
5495 if (state != NULL)
5496 {
5499 }
5500 /* this avoids double pfree if error later: */
5501 afterTriggers.trans_stack[my_level].state = NULL;
5502
5503 /*
5504 * Scan for any remaining deferred events that were marked DONE or IN
5505 * PROGRESS by this subxact or a child, and un-mark them. We can
5506 * recognize such events because they have a firing ID greater than or
5507 * equal to the firing_counter value we saved at subtransaction start.
5508 * (This essentially assumes that the current subxact includes all
5509 * subxacts started after it.)
5510 */
5511 subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5513 {
5514 AfterTriggerShared evtshared = GetTriggerSharedData(event);
5515
5516 if (event->ate_flags &
5518 {
5519 if (evtshared->ats_firing_id >= subxact_firing_id)
5520 event->ate_flags &=
5522 }
5523 }
5524 }
5525}
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3683
#define GetTriggerSharedData(evt)
Definition: trigger.c:3751
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:4235
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3682
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3788

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

5340{
5341 /*
5342 * Forget the pending-events list.
5343 *
5344 * Since all the info is in TopTransactionContext or children thereof, we
5345 * don't really need to do anything to reclaim memory. However, the
5346 * pending-events list could be large, and so it's useful to discard it as
5347 * soon as possible --- especially if we are aborting because we ran out
5348 * of memory for the list!
5349 */
5351 {
5353 afterTriggers.event_cxt = NULL;
5354 afterTriggers.events.head = NULL;
5355 afterTriggers.events.tail = NULL;
5357 }
5358
5359 /*
5360 * Forget any subtransaction state as well. Since this can't be very
5361 * large, we let the eventual reset of TopTransactionContext free the
5362 * memory instead of doing it here.
5363 */
5366
5367
5368 /*
5369 * Forget the query stack and constraint-related state information. As
5370 * with the subtransaction state information, we don't bother freeing the
5371 * memory here.
5372 */
5375 afterTriggers.state = NULL;
5376
5377 /* No more afterTriggers manipulation until next transaction starts. */
5379}
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469

References afterTriggers, AfterTriggersData::event_cxt, AfterTriggersData::events, 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 5623 of file trigger.c.

5624{
5625 int init_depth = afterTriggers.maxquerydepth;
5626
5628
5630 {
5631 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5632
5635 new_alloc * sizeof(AfterTriggersQueryData));
5636 afterTriggers.maxquerydepth = new_alloc;
5637 }
5638 else
5639 {
5640 /* repalloc will keep the stack in the same context */
5641 int old_alloc = afterTriggers.maxquerydepth;
5642 int new_alloc = Max(afterTriggers.query_depth + 1,
5643 old_alloc * 2);
5644
5647 new_alloc * sizeof(AfterTriggersQueryData));
5648 afterTriggers.maxquerydepth = new_alloc;
5649 }
5650
5651 /* Initialize new array entries to empty */
5652 while (init_depth < afterTriggers.maxquerydepth)
5653 {
5655
5656 qs->events.head = NULL;
5657 qs->events.tail = NULL;
5658 qs->events.tailfree = NULL;
5659 qs->fdw_tuplestore = NULL;
5660 qs->tables = NIL;
5661
5662 ++init_depth;
5663 }
5664}
#define Max(x, y)
Definition: c.h:998
#define NIL
Definition: pg_list.h:68
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3899

References afterTriggers, Assert(), AfterTriggersQueryData::events, AfterTriggersQueryData::fdw_tuplestore, AfterTriggerEventList::head, Max, AfterTriggersData::maxquerydepth, MemoryContextAlloc(), NIL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, repalloc(), AfterTriggersQueryData::tables, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, 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 4337 of file trigger.c.

4347{
4348 Relation rel = relInfo->ri_RelationDesc;
4349 Relation src_rel = src_relInfo->ri_RelationDesc;
4350 Relation dst_rel = dst_relInfo->ri_RelationDesc;
4351 AfterTriggerShared evtshared = GetTriggerSharedData(event);
4352 Oid tgoid = evtshared->ats_tgoid;
4353 TriggerData LocTriggerData = {0};
4354 Oid save_rolid;
4355 int save_sec_context;
4356 HeapTuple rettuple;
4357 int tgindx;
4358 bool should_free_trig = false;
4359 bool should_free_new = false;
4360
4361 /*
4362 * Locate trigger in trigdesc. It might not be present, and in fact the
4363 * trigdesc could be NULL, if the trigger was dropped since the event was
4364 * queued. In that case, silently do nothing.
4365 */
4366 if (trigdesc == NULL)
4367 return;
4368 for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4369 {
4370 if (trigdesc->triggers[tgindx].tgoid == tgoid)
4371 {
4372 LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4373 break;
4374 }
4375 }
4376 if (LocTriggerData.tg_trigger == NULL)
4377 return;
4378
4379 /*
4380 * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4381 * to include time spent re-fetching tuples in the trigger cost.
4382 */
4383 if (instr)
4384 InstrStartNode(instr + tgindx);
4385
4386 /*
4387 * Fetch the required tuple(s).
4388 */
4389 switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4390 {
4392 {
4393 Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4394
4395 if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4396 trig_tuple_slot1))
4397 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4398
4399 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4401 !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4402 trig_tuple_slot2))
4403 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4404 }
4405 /* fall through */
4407
4408 /*
4409 * Store tuple in the slot so that tg_trigtuple does not reference
4410 * tuplestore memory. (It is formally possible for the trigger
4411 * function to queue trigger events that add to the same
4412 * tuplestore, which can push other tuples out of memory.) The
4413 * distinction is academic, because we start with a minimal tuple
4414 * that is stored as a heap tuple, constructed in different memory
4415 * context, in the slot anyway.
4416 */
4417 LocTriggerData.tg_trigslot = trig_tuple_slot1;
4418 LocTriggerData.tg_trigtuple =
4419 ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
4420
4421 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4423 {
4424 LocTriggerData.tg_newslot = trig_tuple_slot2;
4425 LocTriggerData.tg_newtuple =
4426 ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new);
4427 }
4428 else
4429 {
4430 LocTriggerData.tg_newtuple = NULL;
4431 }
4432 break;
4433
4434 default:
4435 if (ItemPointerIsValid(&(event->ate_ctid1)))
4436 {
4437 TupleTableSlot *src_slot = ExecGetTriggerOldSlot(estate,
4438 src_relInfo);
4439
4440 if (!table_tuple_fetch_row_version(src_rel,
4441 &(event->ate_ctid1),
4443 src_slot))
4444 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4445
4446 /*
4447 * Store the tuple fetched from the source partition into the
4448 * target (root partitioned) table slot, converting if needed.
4449 */
4450 if (src_relInfo != relInfo)
4451 {
4452 TupleConversionMap *map = ExecGetChildToRootMap(src_relInfo);
4453
4454 LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4455 if (map)
4456 {
4458 src_slot,
4459 LocTriggerData.tg_trigslot);
4460 }
4461 else
4462 ExecCopySlot(LocTriggerData.tg_trigslot, src_slot);
4463 }
4464 else
4465 LocTriggerData.tg_trigslot = src_slot;
4466 LocTriggerData.tg_trigtuple =
4467 ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, &should_free_trig);
4468 }
4469 else
4470 {
4471 LocTriggerData.tg_trigtuple = NULL;
4472 }
4473
4474 /* don't touch ctid2 if not there */
4476 (event->ate_flags & AFTER_TRIGGER_CP_UPDATE)) &&
4477 ItemPointerIsValid(&(event->ate_ctid2)))
4478 {
4479 TupleTableSlot *dst_slot = ExecGetTriggerNewSlot(estate,
4480 dst_relInfo);
4481
4482 if (!table_tuple_fetch_row_version(dst_rel,
4483 &(event->ate_ctid2),
4485 dst_slot))
4486 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4487
4488 /*
4489 * Store the tuple fetched from the destination partition into
4490 * the target (root partitioned) table slot, converting if
4491 * needed.
4492 */
4493 if (dst_relInfo != relInfo)
4494 {
4495 TupleConversionMap *map = ExecGetChildToRootMap(dst_relInfo);
4496
4497 LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4498 if (map)
4499 {
4501 dst_slot,
4502 LocTriggerData.tg_newslot);
4503 }
4504 else
4505 ExecCopySlot(LocTriggerData.tg_newslot, dst_slot);
4506 }
4507 else
4508 LocTriggerData.tg_newslot = dst_slot;
4509 LocTriggerData.tg_newtuple =
4510 ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, &should_free_new);
4511 }
4512 else
4513 {
4514 LocTriggerData.tg_newtuple = NULL;
4515 }
4516 }
4517
4518 /*
4519 * Set up the tuplestore information to let the trigger have access to
4520 * transition tables. When we first make a transition table available to
4521 * a trigger, mark it "closed" so that it cannot change anymore. If any
4522 * additional events of the same type get queued in the current trigger
4523 * query level, they'll go into new transition tables.
4524 */
4525 LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4526 if (evtshared->ats_table)
4527 {
4528 if (LocTriggerData.tg_trigger->tgoldtable)
4529 {
4530 if (TRIGGER_FIRED_BY_UPDATE(evtshared->ats_event))
4531 LocTriggerData.tg_oldtable = evtshared->ats_table->old_upd_tuplestore;
4532 else
4533 LocTriggerData.tg_oldtable = evtshared->ats_table->old_del_tuplestore;
4534 evtshared->ats_table->closed = true;
4535 }
4536
4537 if (LocTriggerData.tg_trigger->tgnewtable)
4538 {
4539 if (TRIGGER_FIRED_BY_INSERT(evtshared->ats_event))
4540 LocTriggerData.tg_newtable = evtshared->ats_table->new_ins_tuplestore;
4541 else
4542 LocTriggerData.tg_newtable = evtshared->ats_table->new_upd_tuplestore;
4543 evtshared->ats_table->closed = true;
4544 }
4545 }
4546
4547 /*
4548 * Setup the remaining trigger information
4549 */
4550 LocTriggerData.type = T_TriggerData;
4551 LocTriggerData.tg_event =
4553 LocTriggerData.tg_relation = rel;
4554 if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4555 LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4556
4557 MemoryContextReset(per_tuple_context);
4558
4559 /*
4560 * If necessary, become the role that was active when the trigger got
4561 * queued. Note that the role might have been dropped since the trigger
4562 * was queued, but if that is a problem, we will get an error later.
4563 * Checking here would still leave a race condition.
4564 */
4565 GetUserIdAndSecContext(&save_rolid, &save_sec_context);
4566 if (save_rolid != evtshared->ats_rolid)
4568 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
4569
4570 /*
4571 * Call the trigger and throw away any possibly returned updated tuple.
4572 * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4573 */
4574 rettuple = ExecCallTriggerFunc(&LocTriggerData,
4575 tgindx,
4576 finfo,
4577 NULL,
4578 per_tuple_context);
4579 if (rettuple != NULL &&
4580 rettuple != LocTriggerData.tg_trigtuple &&
4581 rettuple != LocTriggerData.tg_newtuple)
4582 heap_freetuple(rettuple);
4583
4584 /* Restore the current role if necessary */
4585 if (save_rolid != evtshared->ats_rolid)
4586 SetUserIdAndSecContext(save_rolid, save_sec_context);
4587
4588 /*
4589 * Release resources
4590 */
4591 if (should_free_trig)
4592 heap_freetuple(LocTriggerData.tg_trigtuple);
4593 if (should_free_new)
4594 heap_freetuple(LocTriggerData.tg_newtuple);
4595
4596 /* don't clear slots' contents if foreign table */
4597 if (trig_tuple_slot1 == NULL)
4598 {
4599 if (LocTriggerData.tg_trigslot)
4600 ExecClearTuple(LocTriggerData.tg_trigslot);
4601 if (LocTriggerData.tg_newslot)
4602 ExecClearTuple(LocTriggerData.tg_newslot);
4603 }
4604
4605 /*
4606 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4607 * one "tuple returned" (really the number of firings).
4608 */
4609 if (instr)
4610 InstrStopNode(instr + tgindx, 1);
4611}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1833
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 heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
void InstrStartNode(Instrumentation *instr)
Definition: instrument.c:68
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:84
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
#define SECURITY_LOCAL_USERID_CHANGE
Definition: miscadmin.h:317
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:612
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:619
#define SnapshotAny
Definition: snapmgr.h:33
ItemPointerData ate_ctid2
Definition: trigger.c:3710
ItemPointerData ate_ctid1
Definition: trigger.c:3709
Tuplestorestate * old_upd_tuplestore
Definition: trigger.c:3930
Tuplestorestate * new_upd_tuplestore
Definition: trigger.c:3932
Tuplestorestate * old_del_tuplestore
Definition: trigger.c:3934
Tuplestorestate * new_ins_tuplestore
Definition: trigger.c:3936
Relation ri_RelationDesc
Definition: execnodes.h:480
Tuplestorestate * tg_oldtable
Definition: trigger.h:41
NodeTag type
Definition: trigger.h:33
Relation tg_relation
Definition: trigger.h:35
const Bitmapset * tg_updatedcols
Definition: trigger.h:43
Tuplestorestate * tg_newtable
Definition: trigger.h:42
TriggerEvent tg_event
Definition: trigger.h:34
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
HeapTuple tg_newtuple
Definition: trigger.h:37
TupleTableSlot * tg_newslot
Definition: trigger.h:40
Trigger * tg_trigger
Definition: trigger.h:38
HeapTuple tg_trigtuple
Definition: trigger.h:36
int numtriggers
Definition: reltrigger.h:50
Trigger * triggers
Definition: reltrigger.h:49
Oid tgoid
Definition: reltrigger.h:25
char * tgoldtable
Definition: reltrigger.h:43
char * tgnewtable
Definition: reltrigger.h:44
int16 tgtype
Definition: reltrigger.h:29
AttrMap * attrMap
Definition: tupconvert.h:28
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1253
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3686
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition: trigger.c:2309
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3981
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3685
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:94
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:96
#define TRIGGER_EVENT_ROW
Definition: trigger.h:98
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:110
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:116
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:192
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1130
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:458
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:525

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, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_modifiedcols, AfterTriggerSharedData::ats_rolid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, TupleConversionMap::attrMap, AfterTriggersTableData::closed, elog, ERROR, ExecCallTriggerFunc(), ExecClearTuple(), ExecCopySlot(), ExecFetchSlotHeapTuple(), ExecGetChildToRootMap(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), execute_attr_map_slot(), GetCurrentFDWTuplestore(), GetTriggerSharedData, GetUserIdAndSecContext(), heap_freetuple(), InstrStartNode(), InstrStopNode(), ItemPointerIsValid(), MemoryContextReset(), AfterTriggersTableData::new_ins_tuplestore, AfterTriggersTableData::new_upd_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_del_tuplestore, AfterTriggersTableData::old_upd_tuplestore, ResultRelInfo::ri_RelationDesc, SECURITY_LOCAL_USERID_CHANGE, SetUserIdAndSecContext(), SnapshotAny, table_tuple_fetch_row_version(), TriggerData::tg_event, TriggerData::tg_newslot, TriggerData::tg_newtable, TriggerData::tg_newtuple, TriggerData::tg_oldtable, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, TriggerData::tg_updatedcols, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgtype, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, TriggerDesc::triggers, tuplestore_gettupleslot(), and TriggerData::type.

Referenced by afterTriggerInvokeEvents().

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

Definition at line 5283 of file trigger.c.

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

References afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, Assert(), AfterTriggersData::events, 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 4214 of file trigger.c.

4215{
4217
4218 while ((chunk = events->head) != NULL)
4219 {
4220 events->head = chunk->next;
4221 pfree(chunk);
4222 }
4223 events->tail = NULL;
4224 events->tailfree = NULL;
4225}

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

5216{
5217 Tuplestorestate *ts;
5218 List *tables;
5219 ListCell *lc;
5220
5221 /* Drop the trigger events */
5223
5224 /* Drop FDW tuplestore if any */
5225 ts = qs->fdw_tuplestore;
5226 qs->fdw_tuplestore = NULL;
5227 if (ts)
5228 tuplestore_end(ts);
5229
5230 /* Release per-table subsidiary storage */
5231 tables = qs->tables;
5232 foreach(lc, tables)
5233 {
5235
5236 ts = table->old_upd_tuplestore;
5237 table->old_upd_tuplestore = NULL;
5238 if (ts)
5239 tuplestore_end(ts);
5240 ts = table->new_upd_tuplestore;
5241 table->new_upd_tuplestore = NULL;
5242 if (ts)
5243 tuplestore_end(ts);
5244 ts = table->old_del_tuplestore;
5245 table->old_del_tuplestore = NULL;
5246 if (ts)
5247 tuplestore_end(ts);
5248 ts = table->new_ins_tuplestore;
5249 table->new_ins_tuplestore = NULL;
5250 if (ts)
5251 tuplestore_end(ts);
5252 if (table->storeslot)
5253 {
5254 TupleTableSlot *slot = table->storeslot;
5255
5256 table->storeslot = NULL;
5258 }
5259 }
5260
5261 /*
5262 * Now free the AfterTriggersTableData structs and list cells. Reset list
5263 * pointer first; if list_free_deep somehow gets an error, better to leak
5264 * that storage than have an infinite loop.
5265 */
5266 qs->tables = NIL;
5267 list_free_deep(tables);
5268}
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1443
void list_free_deep(List *list)
Definition: list.c:1560
Definition: pg_list.h:54
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4214
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:492

References afterTriggerFreeEventList(), AfterTriggersQueryData::events, ExecDropSingleTupleTableSlot(), AfterTriggersQueryData::fdw_tuplestore, lfirst, list_free_deep(), NIL, table, AfterTriggersQueryData::tables, 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 4713 of file trigger.c.

4717{
4718 bool all_fired = true;
4720 MemoryContext per_tuple_context;
4721 bool local_estate = false;
4722 ResultRelInfo *rInfo = NULL;
4723 Relation rel = NULL;
4724 TriggerDesc *trigdesc = NULL;
4725 FmgrInfo *finfo = NULL;
4726 Instrumentation *instr = NULL;
4727 TupleTableSlot *slot1 = NULL,
4728 *slot2 = NULL;
4729
4730 /* Make a local EState if need be */
4731 if (estate == NULL)
4732 {
4733 estate = CreateExecutorState();
4734 local_estate = true;
4735 }
4736
4737 /* Make a per-tuple memory context for trigger function calls */
4738 per_tuple_context =
4740 "AfterTriggerTupleContext",
4742
4743 for_each_chunk(chunk, *events)
4744 {
4745 AfterTriggerEvent event;
4746 bool all_fired_in_chunk = true;
4747
4748 for_each_event(event, chunk)
4749 {
4750 AfterTriggerShared evtshared = GetTriggerSharedData(event);
4751
4752 /*
4753 * Is it one for me to fire?
4754 */
4755 if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4756 evtshared->ats_firing_id == firing_id)
4757 {
4758 ResultRelInfo *src_rInfo,
4759 *dst_rInfo;
4760
4761 /*
4762 * So let's fire it... but first, find the correct relation if
4763 * this is not the same relation as before.
4764 */
4765 if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4766 {
4767 rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4768 NULL);
4769 rel = rInfo->ri_RelationDesc;
4770 /* Catch calls with insufficient relcache refcounting */
4772 trigdesc = rInfo->ri_TrigDesc;
4773 /* caution: trigdesc could be NULL here */
4774 finfo = rInfo->ri_TrigFunctions;
4775 instr = rInfo->ri_TrigInstrument;
4776 if (slot1 != NULL)
4777 {
4780 slot1 = slot2 = NULL;
4781 }
4782 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4783 {
4784 slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4786 slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4788 }
4789 }
4790
4791 /*
4792 * Look up source and destination partition result rels of a
4793 * cross-partition update event.
4794 */
4795 if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4797 {
4798 Assert(OidIsValid(event->ate_src_part) &&
4799 OidIsValid(event->ate_dst_part));
4800 src_rInfo = ExecGetTriggerResultRel(estate,
4801 event->ate_src_part,
4802 rInfo);
4803 dst_rInfo = ExecGetTriggerResultRel(estate,
4804 event->ate_dst_part,
4805 rInfo);
4806 }
4807 else
4808 src_rInfo = dst_rInfo = rInfo;
4809
4810 /*
4811 * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4812 * still set, so recursive examinations of the event list
4813 * won't try to re-fire it.
4814 */
4815 AfterTriggerExecute(estate, event, rInfo,
4816 src_rInfo, dst_rInfo,
4817 trigdesc, finfo, instr,
4818 per_tuple_context, slot1, slot2);
4819
4820 /*
4821 * Mark the event as done.
4822 */
4823 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4824 event->ate_flags |= AFTER_TRIGGER_DONE;
4825 }
4826 else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4827 {
4828 /* something remains to be done */
4829 all_fired = all_fired_in_chunk = false;
4830 }
4831 }
4832
4833 /* Clear the chunk if delete_ok and nothing left of interest */
4834 if (delete_ok && all_fired_in_chunk)
4835 {
4836 chunk->freeptr = CHUNK_DATA_START(chunk);
4837 chunk->endfree = chunk->endptr;
4838
4839 /*
4840 * If it's last chunk, must sync event list's tailfree too. Note
4841 * that delete_ok must NOT be passed as true if there could be
4842 * additional AfterTriggerEventList values pointing at this event
4843 * list, since we'd fail to fix their copies of tailfree.
4844 */
4845 if (chunk == events->tail)
4846 events->tailfree = chunk->freeptr;
4847 }
4848 }
4849 if (slot1 != NULL)
4850 {
4853 }
4854
4855 /* Release working resources */
4856 MemoryContextDelete(per_tuple_context);
4857
4858 if (local_estate)
4859 {
4861 ExecResetTupleTable(estate->es_tupleTable, false);
4862 FreeExecutorState(estate);
4863 }
4864
4865 return all_fired;
4866}
#define OidIsValid(objectId)
Definition: c.h:775
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid, ResultRelInfo *rootRelInfo)
Definition: execMain.c:1344
void ExecCloseResultRelations(EState *estate)
Definition: execMain.c:1565
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1380
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1427
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:86
void FreeExecutorState(EState *estate)
Definition: execUtils.c:192
EState * CreateExecutorState(void)
Definition: execUtils.c:88
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
#define RelationHasReferenceCountZero(relation)
Definition: rel.h:498
#define RelationGetRelid(relation)
Definition: rel.h:514
List * es_tupleTable
Definition: execnodes.h:712
Definition: fmgr.h:57
TupleDesc rd_att
Definition: rel.h:112
Form_pg_class rd_rel
Definition: rel.h:111
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:524
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:515
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:518
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:4337
#define for_each_event(eptr, cptr)
Definition: trigger.c:3783
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3781

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, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_relid, CHUNK_DATA_START, CreateExecutorState(), CurrentMemoryContext, AfterTriggerEventChunk::endfree, AfterTriggerEventChunk::endptr, EState::es_tupleTable, ExecCloseResultRelations(), ExecDropSingleTupleTableSlot(), ExecGetTriggerResultRel(), ExecResetTupleTable(), for_each_chunk, for_each_event, FreeExecutorState(), AfterTriggerEventChunk::freeptr, GetTriggerSharedData, MakeSingleTupleTableSlot(), MemoryContextDelete(), OidIsValid, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, RelationHasReferenceCountZero, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, 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 4629 of file trigger.c.

4632{
4633 bool found = false;
4634 bool deferred_found = false;
4635 AfterTriggerEvent event;
4637
4638 for_each_event_chunk(event, chunk, *events)
4639 {
4640 AfterTriggerShared evtshared = GetTriggerSharedData(event);
4641 bool defer_it = false;
4642
4643 if (!(event->ate_flags &
4645 {
4646 /*
4647 * This trigger hasn't been called or scheduled yet. Check if we
4648 * should call it now.
4649 */
4650 if (immediate_only && afterTriggerCheckState(evtshared))
4651 {
4652 defer_it = true;
4653 }
4654 else
4655 {
4656 /*
4657 * Mark it as to be fired in this firing cycle.
4658 */
4660 event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4661 found = true;
4662 }
4663 }
4664
4665 /*
4666 * If it's deferred, move it to move_list, if requested.
4667 */
4668 if (defer_it && move_list != NULL)
4669 {
4670 deferred_found = true;
4671 /* add it to move_list */
4672 afterTriggerAddEvent(move_list, event, evtshared);
4673 /* mark original copy "done" so we don't do it again */
4674 event->ate_flags |= AFTER_TRIGGER_DONE;
4675 }
4676 }
4677
4678 /*
4679 * We could allow deferred triggers if, before the end of the
4680 * security-restricted operation, we were to verify that a SET CONSTRAINTS
4681 * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4682 */
4683 if (deferred_found && InSecurityRestrictedOperation())
4684 ereport(ERROR,
4685 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4686 errmsg("cannot fire deferred trigger within security-restricted operation")));
4687
4688 return found;
4689}
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ereport(elevel,...)
Definition: elog.h:150
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:639
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:4087
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:4017

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

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

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

Definition at line 6060 of file trigger.c.

6061{
6062 AfterTriggerEvent event;
6064 int depth;
6065
6066 /* Scan queued events */
6068 {
6069 AfterTriggerShared evtshared = GetTriggerSharedData(event);
6070
6071 /*
6072 * We can ignore completed events. (Even if a DONE flag is rolled
6073 * back by subxact abort, it's OK because the effects of the TRUNCATE
6074 * or whatever must get rolled back too.)
6075 */
6076 if (event->ate_flags & AFTER_TRIGGER_DONE)
6077 continue;
6078
6079 if (evtshared->ats_relid == relid)
6080 return true;
6081 }
6082
6083 /*
6084 * Also scan events queued by incomplete queries. This could only matter
6085 * if TRUNCATE/etc is executed by a function or trigger within an updating
6086 * query on the same relation, which is pretty perverse, but let's check.
6087 */
6088 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6089 {
6091 {
6092 AfterTriggerShared evtshared = GetTriggerSharedData(event);
6093
6094 if (event->ate_flags & AFTER_TRIGGER_DONE)
6095 continue;
6096
6097 if (evtshared->ats_relid == relid)
6098 return true;
6099 }
6100 }
6101
6102 return false;
6103}

References AFTER_TRIGGER_DONE, afterTriggers, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_relid, AfterTriggersData::events, AfterTriggersQueryData::events, 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 4235 of file trigger.c.

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

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

6155{
6156 Relation rel = relinfo->ri_RelationDesc;
6157 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
6158 AfterTriggerEventData new_event;
6159 AfterTriggerSharedData new_shared;
6160 char relkind = rel->rd_rel->relkind;
6161 int tgtype_event;
6162 int tgtype_level;
6163 int i;
6164 Tuplestorestate *fdw_tuplestore = NULL;
6165
6166 /*
6167 * Check state. We use a normal test not Assert because it is possible to
6168 * reach here in the wrong state given misconfigured RI triggers, in
6169 * particular deferring a cascade action trigger.
6170 */
6171 if (afterTriggers.query_depth < 0)
6172 elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
6173
6174 /* Be sure we have enough space to record events at this query depth. */
6177
6178 /*
6179 * If the directly named relation has any triggers with transition tables,
6180 * then we need to capture transition tuples.
6181 */
6182 if (row_trigger && transition_capture != NULL)
6183 {
6184 TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
6185
6186 /*
6187 * Capture the old tuple in the appropriate transition table based on
6188 * the event.
6189 */
6190 if (!TupIsNull(oldslot))
6191 {
6192 Tuplestorestate *old_tuplestore;
6193
6194 old_tuplestore = GetAfterTriggersTransitionTable(event,
6195 oldslot,
6196 NULL,
6197 transition_capture);
6198 TransitionTableAddTuple(estate, transition_capture, relinfo,
6199 oldslot, NULL, old_tuplestore);
6200 }
6201
6202 /*
6203 * Capture the new tuple in the appropriate transition table based on
6204 * the event.
6205 */
6206 if (!TupIsNull(newslot))
6207 {
6208 Tuplestorestate *new_tuplestore;
6209
6210 new_tuplestore = GetAfterTriggersTransitionTable(event,
6211 NULL,
6212 newslot,
6213 transition_capture);
6214 TransitionTableAddTuple(estate, transition_capture, relinfo,
6215 newslot, original_insert_tuple, new_tuplestore);
6216 }
6217
6218 /*
6219 * If transition tables are the only reason we're here, return. As
6220 * mentioned above, we can also be here during update tuple routing in
6221 * presence of transition tables, in which case this function is
6222 * called separately for OLD and NEW, so we expect exactly one of them
6223 * to be NULL.
6224 */
6225 if (trigdesc == NULL ||
6226 (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
6227 (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
6228 (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
6229 (event == TRIGGER_EVENT_UPDATE && (TupIsNull(oldslot) ^ TupIsNull(newslot))))
6230 return;
6231 }
6232
6233 /*
6234 * We normally don't see partitioned tables here for row level triggers
6235 * except in the special case of a cross-partition update. In that case,
6236 * nodeModifyTable.c:ExecCrossPartitionUpdateForeignKey() calls here to
6237 * queue an update event on the root target partitioned table, also
6238 * passing the source and destination partitions and their tuples.
6239 */
6240 Assert(!row_trigger ||
6241 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
6242 (is_crosspart_update &&
6243 TRIGGER_FIRED_BY_UPDATE(event) &&
6244 src_partinfo != NULL && dst_partinfo != NULL));
6245
6246 /*
6247 * Validate the event code and collect the associated tuple CTIDs.
6248 *
6249 * The event code will be used both as a bitmask and an array offset, so
6250 * validation is important to make sure we don't walk off the edge of our
6251 * arrays.
6252 *
6253 * Also, if we're considering statement-level triggers, check whether we
6254 * already queued a set of them for this event, and cancel the prior set
6255 * if so. This preserves the behavior that statement-level triggers fire
6256 * just once per statement and fire after row-level triggers.
6257 */
6258 switch (event)
6259 {
6261 tgtype_event = TRIGGER_TYPE_INSERT;
6262 if (row_trigger)
6263 {
6264 Assert(oldslot == NULL);
6265 Assert(newslot != NULL);
6266 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
6267 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6268 }
6269 else
6270 {
6271 Assert(oldslot == NULL);
6272 Assert(newslot == NULL);
6273 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6274 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6276 CMD_INSERT, event);
6277 }
6278 break;
6280 tgtype_event = TRIGGER_TYPE_DELETE;
6281 if (row_trigger)
6282 {
6283 Assert(oldslot != NULL);
6284 Assert(newslot == NULL);
6285 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6286 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6287 }
6288 else
6289 {
6290 Assert(oldslot == NULL);
6291 Assert(newslot == NULL);
6292 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6293 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6295 CMD_DELETE, event);
6296 }
6297 break;
6299 tgtype_event = TRIGGER_TYPE_UPDATE;
6300 if (row_trigger)
6301 {
6302 Assert(oldslot != NULL);
6303 Assert(newslot != NULL);
6304 ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6305 ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
6306
6307 /*
6308 * Also remember the OIDs of partitions to fetch these tuples
6309 * out of later in AfterTriggerExecute().
6310 */
6311 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6312 {
6313 Assert(src_partinfo != NULL && dst_partinfo != NULL);
6314 new_event.ate_src_part =
6315 RelationGetRelid(src_partinfo->ri_RelationDesc);
6316 new_event.ate_dst_part =
6317 RelationGetRelid(dst_partinfo->ri_RelationDesc);
6318 }
6319 }
6320 else
6321 {
6322 Assert(oldslot == NULL);
6323 Assert(newslot == NULL);
6324 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6325 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6327 CMD_UPDATE, event);
6328 }
6329 break;
6331 tgtype_event = TRIGGER_TYPE_TRUNCATE;
6332 Assert(oldslot == NULL);
6333 Assert(newslot == NULL);
6334 ItemPointerSetInvalid(&(new_event.ate_ctid1));
6335 ItemPointerSetInvalid(&(new_event.ate_ctid2));
6336 break;
6337 default:
6338 elog(ERROR, "invalid after-trigger event code: %d", event);
6339 tgtype_event = 0; /* keep compiler quiet */
6340 break;
6341 }
6342
6343 /* Determine flags */
6344 if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
6345 {
6346 if (row_trigger && event == TRIGGER_EVENT_UPDATE)
6347 {
6348 if (relkind == RELKIND_PARTITIONED_TABLE)
6350 else
6351 new_event.ate_flags = AFTER_TRIGGER_2CTID;
6352 }
6353 else
6354 new_event.ate_flags = AFTER_TRIGGER_1CTID;
6355 }
6356
6357 /* else, we'll initialize ate_flags for each trigger */
6358
6359 tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
6360
6361 /*
6362 * Must convert/copy the source and destination partition tuples into the
6363 * root partitioned table's format/slot, because the processing in the
6364 * loop below expects both oldslot and newslot tuples to be in that form.
6365 */
6366 if (row_trigger && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6367 {
6368 TupleTableSlot *rootslot;
6369 TupleConversionMap *map;
6370
6371 rootslot = ExecGetTriggerOldSlot(estate, relinfo);
6372 map = ExecGetChildToRootMap(src_partinfo);
6373 if (map)
6374 oldslot = execute_attr_map_slot(map->attrMap,
6375 oldslot,
6376 rootslot);
6377 else
6378 oldslot = ExecCopySlot(rootslot, oldslot);
6379
6380 rootslot = ExecGetTriggerNewSlot(estate, relinfo);
6381 map = ExecGetChildToRootMap(dst_partinfo);
6382 if (map)
6383 newslot = execute_attr_map_slot(map->attrMap,
6384 newslot,
6385 rootslot);
6386 else
6387 newslot = ExecCopySlot(rootslot, newslot);
6388 }
6389
6390 for (i = 0; i < trigdesc->numtriggers; i++)
6391 {
6392 Trigger *trigger = &trigdesc->triggers[i];
6393
6394 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
6395 tgtype_level,
6396 TRIGGER_TYPE_AFTER,
6397 tgtype_event))
6398 continue;
6399 if (!TriggerEnabled(estate, relinfo, trigger, event,
6400 modifiedCols, oldslot, newslot))
6401 continue;
6402
6403 if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
6404 {
6405 if (fdw_tuplestore == NULL)
6406 {
6407 fdw_tuplestore = GetCurrentFDWTuplestore();
6409 }
6410 else
6411 /* subsequent event for the same tuple */
6413 }
6414
6415 /*
6416 * If the trigger is a foreign key enforcement trigger, there are
6417 * certain cases where we can skip queueing the event because we can
6418 * tell by inspection that the FK constraint will still pass. There
6419 * are also some cases during cross-partition updates of a partitioned
6420 * table where queuing the event can be skipped.
6421 */
6423 {
6424 switch (RI_FKey_trigger_type(trigger->tgfoid))
6425 {
6426 case RI_TRIGGER_PK:
6427
6428 /*
6429 * For cross-partitioned updates of partitioned PK table,
6430 * skip the event fired by the component delete on the
6431 * source leaf partition unless the constraint originates
6432 * in the partition itself (!tgisclone), because the
6433 * update event that will be fired on the root
6434 * (partitioned) target table will be used to perform the
6435 * necessary foreign key enforcement action.
6436 */
6437 if (is_crosspart_update &&
6438 TRIGGER_FIRED_BY_DELETE(event) &&
6439 trigger->tgisclone)
6440 continue;
6441
6442 /* Update or delete on trigger's PK table */
6443 if (!RI_FKey_pk_upd_check_required(trigger, rel,
6444 oldslot, newslot))
6445 {
6446 /* skip queuing this event */
6447 continue;
6448 }
6449 break;
6450
6451 case RI_TRIGGER_FK:
6452
6453 /*
6454 * Update on trigger's FK table. We can skip the update
6455 * event fired on a partitioned table during a
6456 * cross-partition of that table, because the insert event
6457 * that is fired on the destination leaf partition would
6458 * suffice to perform the necessary foreign key check.
6459 * Moreover, RI_FKey_fk_upd_check_required() expects to be
6460 * passed a tuple that contains system attributes, most of
6461 * which are not present in the virtual slot belonging to
6462 * a partitioned table.
6463 */
6464 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
6465 !RI_FKey_fk_upd_check_required(trigger, rel,
6466 oldslot, newslot))
6467 {
6468 /* skip queuing this event */
6469 continue;
6470 }
6471 break;
6472
6473 case RI_TRIGGER_NONE:
6474
6475 /*
6476 * Not an FK trigger. No need to queue the update event
6477 * fired during a cross-partitioned update of a
6478 * partitioned table, because the same row trigger must be
6479 * present in the leaf partition(s) that are affected as
6480 * part of this update and the events fired on them are
6481 * queued instead.
6482 */
6483 if (row_trigger &&
6484 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6485 continue;
6486 break;
6487 }
6488 }
6489
6490 /*
6491 * If the trigger is a deferred unique constraint check trigger, only
6492 * queue it if the unique constraint was potentially violated, which
6493 * we know from index insertion time.
6494 */
6495 if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
6496 {
6497 if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
6498 continue; /* Uniqueness definitely not violated */
6499 }
6500
6501 /*
6502 * Fill in event structure and add it to the current query's queue.
6503 * Note we set ats_table to NULL whenever this trigger doesn't use
6504 * transition tables, to improve sharability of the shared event data.
6505 */
6506 new_shared.ats_event =
6507 (event & TRIGGER_EVENT_OPMASK) |
6508 (row_trigger ? TRIGGER_EVENT_ROW : 0) |
6509 (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
6511 new_shared.ats_tgoid = trigger->tgoid;
6512 new_shared.ats_relid = RelationGetRelid(rel);
6513 new_shared.ats_rolid = GetUserId();
6514 new_shared.ats_firing_id = 0;
6515 if ((trigger->tgoldtable || trigger->tgnewtable) &&
6516 transition_capture != NULL)
6517 new_shared.ats_table = transition_capture->tcs_private;
6518 else
6519 new_shared.ats_table = NULL;
6520 new_shared.ats_modifiedcols = modifiedCols;
6521
6523 &new_event, &new_shared);
6524 }
6525
6526 /*
6527 * Finally, spool any foreign tuple(s). The tuplestore squashes them to
6528 * minimal tuples, so this loses any system columns. The executor lost
6529 * those columns before us, for an unrelated reason, so this is fine.
6530 */
6531 if (fdw_tuplestore)
6532 {
6533 if (oldslot != NULL)
6534 tuplestore_puttupleslot(fdw_tuplestore, oldslot);
6535 if (newslot != NULL)
6536 tuplestore_puttupleslot(fdw_tuplestore, newslot);
6537 }
6538}
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition: itemptr.h:172
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
Oid GetUserId(void)
Definition: miscinit.c:469
@ CMD_INSERT
Definition: nodes.h:277
@ CMD_DELETE
Definition: nodes.h:278
@ CMD_UPDATE
Definition: nodes.h:276
bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: ri_triggers.c:1384
bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: ri_triggers.c:1416
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3208
struct AfterTriggersTableData * tcs_private
Definition: trigger.h:81
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:76
bool trig_update_after_row
Definition: reltrigger.h:62
bool trig_insert_after_row
Definition: reltrigger.h:57
bool trig_delete_after_row
Definition: reltrigger.h:67
Oid tgconstrindid
Definition: reltrigger.h:34
Oid tgfoid
Definition: reltrigger.h:28
bool tgdeferrable
Definition: reltrigger.h:36
bool tginitdeferred
Definition: reltrigger.h:37
bool tgisclone
Definition: reltrigger.h:32
ItemPointerData tts_tid
Definition: tuptable.h:129
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:6591
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: trigger.c:3482
static Tuplestorestate * GetAfterTriggersTransitionTable(int event, TupleTableSlot *oldslot, TupleTableSlot *newslot, TransitionCaptureState *transition_capture)
Definition: trigger.c:5532
static void TransitionTableAddTuple(EState *estate, TransitionCaptureState *transition_capture, ResultRelInfo *relinfo, TupleTableSlot *slot, TupleTableSlot *original_insert_tuple, Tuplestorestate *tuplestore)
Definition: trigger.c:5583
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:5623
#define RI_TRIGGER_FK
Definition: trigger.h:285
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:113
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:93
#define RI_TRIGGER_NONE
Definition: trigger.h:286
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:92
#define TRIGGER_EVENT_TRUNCATE
Definition: trigger.h:95
#define RI_TRIGGER_PK
Definition: trigger.h:284
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:742
#define TupIsNull(slot)
Definition: tuptable.h:310

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(), AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_dst_part, AfterTriggerEventData::ate_flags, AfterTriggerEventData::ate_src_part, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_modifiedcols, AfterTriggerSharedData::ats_relid, AfterTriggerSharedData::ats_rolid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, TupleConversionMap::attrMap, cancel_prior_stmt_triggers(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, AfterTriggersQueryData::events, ExecCopySlot(), ExecGetChildToRootMap(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), execute_attr_map_slot(), 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(), ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, RI_TRIGGER_FK, RI_TRIGGER_NONE, RI_TRIGGER_PK, TransitionCaptureState::tcs_original_insert_tuple, TransitionCaptureState::tcs_private, Trigger::tgconstrindid, Trigger::tgdeferrable, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgisclone, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgtype, 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, TupleTableSlot::tts_tid, TupIsNull, and tuplestore_puttupleslot().

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

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5745 of file trigger.c.

5746{
5747 int my_level = GetCurrentTransactionNestLevel();
5748
5749 /* If we haven't already done so, initialize our state. */
5750 if (afterTriggers.state == NULL)
5752
5753 /*
5754 * If in a subtransaction, and we didn't save the current state already,
5755 * save it so it can be restored if the subtransaction aborts.
5756 */
5757 if (my_level > 1 &&
5758 afterTriggers.trans_stack[my_level].state == NULL)
5759 {
5760 afterTriggers.trans_stack[my_level].state =
5762 }
5763
5764 /*
5765 * Handle SET CONSTRAINTS ALL ...
5766 */
5767 if (stmt->constraints == NIL)
5768 {
5769 /*
5770 * Forget any previous SET CONSTRAINTS commands in this transaction.
5771 */
5773
5774 /*
5775 * Set the per-transaction ALL state to known.
5776 */
5779 }
5780 else
5781 {
5782 Relation conrel;
5783 Relation tgrel;
5784 List *conoidlist = NIL;
5785 List *tgoidlist = NIL;
5786 ListCell *lc;
5787
5788 /*
5789 * Handle SET CONSTRAINTS constraint-name [, ...]
5790 *
5791 * First, identify all the named constraints and make a list of their
5792 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5793 * the same name within a schema, the specifications are not
5794 * necessarily unique. Our strategy is to target all matching
5795 * constraints within the first search-path schema that has any
5796 * matches, but disregard matches in schemas beyond the first match.
5797 * (This is a bit odd but it's the historical behavior.)
5798 *
5799 * A constraint in a partitioned table may have corresponding
5800 * constraints in the partitions. Grab those too.
5801 */
5802 conrel = table_open(ConstraintRelationId, AccessShareLock);
5803
5804 foreach(lc, stmt->constraints)
5805 {
5806 RangeVar *constraint = lfirst(lc);
5807 bool found;
5808 List *namespacelist;
5809 ListCell *nslc;
5810
5811 if (constraint->catalogname)
5812 {
5813 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5814 ereport(ERROR,
5815 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5816 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5817 constraint->catalogname, constraint->schemaname,
5818 constraint->relname)));
5819 }
5820
5821 /*
5822 * If we're given the schema name with the constraint, look only
5823 * in that schema. If given a bare constraint name, use the
5824 * search path to find the first matching constraint.
5825 */
5826 if (constraint->schemaname)
5827 {
5828 Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5829 false);
5830
5831 namespacelist = list_make1_oid(namespaceId);
5832 }
5833 else
5834 {
5835 namespacelist = fetch_search_path(true);
5836 }
5837
5838 found = false;
5839 foreach(nslc, namespacelist)
5840 {
5841 Oid namespaceId = lfirst_oid(nslc);
5842 SysScanDesc conscan;
5843 ScanKeyData skey[2];
5844 HeapTuple tup;
5845
5846 ScanKeyInit(&skey[0],
5847 Anum_pg_constraint_conname,
5848 BTEqualStrategyNumber, F_NAMEEQ,
5849 CStringGetDatum(constraint->relname));
5850 ScanKeyInit(&skey[1],
5851 Anum_pg_constraint_connamespace,
5852 BTEqualStrategyNumber, F_OIDEQ,
5853 ObjectIdGetDatum(namespaceId));
5854
5855 conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5856 true, NULL, 2, skey);
5857
5858 while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5859 {
5861
5862 if (con->condeferrable)
5863 conoidlist = lappend_oid(conoidlist, con->oid);
5864 else if (stmt->deferred)
5865 ereport(ERROR,
5866 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5867 errmsg("constraint \"%s\" is not deferrable",
5868 constraint->relname)));
5869 found = true;
5870 }
5871
5872 systable_endscan(conscan);
5873
5874 /*
5875 * Once we've found a matching constraint we do not search
5876 * later parts of the search path.
5877 */
5878 if (found)
5879 break;
5880 }
5881
5882 list_free(namespacelist);
5883
5884 /*
5885 * Not found ?
5886 */
5887 if (!found)
5888 ereport(ERROR,
5889 (errcode(ERRCODE_UNDEFINED_OBJECT),
5890 errmsg("constraint \"%s\" does not exist",
5891 constraint->relname)));
5892 }
5893
5894 /*
5895 * Scan for any possible descendants of the constraints. We append
5896 * whatever we find to the same list that we're scanning; this has the
5897 * effect that we create new scans for those, too, so if there are
5898 * further descendents, we'll also catch them.
5899 */
5900 foreach(lc, conoidlist)
5901 {
5902 Oid parent = lfirst_oid(lc);
5904 SysScanDesc scan;
5905 HeapTuple tuple;
5906
5908 Anum_pg_constraint_conparentid,
5909 BTEqualStrategyNumber, F_OIDEQ,
5910 ObjectIdGetDatum(parent));
5911
5912 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5913
5914 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5915 {
5917
5918 conoidlist = lappend_oid(conoidlist, con->oid);
5919 }
5920
5921 systable_endscan(scan);
5922 }
5923
5925
5926 /*
5927 * Now, locate the trigger(s) implementing each of these constraints,
5928 * and make a list of their OIDs.
5929 */
5930 tgrel = table_open(TriggerRelationId, AccessShareLock);
5931
5932 foreach(lc, conoidlist)
5933 {
5934 Oid conoid = lfirst_oid(lc);
5935 ScanKeyData skey;
5936 SysScanDesc tgscan;
5937 HeapTuple htup;
5938
5939 ScanKeyInit(&skey,
5940 Anum_pg_trigger_tgconstraint,
5941 BTEqualStrategyNumber, F_OIDEQ,
5942 ObjectIdGetDatum(conoid));
5943
5944 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5945 NULL, 1, &skey);
5946
5947 while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5948 {
5949 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5950
5951 /*
5952 * Silently skip triggers that are marked as non-deferrable in
5953 * pg_trigger. This is not an error condition, since a
5954 * deferrable RI constraint may have some non-deferrable
5955 * actions.
5956 */
5957 if (pg_trigger->tgdeferrable)
5958 tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid);
5959 }
5960
5961 systable_endscan(tgscan);
5962 }
5963
5965
5966 /*
5967 * Now we can set the trigger states of individual triggers for this
5968 * xact.
5969 */
5970 foreach(lc, tgoidlist)
5971 {
5972 Oid tgoid = lfirst_oid(lc);
5974 bool found = false;
5975 int i;
5976
5977 for (i = 0; i < state->numstates; i++)
5978 {
5979 if (state->trigstates[i].sct_tgoid == tgoid)
5980 {
5981 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5982 found = true;
5983 break;
5984 }
5985 }
5986 if (!found)
5987 {
5989 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5990 }
5991 }
5992 }
5993
5994 /*
5995 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5996 * checks against that constraint must be made when the SET CONSTRAINTS
5997 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5998 * apply retroactively. We've updated the constraints state, so scan the
5999 * list of previously deferred events to fire any that have now become
6000 * immediate.
6001 *
6002 * Obviously, if this was SET ... DEFERRED then it can't have converted
6003 * any unfired events to immediate, so we need do nothing in that case.
6004 */
6005 if (!stmt->deferred)
6006 {
6008 bool snapshot_set = false;
6009
6010 while (afterTriggerMarkEvents(events, NULL, true))
6011 {
6013
6014 /*
6015 * Make sure a snapshot has been established in case trigger
6016 * functions need one. Note that we avoid setting a snapshot if
6017 * we don't find at least one trigger that has to be fired now.
6018 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6019 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6020 * at the start of a transaction it's not possible for any trigger
6021 * events to be queued yet.)
6022 */
6023 if (!snapshot_set)
6024 {
6026 snapshot_set = true;
6027 }
6028
6029 /*
6030 * We can delete fired events if we are at top transaction level,
6031 * but we'd better not if inside a subtransaction, since the
6032 * subtransaction could later get rolled back.
6033 */
6034 if (afterTriggerInvokeEvents(events, firing_id, NULL,
6035 !IsSubTransaction()))
6036 break; /* all fired */
6037 }
6038
6039 if (snapshot_set)
6041 }
6042}
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
Oid MyDatabaseId
Definition: globals.c:94
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
#define stmt
Definition: indent_codes.h:59
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
void list_free(List *list)
Definition: list.c:1546
#define AccessShareLock
Definition: lockdefs.h:36
char * get_database_name(Oid dbid)
Definition: lsyscache.c:1259
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:3455
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4889
FormData_pg_constraint * Form_pg_constraint
#define list_make1_oid(x1)
Definition: pg_list.h:242
#define lfirst_oid(lc)
Definition: pg_list.h:174
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:360
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
char * relname
Definition: primnodes.h:83
char * catalogname
Definition: primnodes.h:77
char * schemaname
Definition: primnodes.h:80
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
static SetConstraintState SetConstraintStateCopy(SetConstraintState origstate)
Definition: trigger.c:5695
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition: trigger.c:5715
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:5670
bool IsSubTransaction(void)
Definition: xact.c:5056

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, BTEqualStrategyNumber, RangeVar::catalogname, CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, AfterTriggersData::events, fetch_search_path(), AfterTriggersData::firing_counter, get_database_name(), GetCurrentTransactionNestLevel(), GETSTRUCT(), GetTransactionSnapshot(), HeapTupleIsValid, i, IsSubTransaction(), sort-test::key, 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 6666 of file trigger.c.

6667{
6668 /*
6669 * Must flush the plan cache when changing replication role; but don't
6670 * flush unnecessarily.
6671 */
6674}
#define newval
void ResetPlanCache(void)
Definition: plancache.c:2323
int SessionReplicationRole
Definition: trigger.c:63

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6545 of file trigger.c.

6546{
6547 bool result;
6549
6550 /* Check state, like AfterTriggerSaveEvent. */
6551 if (afterTriggers.query_depth < 0)
6552 elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6553
6554 /* Be sure we have enough space to record events at this query depth. */
6557
6558 /*
6559 * We keep this state in the AfterTriggersTableData that also holds
6560 * transition tables for the relation + operation. In this way, if we are
6561 * forced to make a new set of transition tables because more tuples get
6562 * entered after we've already fired triggers, we will allow a new set of
6563 * statement triggers to get queued.
6564 */
6565 table = GetAfterTriggersTableData(relid, cmdType);
6566 result = table->before_trig_done;
6567 table->before_trig_done = true;
6568 return result;
6569}
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4882

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

6592{
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 without canceling the old ones.
6602 */
6603 table = GetAfterTriggersTableData(relid, cmdType);
6604
6605 if (table->after_trig_done)
6606 {
6607 /*
6608 * We want to start scanning from the tail location that existed just
6609 * before we inserted any statement triggers. But the events list
6610 * might've been entirely empty then, in which case scan from the
6611 * current head.
6612 */
6613 AfterTriggerEvent event;
6615
6616 if (table->after_trig_events.tail)
6617 {
6618 chunk = table->after_trig_events.tail;
6619 event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6620 }
6621 else
6622 {
6623 chunk = qs->events.head;
6624 event = NULL;
6625 }
6626
6627 for_each_chunk_from(chunk)
6628 {
6629 if (event == NULL)
6630 event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6631 for_each_event_from(event, chunk)
6632 {
6633 AfterTriggerShared evtshared = GetTriggerSharedData(event);
6634
6635 /*
6636 * Exit loop when we reach events that aren't AS triggers for
6637 * the target relation.
6638 */
6639 if (evtshared->ats_relid != relid)
6640 goto done;
6641 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6642 goto done;
6643 if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6644 goto done;
6645 if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6646 goto done;
6647 /* OK, mark it DONE */
6648 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6649 event->ate_flags |= AFTER_TRIGGER_DONE;
6650 }
6651 /* signal we must reinitialize event ptr for next chunk */
6652 event = NULL;
6653 }
6654 }
6655done:
6656
6657 /* In any case, save current insertion point for next time */
6658 table->after_trig_done = true;
6659 table->after_trig_events = qs->events;
6660}
#define for_each_chunk_from(cptr)
Definition: trigger.c:3792
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3794
#define TRIGGER_FIRED_FOR_STATEMENT(event)
Definition: trigger.h:125
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:131

References AFTER_TRIGGER_DONE, afterTriggers, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_relid, CHUNK_DATA_START, AfterTriggersQueryData::events, for_each_chunk_from, for_each_event_from, GetAfterTriggersTableData(), GetTriggerSharedData, AfterTriggerEventList::head, 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 6696 of file trigger.c.

6697{
6698 if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
6699 return tuple;
6700
6701 for (int i = 0; i < tupdesc->natts; i++)
6702 {
6703 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6704 {
6705 if (!heap_attisnull(tuple, i + 1, tupdesc))
6706 {
6707 int replCol = i + 1;
6708 Datum replValue = 0;
6709 bool replIsnull = true;
6710
6711 tuple = heap_modify_tuple_by_cols(tuple, tupdesc, 1, &replCol, &replValue, &replIsnull);
6712 }
6713 }
6714 }
6715
6716 return tuple;
6717}
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
uint64_t Datum
Definition: postgres.h:70
bool has_generated_virtual
Definition: tupdesc.h:47
TupleConstr * constr
Definition: tupdesc.h:141
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160

References TupleDescData::constr, 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{
2092 TriggerDesc *newdesc;
2093 Trigger *trigger;
2094 int i;
2095
2096 if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2097 return NULL;
2098
2099 newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
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:534
int j
Definition: isn.c:78
char * pstrdup(const char *in)
Definition: mcxt.c:1759
void * palloc(Size size)
Definition: mcxt.c:1365
char * tgname
Definition: reltrigger.h:27
int16 tgnargs
Definition: reltrigger.h:38
char * tgqual
Definition: reltrigger.h:42
int16 tgnattr
Definition: reltrigger.h:39
char ** tgargs
Definition: reltrigger.h:41
int16 * tgattr
Definition: reltrigger.h:40

References i, j, TriggerDesc::numtriggers, palloc(), pstrdup(), Trigger::tgargs, Trigger::tgattr, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgnewtable, Trigger::tgoldtable, Trigger::tgqual, 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,
169 in_partition, TRIGGER_FIRES_ON_ORIGIN);
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:149

References CreateTriggerFiringOn(), 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;
187 List *whenRtable;
188 char *qual;
189 Datum values[Natts_pg_trigger];
190 bool nulls[Natts_pg_trigger];
191 Relation rel;
192 AclResult aclresult;
193 Relation tgrel;
194 Relation pgrel;
195 HeapTuple tuple = NULL;
196 Oid funcrettype;
197 Oid trigoid = InvalidOid;
198 char internaltrigname[NAMEDATALEN];
199 char *trigname;
200 Oid constrrelid = InvalidOid;
201 ObjectAddress myself,
202 referenced;
203 char *oldtablename = NULL;
204 char *newtablename = NULL;
205 bool partition_recurse;
206 bool trigger_exists = false;
207 Oid existing_constraint_oid = InvalidOid;
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)
226 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
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)
237 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
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)
260 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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)
274 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
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))
281 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
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)
291 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
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)
303 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
304 errmsg("\"%s\" is a foreign table",
306 errdetail("Foreign tables cannot have constraint triggers.")));
307 }
308 else
310 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
311 errmsg("relation \"%s\" cannot have triggers",
314
317 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
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 */
330 if (OidIsValid(refRelOid))
331 {
333 constrrelid = refRelOid;
334 }
335 else if (stmt->constrrel != NULL)
336 constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock,
337 false);
338 }
339
340 /* permission checks */
341 if (!isInternal)
342 {
343 aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
345 if (aclresult != ACLCHECK_OK)
346 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
348
349 if (OidIsValid(constrrelid))
350 {
351 aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
353 if (aclresult != ACLCHECK_OK)
354 aclcheck_error(aclresult, get_relkind_objtype(get_rel_relkind(constrrelid)),
355 get_rel_name(constrrelid));
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;
367 if (partition_recurse)
369 ShareRowExclusiveLock, NULL));
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))
381 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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))
389 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
390 errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
391 if (stmt->whenClause)
393 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
394 errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
395 if (stmt->columns != NIL)
397 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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))
426 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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)
438 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
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)
445 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
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)
463 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
464 errmsg("ROW triggers with transition tables are not supported on partitions")));
465 else
467 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
468 errmsg("ROW triggers with transition tables are not supported on inheritance children")));
469 }
470
471 if (stmt->timing != TRIGGER_TYPE_AFTER)
473 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
474 errmsg("transition table name can only be specified for an AFTER trigger")));
475
476 if (TRIGGER_FOR_TRUNCATE(tgtype))
478 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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)
495 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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)
506 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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)))
523 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
524 errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger")));
525
526 if (newtablename != NULL)
528 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
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)))
538 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
539 errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger")));
540
541 if (oldtablename != NULL)
543 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
544 errmsg("OLD TABLE cannot be specified multiple times")));
545
546 oldtablename = tt->name;
547 }
548 }
549
550 if (newtablename != NULL && oldtablename != NULL &&
551 strcmp(newtablename, oldtablename) == 0)
553 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
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;
568 ParseNamespaceItem *nsitem;
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 */
581 nsitem = addRangeTableEntryForRelation(pstate, rel,
583 makeAlias("old", NIL),
584 false, false);
585 addNSItemToQuery(pstate, nsitem, false, true, true);
586 nsitem = addRangeTableEntryForRelation(pstate, rel,
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))
617 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
618 errmsg("statement trigger's WHEN condition cannot reference column values"),
619 parser_errposition(pstate, var->location)));
620 if (TRIGGER_FOR_INSERT(tgtype))
622 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
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))
630 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
631 errmsg("statement trigger's WHEN condition cannot reference column values"),
632 parser_errposition(pstate, var->location)));
633 if (TRIGGER_FOR_DELETE(tgtype))
635 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
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))
640 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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))
649 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
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)
657 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
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 {
696 aclresult = object_aclcheck(ProcedureRelationId, funcoid, GetUserId(), ACL_EXECUTE);
697 if (aclresult != ACLCHECK_OK)
699 NameListToString(stmt->funcname));
700 }
701 funcrettype = get_func_rettype(funcoid);
702 if (funcrettype != TRIGGEROID)
704 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
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 */
716 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
717 if (!isInternal)
718 {
719 ScanKeyData skeys[2];
720 SysScanDesc tgscan;
721
722 ScanKeyInit(&skeys[0],
723 Anum_pg_trigger_tgrelid,
724 BTEqualStrategyNumber, F_OIDEQ,
726
727 ScanKeyInit(&skeys[1],
728 Anum_pg_trigger_tgname,
729 BTEqualStrategyNumber, F_NAMEEQ,
730 CStringGetDatum(stmt->trigname));
731
732 tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
733 NULL, 2, skeys);
734
735 /* There should be at most one matching tuple */
736 if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
737 {
738 Form_pg_trigger oldtrigger = (Form_pg_trigger) GETSTRUCT(tuple);
739
740 trigoid = oldtrigger->oid;
741 existing_constraint_oid = oldtrigger->tgconstraint;
742 existing_isInternal = oldtrigger->tgisinternal;
743 existing_isClone = OidIsValid(oldtrigger->tgparentid);
744 trigger_exists = true;
745 /* copy the tuple to use in CatalogTupleUpdate() */
746 tuple = heap_copytuple(tuple);
747 }
748 systable_endscan(tgscan);
749 }
750
751 if (!trigger_exists)
752 {
753 /* Generate the OID for the new trigger. */
754 trigoid = GetNewOidWithIndex(tgrel, TriggerOidIndexId,
755 Anum_pg_trigger_oid);
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 */
775 if ((existing_isInternal || existing_isClone) &&
776 !isInternal && !in_partition)
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 */
795 if (OidIsValid(existing_constraint_oid))
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 */
809 Assert(!isInternal);
810 constraintOid = CreateConstraintEntry(stmt->trigname,
812 CONSTRAINT_TRIGGER,
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 {
852 snprintf(internaltrigname, sizeof(internaltrigname),
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
867 values[Anum_pg_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
868 values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
869 values[Anum_pg_trigger_tgparentid - 1] = ObjectIdGetDatum(parentTriggerOid);
870 values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
871 CStringGetDatum(trigname));
872 values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
873 values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
874 values[Anum_pg_trigger_tgenabled - 1] = CharGetDatum(trigger_fires_when);
875 values[Anum_pg_trigger_tgisinternal - 1] = BoolGetDatum(isInternal);
876 values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
877 values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid);
878 values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
879 values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
880 values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
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 }
915 values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
916 values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
918 }
919 else
920 {
921 values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
922 values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
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);
946 (errcode(ERRCODE_UNDEFINED_COLUMN),
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)
955 (errcode(ERRCODE_DUPLICATE_COLUMN),
956 errmsg("column \"%s\" specified more than once",
957 name)));
958 }
959
960 columns[i++] = attnum;
961 }
962 }
963 tgattr = buildint2vector(columns, ncolumns);
964 values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
965
966 /* set tgqual if trigger has WHEN clause */
967 if (qual)
968 values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(qual);
969 else
970 nulls[Anum_pg_trigger_tgqual - 1] = true;
971
972 if (oldtablename)
973 values[Anum_pg_trigger_tgoldtable - 1] = DirectFunctionCall1(namein,
974 CStringGetDatum(oldtablename));
975 else
976 nulls[Anum_pg_trigger_tgoldtable - 1] = true;
977 if (newtablename)
978 values[Anum_pg_trigger_tgnewtable - 1] = DirectFunctionCall1(namein,
979 CStringGetDatum(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);
989 CatalogTupleInsert(tgrel, tuple);
990 }
991 else
992 {
993 HeapTuple newtup;
994
995 newtup = heap_form_tuple(tgrel->rd_att, values, nulls);
996 CatalogTupleUpdate(tgrel, &tuple->t_self, newtup);
997 heap_freetuple(newtup);
998 }
999
1000 heap_freetuple(tuple); /* free either original or new tuple */
1002
1003 pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
1004 pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
1005 pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1]));
1006 if (oldtablename)
1007 pfree(DatumGetPointer(values[Anum_pg_trigger_tgoldtable - 1]));
1008 if (newtablename)
1009 pfree(DatumGetPointer(values[Anum_pg_trigger_tgnewtable - 1]));
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 */
1015 pgrel = table_open(RelationRelationId, RowExclusiveLock);
1016 tuple = SearchSysCacheCopy1(RELOID,
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)
1040 deleteDependencyRecordsFor(TriggerRelationId, trigoid, true);
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
1050 referenced.classId = ProcedureRelationId;
1051 referenced.objectId = funcoid;
1052 referenced.objectSubId = 0;
1053 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1054
1055 if (isInternal && OidIsValid(constraintOid))
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 */
1063 referenced.classId = ConstraintRelationId;
1064 referenced.objectId = constraintOid;
1065 referenced.objectSubId = 0;
1066 recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
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 */
1075 referenced.classId = RelationRelationId;
1076 referenced.objectId = RelationGetRelid(rel);
1077 referenced.objectSubId = 0;
1078 recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1079
1080 if (OidIsValid(constrrelid))
1081 {
1082 referenced.classId = RelationRelationId;
1083 referenced.objectId = constrrelid;
1084 referenced.objectSubId = 0;
1085 recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
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 */
1094 if (OidIsValid(constraintOid))
1095 {
1096 referenced.classId = ConstraintRelationId;
1097 referenced.objectId = constraintOid;
1098 referenced.objectSubId = 0;
1099 recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
1100 }
1101
1102 /*
1103 * If it's a partition trigger, create the partition dependencies.
1104 */
1105 if (OidIsValid(parentTriggerOid))
1106 {
1107 ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
1108 recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
1109 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
1110 recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
1111 }
1112 }
1113
1114 /* If column-specific trigger, add normal dependencies on columns */
1115 if (columns != NULL)
1116 {
1117 int i;
1118
1119 referenced.classId = RelationRelationId;
1120 referenced.objectId = RelationGetRelid(rel);
1121 for (i = 0; i < ncolumns; i++)
1122 {
1123 referenced.objectSubId = columns[i];
1124 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
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)
1133 recordDependencyOnExpr(&myself, whenClause, whenRtable,
1135
1136 /* Post creation hook for new trigger */
1137 InvokeObjectPostCreateHookArg(TriggerRelationId, trigoid, 0,
1138 isInternal);
1139
1140 /*
1141 * Lastly, create the trigger on child relations, if needed.
1142 */
1143 if (partition_recurse)
1144 {
1145 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1146 int i;
1147 MemoryContext oldcxt,
1148 perChildCxt;
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
1161 oldcxt = MemoryContextSwitchTo(perChildCxt);
1162
1163 /* Iterate to create the trigger on each existing partition */
1164 for (i = 0; i < partdesc->nparts; i++)
1165 {
1166 CreateTrigStmt *childStmt;
1167 Relation childTbl;
1168 Node *qual;
1169
1170 childTbl = table_open(partdesc->oids[i], ShareRowExclusiveLock);
1171
1172 /*
1173 * Initialize our fabricated parse node by copying the original
1174 * one, then resetting fields that we pass separately.
1175 */
1176 childStmt = copyObject(stmt);
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,
1193 isInternal, true, trigger_fires_when);
1194
1195 table_close(childTbl, NoLock);
1196
1197 MemoryContextReset(perChildCxt);
1198 }
1199
1200 MemoryContextSwitchTo(oldcxt);
1201 MemoryContextDelete(perChildCxt);
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:2652
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3834
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4037
#define InvalidAttrNumber
Definition: attnum.h:23
static Datum values[MAXATTR]
Definition: bootstrap.c:153
#define CStringGetTextDatum(s)
Definition: builtins.h:97
Datum byteain(PG_FUNCTION_ARGS)
Definition: bytea.c:187
#define NameStr(name)
Definition: c.h:752
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)
Definition: dependency.c:1553
@ 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:1207
int errhint(const char *fmt,...)
Definition: elog.c:1321
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:682
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, ItemPointer 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:1665
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:2095
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2170
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1822
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)
Definition: objectaccess.h:175
ObjectType get_relkind_objtype(char relkind)
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
char * nodeToString(const void *obj)
Definition: outfuncs.c:805
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)
Definition: parse_func.c:2260
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
Definition: parsenodes.h:2343
#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
Definition: pg_attribute.h:74
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)
Definition: pg_constraint.c:51
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)
Definition: pg_inherits.c:255
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:377
#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:239
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
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:322
static Datum CharGetDatum(char X)
Definition: postgres.h:132
#define InvalidOid
Definition: postgres_ext.h:37
#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
Node * whenClause
Definition: parsenodes.h:3120
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
Definition: primnodes.h:262
ParseLoc location
Definition: primnodes.h:310
AttrNumber varattno
Definition: primnodes.h:274
int varno
Definition: primnodes.h:269
Definition: c.h:721
#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:1100

References AccessShareLock, ACL_EXECUTE, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addNSItemToQuery(), addRangeTableEntryForRelation(), ALLOCSET_SMALL_SIZES, AllocSetContextCreate, allowSystemTableMods, generate_unaccent_rules::args, Assert(), assign_expr_collations(), attnameAttNum(), attnum, BoolGetDatum(), BTEqualStrategyNumber, buildint2vector(), byteain(), CacheInvalidateRelcacheByTuple(), CatalogTupleInsert(), CatalogTupleUpdate(), CharGetDatum(), ObjectAddress::classId, 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, find_all_inheritors(), free_parsestate(), CreateTrigStmt::funcname, 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, TriggerTransition::isNew, IsSystemRelation(), TriggerTransition::isTable, j, len, lfirst, lfirst_node, list_free(), list_length(), Var::location, LockRelationOid(), LookupFuncName(), make_parsestate(), makeAlias(), map_partition_varattnos(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), name, TriggerTransition::name, NAMEDATALEN, namein(), NameListToString(), NameStr, NIL, nodeToString(), NoLock, PartitionDescData::nparts, object_aclcheck(), OBJECT_FUNCTION, ObjectAddressSet, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, 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_att, 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, Var::varno, and CreateTrigStmt::whenClause.

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{
1730 Relation tgrel;
1731 int nkeys;
1732 ScanKeyData keys[2];
1733 SysScanDesc tgscan;
1734 HeapTuple tuple;
1735 bool found;
1736 bool changed;
1737
1738 /* Scan the relevant entries in pg_triggers */
1739 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1740
1741 ScanKeyInit(&keys[0],
1742 Anum_pg_trigger_tgrelid,
1743 BTEqualStrategyNumber, F_OIDEQ,
1745 if (tgname)
1746 {
1747 ScanKeyInit(&keys[1],
1748 Anum_pg_trigger_tgname,
1749 BTEqualStrategyNumber, F_NAMEEQ,
1750 CStringGetDatum(tgname));
1751 nkeys = 2;
1752 }
1753 else
1754 nkeys = 1;
1755
1756 tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1757 NULL, nkeys, keys);
1758
1759 found = changed = false;
1760
1761 while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1762 {
1763 Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
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,
1775 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
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 */
1785 HeapTuple newtup = heap_copytuple(tuple);
1786 Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
1787
1788 newtrig->tgenabled = fires_when;
1789
1790 CatalogTupleUpdate(tgrel, &newtup->t_self, newtup);
1791
1792 heap_freetuple(newtup);
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 */
1819 EnableDisableTrigger(part, NULL, oldtrig->oid,
1820 fires_when, skip_system, recurse,
1821 lockmode);
1822 table_close(part, NoLock); /* keep lock till commit */
1823 }
1824 }
1825
1826 InvokeObjectPostAlterHook(TriggerRelationId,
1827 oldtrig->oid, 0);
1828 }
1829
1830 systable_endscan(tgscan);
1831
1833
1834 if (tgname && !found)
1835 ereport(ERROR,
1836 (errcode(ERRCODE_UNDEFINED_OBJECT),
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:1631
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
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, 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(), HeapTupleData::t_self, 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,
2815 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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 {
2822 TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2823
2824 Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
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
2837 ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2838
2839 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2841 true, slot, NULL, NIL, NULL,
2842 transition_capture,
2843 is_crosspart_update);
2844 }
2845}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1658
@ LockTupleExclusive
Definition: lockoptions.h:58
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:618
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:533
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
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:6147

References AfterTriggerSaveEvent(), Assert(), ereport, errcode(), errmsg(), ERROR, ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), GetTupleForTrigger(), HeapTupleIsValid, ItemPointerIsValid(), LockTupleExclusive, NIL, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, 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,
2554 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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))
2560 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2562 true, NULL, slot,
2563 recheckIndexes, NULL,
2564 transition_capture,
2565 false);
2566}

References AfterTriggerSaveEvent(), Assert(), ereport, errcode(), errmsg(), ERROR, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, 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,
3162 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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 */
3177 TupleTableSlot *oldslot;
3178 ResultRelInfo *tupsrc;
3179
3180 Assert((src_partinfo != NULL && dst_partinfo != NULL) ||
3181 !is_crosspart_update);
3182
3183 tupsrc = src_partinfo ? src_partinfo : relinfo;
3184 oldslot = ExecGetTriggerOldSlot(estate, tupsrc);
3185
3186 if (fdw_trigtuple == NULL && ItemPointerIsValid(tupleid))
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)
3198 ExecForceStoreHeapTuple(fdw_trigtuple, oldslot, false);
3199 else
3200 ExecClearTuple(oldslot);
3201
3202 AfterTriggerSaveEvent(estate, relinfo,
3203 src_partinfo, dst_partinfo,
3205 true,
3206 oldslot, newslot, recheckIndexes,
3207 ExecGetAllUpdatedCols(relinfo, estate),
3208 transition_capture,
3209 is_crosspart_update);
3210 }
3211}
Bitmapset * ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition: execUtils.c:1418

References AfterTriggerSaveEvent(), Assert(), ereport, errcode(), errmsg(), ERROR, ExecClearTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), GetTupleForTrigger(), ItemPointerIsValid(), LockTupleExclusive, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, 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)
2687 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2689 false, NULL, NULL, NIL, NULL, transition_capture,
2690 false);
2691}
bool trig_delete_after_statement
Definition: reltrigger.h:70

References AfterTriggerSaveEvent(), NIL, ResultRelInfo::ri_TrigDesc, 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)
2458 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2460 false, NULL, NULL, NIL, NULL, transition_capture,
2461 false);
2462}
bool trig_insert_after_statement
Definition: reltrigger.h:60

References AfterTriggerSaveEvent(), NIL, ResultRelInfo::ri_TrigDesc, 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)
3332 AfterTriggerSaveEvent(estate, relinfo,
3333 NULL, NULL,
3335 false, NULL, NULL, NIL, NULL, NULL,
3336 false);
3337}
bool trig_truncate_after_statement
Definition: reltrigger.h:73

References AfterTriggerSaveEvent(), NIL, ResultRelInfo::ri_TrigDesc, 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)
2962 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2964 false, NULL, NULL, NIL,
2965 ExecGetAllUpdatedCols(relinfo, estate),
2966 transition_capture,
2967 false);
2968}
bool trig_update_after_statement
Definition: reltrigger.h:65

References AfterTriggerSaveEvent(), Assert(), ExecGetAllUpdatedCols(), NIL, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, 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{
2710 TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2711 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2712 bool result = true;
2713 TriggerData LocTriggerData = {0};
2714 HeapTuple trigtuple;
2715 bool should_free = false;
2716 int i;
2717
2718 Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2719 if (fdw_trigtuple == NULL)
2720 {
2721 TupleTableSlot *epqslot_candidate = NULL;
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,
2732 LockTupleExclusive, slot, !is_merge_delete,
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 {
2743 *epqslot = epqslot_candidate;
2744 return false;
2745 }
2746
2747 trigtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2748 }
2749 else
2750 {
2751 trigtuple = fdw_trigtuple;
2752 ExecForceStoreHeapTuple(trigtuple, slot, false);
2753 }
2754
2755 LocTriggerData.type = T_TriggerData;
2756 LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
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,
2766 TRIGGER_TYPE_ROW,
2767 TRIGGER_TYPE_BEFORE,
2768 TRIGGER_TYPE_DELETE))
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;
2777 newtuple = ExecCallTriggerFunc(&LocTriggerData,
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)
2791 heap_freetuple(trigtuple);
2792
2793 return result;
2794}
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:658
#define TRIGGER_EVENT_BEFORE
Definition: trigger.h:100

References Assert(), ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), HeapTupleIsValid, i, ItemPointerIsValid(), LockTupleExclusive, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgtype, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

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;
2471 TriggerData LocTriggerData = {0};
2472 int i;
2473
2474 LocTriggerData.type = T_TriggerData;
2475 LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
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,
2485 TRIGGER_TYPE_ROW,
2486 TRIGGER_TYPE_BEFORE,
2487 TRIGGER_TYPE_INSERT))
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;
2499 newtuple = ExecCallTriggerFunc(&LocTriggerData,
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 {
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,
2524 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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,
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:1846
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3533
static HeapTuple check_modified_virtual_generated(TupleDesc tupdesc, HeapTuple tuple)
Definition: trigger.c:6696

References check_modified_virtual_generated(), ereport, errcode(), errdetail(), errmsg(), ERROR, ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecPartitionCheck(), get_namespace_name(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, RelationGetDescr, RelationGetNamespace, RelationGetRelationName, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgisclone, Trigger::tgname, Trigger::tgtype, 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;
2981 TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
2982 HeapTuple newtuple = NULL;
2983 HeapTuple trigtuple;
2984 bool should_free_trig = false;
2985 bool should_free_new = false;
2986 TriggerData LocTriggerData = {0};
2987 int i;
2988 Bitmapset *updatedCols;
2989 LockTupleMode lockmode;
2990
2991 /* Determine lock mode to use */
2992 lockmode = ExecUpdateLockMode(estate, relinfo);
2993
2994 Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2995 if (fdw_trigtuple == NULL)
2996 {
2997 TupleTableSlot *epqslot_candidate = NULL;
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 {
3023 TupleTableSlot *epqslot_clean;
3024
3025 epqslot_clean = ExecGetUpdateNewTuple(relinfo, epqslot_candidate,
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 */
3034 if (unlikely(newslot != epqslot_clean))
3035 ExecCopySlot(newslot, epqslot_clean);
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 */
3046 ExecMaterializeSlot(newslot);
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 */
3053 trigtuple = ExecFetchSlotHeapTuple(oldslot, true, &should_free_trig);
3054 }
3055 else
3056 {
3057 /* Put the FDW-supplied tuple into oldslot to unify the cases */
3058 ExecForceStoreHeapTuple(fdw_trigtuple, oldslot, false);
3059 trigtuple = fdw_trigtuple;
3060 }
3061
3062 LocTriggerData.type = T_TriggerData;
3063 LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
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,
3075 TRIGGER_TYPE_ROW,
3076 TRIGGER_TYPE_BEFORE,
3077 TRIGGER_TYPE_UPDATE))
3078 continue;
3079 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3080 updatedCols, oldslot, newslot))
3081 continue;
3082
3083 if (!newtuple)
3084 newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free_new);
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;
3091 newtuple = ExecCallTriggerFunc(&LocTriggerData,
3092 i,
3093 relinfo->ri_TrigFunctions,
3094 relinfo->ri_TrigInstrument,
3095 GetPerTupleMemoryContext(estate));
3096
3097 if (newtuple == NULL)
3098 {
3099 if (should_free_trig)
3100 heap_freetuple(trigtuple);
3101 if (should_free_new)
3102 heap_freetuple(oldtuple);
3103 return false; /* "do nothing" */
3104 }
3105 else if (newtuple != oldtuple)
3106 {
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)
3118 ExecMaterializeSlot(newslot);
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)
3128 heap_freetuple(trigtuple);
3129
3130 return true;
3131}
#define unlikely(x)
Definition: c.h:403
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2520
LockTupleMode
Definition: lockoptions.h:50
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:476

References Assert(), check_modified_virtual_generated(), ExecCallTriggerFunc(), ExecCopySlot(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), ExecGetUpdateNewTuple(), ExecMaterializeSlot(), ExecUpdateLockMode(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), HeapTupleIsValid, i, ItemPointerIsValid(), TriggerDesc::numtriggers, RelationGetDescr, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newslot, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, TriggerData::tg_updatedcols, Trigger::tgtype, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TriggerData::type, 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;
2634 TriggerData LocTriggerData = {0};
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
2648 LocTriggerData.type = T_TriggerData;
2649 LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
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,
2658 TRIGGER_TYPE_STATEMENT,
2659 TRIGGER_TYPE_BEFORE,
2660 TRIGGER_TYPE_DELETE))
2661 continue;
2662 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2663 NULL, NULL, NULL))
2664 continue;
2665
2666 LocTriggerData.tg_trigger = trigger;
2667 newtuple = ExecCallTriggerFunc(&LocTriggerData,
2668 i,
2669 relinfo->ri_TrigFunctions,
2670 relinfo->ri_TrigInstrument,
2671 GetPerTupleMemoryContext(estate));
2672
2673 if (newtuple)
2674 ereport(ERROR,
2675 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
2676 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2677 }
2678}
bool trig_delete_before_statement
Definition: reltrigger.h:69
static bool before_stmt_triggers_fired(Oid relid, CmdType cmdType)
Definition: trigger.c:6545

References before_stmt_triggers_fired(), CMD_DELETE, ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, Trigger::tgtype, TriggerDesc::trig_delete_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_DELETE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

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;
2405 TriggerData LocTriggerData = {0};
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
2419 LocTriggerData.type = T_TriggerData;
2420 LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
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,
2429 TRIGGER_TYPE_STATEMENT,
2430 TRIGGER_TYPE_BEFORE,
2431 TRIGGER_TYPE_INSERT))
2432 continue;
2433 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2434 NULL, NULL, NULL))
2435 continue;
2436
2437 LocTriggerData.tg_trigger = trigger;
2438 newtuple = ExecCallTriggerFunc(&LocTriggerData,
2439 i,
2440 relinfo->ri_TrigFunctions,
2441 relinfo->ri_TrigInstrument,
2442 GetPerTupleMemoryContext(estate));
2443
2444 if (newtuple)
2445 ereport(ERROR,
2446 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
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(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, Trigger::tgtype, TriggerDesc::trig_insert_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_INSERT, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

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;
3284 TriggerData LocTriggerData = {0};
3285
3286 trigdesc = relinfo->ri_TrigDesc;
3287
3288 if (trigdesc == NULL)
3289 return;
3290 if (!trigdesc->trig_truncate_before_statement)
3291 return;
3292
3293 LocTriggerData.type = T_TriggerData;
3294 LocTriggerData.tg_event = TRIGGER_EVENT_TRUNCATE |
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,
3304 TRIGGER_TYPE_STATEMENT,
3305 TRIGGER_TYPE_BEFORE,
3306 TRIGGER_TYPE_TRUNCATE))
3307 continue;
3308 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3309 NULL, NULL, NULL))
3310 continue;
3311
3312 LocTriggerData.tg_trigger = trigger;
3313 newtuple = ExecCallTriggerFunc(&LocTriggerData,
3314 i,
3315 relinfo->ri_TrigFunctions,
3316 relinfo->ri_TrigInstrument,
3317 GetPerTupleMemoryContext(estate));
3318
3319 if (newtuple)
3320 ereport(ERROR,
3321 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
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(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, Trigger::tgtype, TriggerDesc::trig_truncate_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_TRUNCATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

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;
2899 TriggerData LocTriggerData = {0};
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
2919 LocTriggerData.type = T_TriggerData;
2920 LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
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,
2930 TRIGGER_TYPE_STATEMENT,
2931 TRIGGER_TYPE_BEFORE,
2932 TRIGGER_TYPE_UPDATE))
2933 continue;
2934 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2935 updatedCols, NULL, NULL))
2936 continue;
2937
2938 LocTriggerData.tg_trigger = trigger;
2939 newtuple = ExecCallTriggerFunc(&LocTriggerData,
2940 i,
2941 relinfo->ri_TrigFunctions,
2942 relinfo->ri_TrigInstrument,
2943 GetPerTupleMemoryContext(estate));
2944
2945 if (newtuple)
2946 ereport(ERROR,
2947 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
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(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_updatedcols, Trigger::tgtype, TriggerDesc::trig_update_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

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;
2318 MemoryContext oldContext;
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 */
2355 oldContext = MemoryContextSwitchTo(per_tuple_context);
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
2363 pgstat_init_function_usage(fcinfo, &fcusage);
2364
2366 PG_TRY();
2367 {
2368 result = FunctionCallInvoke(fcinfo);
2369 }
2370 PG_FINALLY();
2371 {
2373 }
2374 PG_END_TRY();
2375
2376 pgstat_end_function_usage(&fcusage, true);
2377
2378 MemoryContextSwitchTo(oldContext);
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,
2386 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
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:127
#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
static int MyTriggerDepth
Definition: trigger.c:66

References AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_INITDEFERRED, Assert(), DatumGetPointer(), ereport, errcode(), errmsg(), ERROR, 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;
2852 TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2853 TriggerData LocTriggerData = {0};
2854 int i;
2855
2856 LocTriggerData.type = T_TriggerData;
2857 LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
2860 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2861
2862 ExecForceStoreHeapTuple(trigtuple, slot, false);
2863
2864 for (i = 0; i < trigdesc->numtriggers; i++)
2865 {
2866 HeapTuple rettuple;
2867 Trigger *trigger = &trigdesc->triggers[i];
2868
2869 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2870 TRIGGER_TYPE_ROW,
2871 TRIGGER_TYPE_INSTEAD,
2872 TRIGGER_TYPE_DELETE))
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;
2881 rettuple = ExecCallTriggerFunc(&LocTriggerData,
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)
2889 heap_freetuple(rettuple);
2890 }
2891 return true;
2892}
#define TRIGGER_EVENT_INSTEAD
Definition: trigger.h:102

References ExecCallTriggerFunc(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgtype, 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;
2575 TriggerData LocTriggerData = {0};
2576 int i;
2577
2578 LocTriggerData.type = T_TriggerData;
2579 LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
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,
2589 TRIGGER_TYPE_ROW,
2590 TRIGGER_TYPE_INSTEAD,
2591 TRIGGER_TYPE_INSERT))
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;
2603 newtuple = ExecCallTriggerFunc(&LocTriggerData,
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(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgtype, 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;
3218 TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
3219 HeapTuple newtuple = NULL;
3220 bool should_free;
3221 TriggerData LocTriggerData = {0};
3222 int i;
3223
3224 LocTriggerData.type = T_TriggerData;
3225 LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
3228 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3229
3230 ExecForceStoreHeapTuple(trigtuple, oldslot, false);
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,
3238 TRIGGER_TYPE_ROW,
3239 TRIGGER_TYPE_INSTEAD,
3240 TRIGGER_TYPE_UPDATE))
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;
3255 newtuple = ExecCallTriggerFunc(&LocTriggerData,
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(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newslot, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgtype, 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}

References i, TriggerDesc::numtriggers, Trigger::tgname, Trigger::tgnewtable, Trigger::tgoldtable, Trigger::tgtype, and TriggerDesc::triggers.

Referenced by ATExecAddInherit(), and ATExecAttachPartition().

◆ FreeTriggerDesc()

void FreeTriggerDesc ( TriggerDesc trigdesc)

Definition at line 2145 of file trigger.c.

2146{
2147 Trigger *trigger;
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 i, TriggerDesc::numtriggers, pfree(), Trigger::tgargs, Trigger::tgattr, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgnewtable, Trigger::tgoldtable, Trigger::tgqual, 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{
1372 Relation tgrel;
1373 ScanKeyData skey[2];
1374 SysScanDesc tgscan;
1375 HeapTuple tup;
1376 Oid oid;
1377
1378 /*
1379 * Find the trigger, verify permissions, set up object address
1380 */
1381 tgrel = table_open(TriggerRelationId, AccessShareLock);
1382
1383 ScanKeyInit(&skey[0],
1384 Anum_pg_trigger_tgrelid,
1385 BTEqualStrategyNumber, F_OIDEQ,
1386 ObjectIdGetDatum(relid));
1387 ScanKeyInit(&skey[1],
1388 Anum_pg_trigger_tgname,
1389 BTEqualStrategyNumber, F_NAMEEQ,
1390 CStringGetDatum(trigname));
1391
1392 tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1393 NULL, 2, skey);
1394
1395 tup = systable_getnext(tgscan);
1396
1397 if (!HeapTupleIsValid(tup))
1398 {
1399 if (!missing_ok)
1400 ereport(ERROR,
1401 (errcode(ERRCODE_UNDEFINED_OBJECT),
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
1411 systable_endscan(tgscan);
1413 return oid;
1414}

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

4921{
4922 /* Create it if not already done. */
4923 if (!table->storeslot)
4924 {
4925 MemoryContext oldcxt;
4926
4927 /*
4928 * We need this slot only until AfterTriggerEndQuery, but making it
4929 * last till end-of-subxact is good enough. It'll be freed by
4930 * AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4931 * a different lifespan, so we'd better make a copy of that.
4932 */
4934 tupdesc = CreateTupleDescCopy(tupdesc);
4935 table->storeslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
4936 MemoryContextSwitchTo(oldcxt);
4937 }
4938
4939 return table->storeslot;
4940}
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
MemoryContext CurTransactionContext
Definition: mcxt.c:172
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:252

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

Referenced by TransitionTableAddTuple().

◆ GetAfterTriggersTableData()

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

Definition at line 4882 of file trigger.c.

4883{
4886 MemoryContext oldcxt;
4887 ListCell *lc;
4888
4889 /* Caller should have ensured query_depth is OK. */
4893
4894 foreach(lc, qs->tables)
4895 {
4897 if (table->relid == relid && table->cmdType == cmdType &&
4898 !table->closed)
4899 return table;
4900 }
4901
4903
4905 table->relid = relid;
4906 table->cmdType = cmdType;
4907 qs->tables = lappend(qs->tables, table);
4908
4909 MemoryContextSwitchTo(oldcxt);
4910
4911 return table;
4912}
List * lappend(List *list, void *datum)
Definition: list.c:339
void * palloc0(Size size)
Definition: mcxt.c:1395

References afterTriggers, Assert(), CurTransactionContext, lappend(), lfirst, AfterTriggersData::maxquerydepth, MemoryContextSwitchTo(), palloc0(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, table, and AfterTriggersQueryData::tables.

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

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

References Assert(), AfterTriggersTableData::new_ins_tuplestore, AfterTriggersTableData::new_upd_tuplestore, AfterTriggersTableData::old_del_tuplestore, AfterTriggersTableData::old_upd_tuplestore, TransitionCaptureState::tcs_delete_old_table, TransitionCaptureState::tcs_insert_new_table, TransitionCaptureState::tcs_private, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_UPDATE, and TupIsNull.

Referenced by AfterTriggerSaveEvent().

◆ GetCurrentFDWTuplestore()

static Tuplestorestate * GetCurrentFDWTuplestore ( void  )
static

Definition at line 3981 of file trigger.c.

3982{
3983 Tuplestorestate *ret;
3984
3986 if (ret == NULL)
3987 {
3988 MemoryContext oldcxt;
3989 ResourceOwner saveResourceOwner;
3990
3991 /*
3992 * Make the tuplestore valid until end of subtransaction. We really
3993 * only need it until AfterTriggerEndQuery().
3994 */
3996 saveResourceOwner = CurrentResourceOwner;
3998
3999 ret = tuplestore_begin_heap(false, false, work_mem);
4000
4001 CurrentResourceOwner = saveResourceOwner;
4002 MemoryContextSwitchTo(oldcxt);
4003
4005 }
4006
4007 return ret;
4008}
int work_mem
Definition: globals.c:131
ResourceOwner CurrentResourceOwner
Definition: resowner.c:173
ResourceOwner CurTransactionResourceOwner
Definition: resowner.c:174
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:330

References afterTriggers, CurrentResourceOwner, CurTransactionContext, CurTransactionResourceOwner, AfterTriggersQueryData::fdw_tuplestore, MemoryContextSwitchTo(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, tuplestore_begin_heap(), and work_mem.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ 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,
3399 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
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)
3433 *tmresultp = TM_Updated;
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:2639
@ 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:682
Snapshot es_snapshot
Definition: execnodes.h:660
Index ri_RangeTableIndex
Definition: execnodes.h:477
bool traversed
Definition: tableam.h:152
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:1549
#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(), IsolationUsesXactSnapshot, LockWaitBlock, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, 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 4968 of file trigger.c.

4969{
4971 bool need_old_upd,
4972 need_new_upd,
4973 need_old_del,
4974 need_new_ins;
4976 MemoryContext oldcxt;
4977 ResourceOwner saveResourceOwner;
4978
4979 if (trigdesc == NULL)
4980 return NULL;
4981
4982 /* Detect which table(s) we need. */
4983 switch (cmdType)
4984 {
4985 case CMD_INSERT:
4986 need_old_upd = need_old_del = need_new_upd = false;
4987 need_new_ins = trigdesc->trig_insert_new_table;
4988 break;
4989 case CMD_UPDATE:
4990 need_old_upd = trigdesc->trig_update_old_table;
4991 need_new_upd = trigdesc->trig_update_new_table;
4992 need_old_del = need_new_ins = false;
4993 break;
4994 case CMD_DELETE:
4995 need_old_del = trigdesc->trig_delete_old_table;
4996 need_old_upd = need_new_upd = need_new_ins = false;
4997 break;
4998 case CMD_MERGE:
4999 need_old_upd = trigdesc->trig_update_old_table;
5000 need_new_upd = trigdesc->trig_update_new_table;
5001 need_old_del = trigdesc->trig_delete_old_table;
5002 need_new_ins = trigdesc->trig_insert_new_table;
5003 break;
5004 default:
5005 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
5006 /* keep compiler quiet */
5007 need_old_upd = need_new_upd = need_old_del = need_new_ins = false;
5008 break;
5009 }
5010 if (!need_old_upd && !need_new_upd && !need_new_ins && !need_old_del)
5011 return NULL;
5012
5013 /* Check state, like AfterTriggerSaveEvent. */
5014 if (afterTriggers.query_depth < 0)
5015 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
5016
5017 /* Be sure we have enough space to record events at this query depth. */
5020
5021 /*
5022 * Find or create an AfterTriggersTableData struct to hold the
5023 * tuplestore(s). If there's a matching struct but it's marked closed,
5024 * ignore it; we need a newer one.
5025 *
5026 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
5027 * allocated in the current (sub)transaction's CurTransactionContext, and
5028 * the tuplestores are managed by the (sub)transaction's resource owner.
5029 * This is sufficient lifespan because we do not allow triggers using
5030 * transition tables to be deferrable; they will be fired during
5031 * AfterTriggerEndQuery, after which it's okay to delete the data.
5032 */
5033 table = GetAfterTriggersTableData(relid, cmdType);
5034
5035 /* Now create required tuplestore(s), if we don't have them already. */
5037 saveResourceOwner = CurrentResourceOwner;
5039
5040 if (need_old_upd && table->old_upd_tuplestore == NULL)
5041 table->old_upd_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5042 if (need_new_upd && table->new_upd_tuplestore == NULL)
5043 table->new_upd_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5044 if (need_old_del && table->old_del_tuplestore == NULL)
5045 table->old_del_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5046 if (need_new_ins && table->new_ins_tuplestore == NULL)
5047 table->new_ins_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5048
5049 CurrentResourceOwner = saveResourceOwner;
5050 MemoryContextSwitchTo(oldcxt);
5051
5052 /* Now build the TransitionCaptureState struct, in caller's context */
5054 state->tcs_delete_old_table = need_old_del;
5055 state->tcs_update_old_table = need_old_upd;
5056 state->tcs_update_new_table = need_new_upd;
5057 state->tcs_insert_new_table = need_new_ins;
5058 state->tcs_private = table;
5059
5060 return state;
5061}
@ CMD_MERGE
Definition: nodes.h:279
bool trig_update_new_table
Definition: reltrigger.h:77
bool trig_insert_new_table
Definition: reltrigger.h:75
bool trig_delete_old_table
Definition: reltrigger.h:78
bool trig_update_old_table
Definition: reltrigger.h:76

References AfterTriggerEnlargeQueryState(), afterTriggers, CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, CurrentResourceOwner, CurTransactionContext, CurTransactionResourceOwner, elog, ERROR, GetAfterTriggersTableData(), AfterTriggersData::maxquerydepth, MemoryContextSwitchTo(), palloc0(), AfterTriggersData::query_depth, table, 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 6680 of file trigger.c.

6681{
6683}
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354

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;
1424 Form_pg_class form;
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,
1436 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1437 errmsg("relation \"%s\" cannot have triggers",
1438 rv->relname),
1439 errdetail_relkind_not_supported(form->relkind)));
1440
1441 /* you must own the table to rename one of its triggers */
1442 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
1444 if (!allowSystemTableMods && IsSystemClass(relid, form))
1445 ereport(ERROR,
1446 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
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:4088
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, 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;
1867 Relation tgrel;
1868 ScanKeyData skey;
1869 SysScanDesc tgscan;
1870 HeapTuple htup;
1871 MemoryContext oldContext;
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 */
1888 ScanKeyInit(&skey,
1889 Anum_pg_trigger_tgrelid,
1890 BTEqualStrategyNumber, F_OIDEQ,
1892
1893 tgrel = table_open(TriggerRelationId, AccessShareLock);
1894 tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1895 NULL, 1, &skey);
1896
1897 while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
1898 {
1899 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
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
1941 Anum_pg_trigger_tgargs,
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
1957 datum = fastgetattr(htup, Anum_pg_trigger_tgoldtable,
1958 tgrel->rd_att, &isnull);
1959 if (!isnull)
1960 build->tgoldtable =
1962 else
1963 build->tgoldtable = NULL;
1964
1965 datum = fastgetattr(htup, Anum_pg_trigger_tgnewtable,
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
1983 systable_endscan(tgscan);
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 = (TriggerDesc *) palloc0(sizeof(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);
2003 MemoryContextSwitchTo(oldContext);
2004
2005 /* Release working memory */
2006 FreeTriggerDesc(trigdesc);
2007}
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:861
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:345
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:383
TriggerDesc * trigdesc
Definition: rel.h:117
char tgenabled
Definition: reltrigger.h:30
Oid tgconstraint
Definition: reltrigger.h:35
Oid tgconstrrelid
Definition: reltrigger.h:33
bool tgisinternal
Definition: reltrigger.h:31
Definition: c.h:693
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(), FreeTriggerDesc(), GETSTRUCT(), HeapTupleIsValid, i, MemoryContextSwitchTo(), NameGetDatum(), nameout(), TriggerDesc::numtriggers, ObjectIdGetDatum(), OidIsValid, palloc(), palloc0(), pfree(), pstrdup(), RelationData::rd_att, RelationGetRelationName, RelationGetRelid, repalloc(), ScanKeyInit(), SetTriggerFlags(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TextDatumGetCString, Trigger::tgargs, Trigger::tgattr, Trigger::tgconstraint, Trigger::tgconstrindid, Trigger::tgconstrrelid, Trigger::tgdeferrable, Trigger::tgenabled, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgisclone, Trigger::tgisinternal, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgqual, Trigger::tgtype, 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{
1293 Relation tgrel;
1294 SysScanDesc tgscan;
1295 ScanKeyData skey[1];
1296 HeapTuple tup;
1297 Oid relid;
1298 Relation rel;
1299
1300 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1301
1302 /*
1303 * Find the trigger to delete.
1304 */
1305 ScanKeyInit(&skey[0],
1306 Anum_pg_trigger_oid,
1307 BTEqualStrategyNumber, F_OIDEQ,
1308 ObjectIdGetDatum(trigOid));
1309
1310 tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
1311 NULL, 1, skey);
1312
1313 tup = systable_getnext(tgscan);
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,
1329 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1330 errmsg("relation \"%s\" cannot have triggers",
1332 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
1333
1335 ereport(ERROR,
1336 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
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
1345 systable_endscan(tgscan);
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, ItemPointer 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, GETSTRUCT(), HeapTupleIsValid, IsSystemRelation(), NoLock, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelationName, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, 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;
1470 Relation targetrel;
1471 Relation tgrel;
1472 HeapTuple tuple;
1473 SysScanDesc tgscan;
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)
1495 (void) find_all_inheritors(relid, AccessExclusiveLock, NULL);
1496
1497 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1498
1499 /*
1500 * Search for the trigger to modify.
1501 */
1502 ScanKeyInit(&key[0],
1503 Anum_pg_trigger_tgrelid,
1504 BTEqualStrategyNumber, F_OIDEQ,
1505 ObjectIdGetDatum(relid));
1506 ScanKeyInit(&key[1],
1507 Anum_pg_trigger_tgname,
1508 BTEqualStrategyNumber, F_NAMEEQ,
1509 PointerGetDatum(stmt->subname));
1510 tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1511 NULL, 2, key);
1512 if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1513 {
1514 Form_pg_trigger trigform;
1515
1516 trigform = (Form_pg_trigger) GETSTRUCT(tuple);
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,
1527 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1528 errmsg("cannot rename trigger \"%s\" on table \"%s\"",
1529 stmt->subname, RelationGetRelationName(targetrel)),
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 {
1541 PartitionDesc partdesc = RelationGetPartitionDesc(targetrel, true);
1542
1543 for (int i = 0; i < partdesc->nparts; i++)
1544 {
1545 Oid partitionId = partdesc->oids[i];
1546
1547 renametrig_partition(tgrel, partitionId, trigform->oid,
1548 stmt->newname, stmt->subname);
1549 }
1550 }
1551 }
1552 else
1553 {
1554 ereport(ERROR,
1555 (errcode(ERRCODE_UNDEFINED_OBJECT),
1556 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1557 stmt->subname, RelationGetRelationName(targetrel))));
1558 }
1559
1560 ObjectAddressSet(address, TriggerRelationId, tgoid);
1561
1562 systable_endscan(tgscan);
1563
1565
1566 /*
1567 * Close rel, but keep exclusive lock!
1568 */
1569 relation_close(targetrel, NoLock);
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, find_all_inheritors(), get_partition_parent(), get_rel_name(), GETSTRUCT(), HeapTupleIsValid, i, sort-test::key, NoLock, PartitionDescData::nparts, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, PointerGetDatum(), RangeVarCallbackForRenameTrigger(), RangeVarGetRelidExtended(), RelationData::rd_rel, 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;
1586 Form_pg_trigger tgform;
1587 ScanKeyData key[2];
1588 SysScanDesc tgscan;
1589
1590 /* If the trigger already has the new name, nothing to do. */
1591 tgform = (Form_pg_trigger) GETSTRUCT(trigtup);
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],
1601 Anum_pg_trigger_tgrelid,
1602 BTEqualStrategyNumber, F_OIDEQ,
1604 ScanKeyInit(&key[1],
1605 Anum_pg_trigger_tgname,
1606 BTEqualStrategyNumber, F_NAMEEQ,
1607 PointerGetDatum(newname));
1608 tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1609 NULL, 2, key);
1610 if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1611 ereport(ERROR,
1613 errmsg("trigger \"%s\" for relation \"%s\" already exists",
1614 newname, RelationGetRelationName(targetrel))));
1615 systable_endscan(tgscan);
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),
1632 RelationGetRelationName(targetrel)));
1633
1634 namestrcpy(&tgform->tgname, newname);
1635
1636 CatalogTupleUpdate(tgrel, &tuple->t_self, tuple);
1637
1638 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
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 */
1645 CacheInvalidateRelcache(targetrel);
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, GETSTRUCT(), heap_copytuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, sort-test::key, 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{
1656 SysScanDesc tgscan;
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 */
1666 Anum_pg_trigger_tgrelid,
1667 BTEqualStrategyNumber, F_OIDEQ,
1668 ObjectIdGetDatum(partitionId));
1669 tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1670 NULL, 1, &key);
1671 while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1672 {
1673 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tuple);
1674 Relation partitionRel;
1675
1676 if (tgform->tgparentid != parentTriggerOid)
1677 continue; /* not our trigger */
1678
1679 partitionRel = table_open(partitionId, NoLock);
1680
1681 /* Rename the trigger on this partition */
1682 renametrig_internal(tgrel, partitionRel, tuple, newname, expected_name);
1683
1684 /* And if this relation is partitioned, recurse to its partitions */
1685 if (partitionRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1686 {
1687 PartitionDesc partdesc = RelationGetPartitionDesc(partitionRel,
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 }
1698 table_close(partitionRel, NoLock);
1699
1700 /* There should be at most one matching tuple */
1701 break;
1702 }
1703 systable_endscan(tgscan);
1704}

References BTEqualStrategyNumber, GETSTRUCT(), HeapTupleIsValid, i, sort-test::key, NameStr, NoLock, PartitionDescData::nparts, ObjectIdGetDatum(), PartitionDescData::oids, RelationData::rd_rel, 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 5715 of file trigger.c.

5717{
5718 if (state->numstates >= state->numalloc)
5719 {
5720 int newalloc = state->numalloc * 2;
5721
5722 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5725 offsetof(SetConstraintStateData, trigstates) +
5726 newalloc * sizeof(SetConstraintTriggerData));
5727 state->numalloc = newalloc;
5728 Assert(state->numstates < state->numalloc);
5729 }
5730
5731 state->trigstates[state->numstates].sct_tgoid = tgoid;
5732 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5733 state->numstates++;
5734
5735 return state;
5736}
struct SetConstraintTriggerData SetConstraintTriggerData
SetConstraintStateData * SetConstraintState
Definition: trigger.c:3634

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCopy()

static SetConstraintState SetConstraintStateCopy ( SetConstraintState  origstate)
static

Definition at line 5695 of file trigger.c.

5696{
5698
5700
5701 state->all_isset = origstate->all_isset;
5702 state->all_isdeferred = origstate->all_isdeferred;
5703 state->numstates = origstate->numstates;
5704 memcpy(state->trigstates, origstate->trigstates,
5705 origstate->numstates * sizeof(SetConstraintTriggerData));
5706
5707 return state;
5708}
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3631

References SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, SetConstraintStateData::numstates, SetConstraintStateCreate(), and SetConstraintStateData::trigstates.

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCreate()

static SetConstraintState SetConstraintStateCreate ( int  numalloc)
static

Definition at line 5670 of file trigger.c.

5671{
5673
5674 /* Behave sanely with numalloc == 0 */
5675 if (numalloc <= 0)
5676 numalloc = 1;
5677
5678 /*
5679 * We assume that zeroing will correctly initialize the state values.
5680 */
5683 offsetof(SetConstraintStateData, trigstates) +
5684 numalloc * sizeof(SetConstraintTriggerData));
5685
5686 state->numalloc = numalloc;
5687
5688 return state;
5689}
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1263

References 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 |=
2018 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2019 TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT);
2020 trigdesc->trig_insert_after_row |=
2021 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2022 TRIGGER_TYPE_AFTER, TRIGGER_TYPE_INSERT);
2023 trigdesc->trig_insert_instead_row |=
2024 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2025 TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_INSERT);
2026 trigdesc->trig_insert_before_statement |=
2027 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2028 TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT);
2029 trigdesc->trig_insert_after_statement |=
2030 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2031 TRIGGER_TYPE_AFTER, TRIGGER_TYPE_INSERT);
2032 trigdesc->trig_update_before_row |=
2033 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2034 TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_UPDATE);
2035 trigdesc->trig_update_after_row |=
2036 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2037 TRIGGER_TYPE_AFTER, TRIGGER_TYPE_UPDATE);
2038 trigdesc->trig_update_instead_row |=
2039 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2040 TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_UPDATE);
2041 trigdesc->trig_update_before_statement |=
2042 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2043 TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_UPDATE);
2044 trigdesc->trig_update_after_statement |=
2045 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2046 TRIGGER_TYPE_AFTER, TRIGGER_TYPE_UPDATE);
2047 trigdesc->trig_delete_before_row |=
2048 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2049 TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE);
2050 trigdesc->trig_delete_after_row |=
2051 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2052 TRIGGER_TYPE_AFTER, TRIGGER_TYPE_DELETE);
2053 trigdesc->trig_delete_instead_row |=
2054 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2055 TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_DELETE);
2056 trigdesc->trig_delete_before_statement |=
2057 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2058 TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE);
2059 trigdesc->trig_delete_after_statement |=
2060 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2061 TRIGGER_TYPE_AFTER, TRIGGER_TYPE_DELETE);
2062 /* there are no row-level truncate triggers */
2064 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2065 TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_TRUNCATE);
2067 TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2068 TRIGGER_TYPE_AFTER, TRIGGER_TYPE_TRUNCATE);
2069
2070 trigdesc->trig_insert_new_table |=
2071 (TRIGGER_FOR_INSERT(tgtype) &&
2072 TRIGGER_USES_TRANSITION_TABLE(trigger->tgnewtable));
2073 trigdesc->trig_update_old_table |=
2074 (TRIGGER_FOR_UPDATE(tgtype) &&
2075 TRIGGER_USES_TRANSITION_TABLE(trigger->tgoldtable));
2076 trigdesc->trig_update_new_table |=
2077 (TRIGGER_FOR_UPDATE(tgtype) &&
2078 TRIGGER_USES_TRANSITION_TABLE(trigger->tgnewtable));
2079 trigdesc->trig_delete_old_table |=
2080 (TRIGGER_FOR_DELETE(tgtype) &&
2081 TRIGGER_USES_TRANSITION_TABLE(trigger->tgoldtable));
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 Trigger::tgnewtable, Trigger::tgoldtable, Trigger::tgtype, 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,
TransitionCaptureState transition_capture,
ResultRelInfo relinfo,
TupleTableSlot slot,
TupleTableSlot original_insert_tuple,
Tuplestorestate tuplestore 
)
static

Definition at line 5583 of file trigger.c.

5589{
5590 TupleConversionMap *map;
5591
5592 /*
5593 * Nothing needs to be done if we don't have a tuplestore.
5594 */
5595 if (tuplestore == NULL)
5596 return;
5597
5598 if (original_insert_tuple)
5599 tuplestore_puttupleslot(tuplestore, original_insert_tuple);
5600 else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5601 {
5602 AfterTriggersTableData *table = transition_capture->tcs_private;
5603 TupleTableSlot *storeslot;
5604
5605 storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5606 execute_attr_map_slot(map->attrMap, slot, storeslot);
5607 tuplestore_puttupleslot(tuplestore, storeslot);
5608 }
5609 else
5610 tuplestore_puttupleslot(tuplestore, slot);
5611}
TupleDesc outdesc
Definition: tupconvert.h:27
static TupleTableSlot * GetAfterTriggersStoreSlot(AfterTriggersTableData *table, TupleDesc tupdesc)
Definition: trigger.c:4919

References TupleConversionMap::attrMap, ExecGetChildToRootMap(), execute_attr_map_slot(), GetAfterTriggersStoreSlot(), TupleConversionMap::outdesc, table, TransitionCaptureState::tcs_private, 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 {
3527 ExprState **predicate;
3528 ExprContext *econtext;
3529 MemoryContext oldContext;
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
3550 oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
3551 tgqual = stringToNode(trigger->tgqual);
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);
3560 MemoryContextSwitchTo(oldContext);
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:653
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:516
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)
Definition: rewriteManip.c:736
MemoryContext es_query_cxt
Definition: execnodes.h:710
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:275
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:277
ExprState ** ri_TrigWhenExprs
Definition: execnodes.h:521
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
#define SESSION_REPLICATION_ROLE_REPLICA
Definition: trigger.h:141
#define TRIGGER_DISABLED
Definition: trigger.h:152
#define TRIGGER_FIRES_ON_REPLICA
Definition: trigger.h:151

References Assert(), bms_is_member(), ChangeVarNodes(), ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, EState::es_query_cxt, ExecPrepareQual(), ExecQual(), expand_generated_columns_in_expr(), FirstLowInvalidHeapAttributeNumber, GetPerTupleExprContext, i, INNER_VAR, make_ands_implicit(), MemoryContextSwitchTo(), OUTER_VAR, PRS2_NEW_VARNO, PRS2_OLD_VARNO, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigWhenExprs, SESSION_REPLICATION_ROLE_REPLICA, SessionReplicationRole, stringToNode(), Trigger::tgattr, Trigger::tgenabled, Trigger::tgnattr, Trigger::tgqual, TRIGGER_DISABLED, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRES_ON_ORIGIN, TRIGGER_FIRES_ON_REPLICA, and TriggerDesc::triggers.

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{
1225 SysScanDesc tgscan;
1226 ScanKeyData skey[1];
1227 Form_pg_trigger trigForm;
1228 HeapTuple tuple,
1229 newtup;
1230 ObjectAddress depender;
1231 ObjectAddress referenced;
1232
1233 /*
1234 * Find the trigger to delete.
1235 */
1236 ScanKeyInit(&skey[0],
1237 Anum_pg_trigger_oid,
1238 BTEqualStrategyNumber, F_OIDEQ,
1239 ObjectIdGetDatum(childTrigId));
1240
1241 tgscan = systable_beginscan(trigRel, TriggerOidIndexId, true,
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);
1248 trigForm = (Form_pg_trigger) GETSTRUCT(newtup);
1249 if (OidIsValid(parentTrigId))
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
1258 CatalogTupleUpdate(trigRel, &tuple->t_self, newtup);
1259
1260 ObjectAddressSet(depender, TriggerRelationId, childTrigId);
1261
1262 ObjectAddressSet(referenced, TriggerRelationId, parentTrigId);
1263 recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
1264
1265 ObjectAddressSet(referenced, RelationRelationId, childTableId);
1266 recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
1267 }
1268 else
1269 {
1270 trigForm->tgparentid = InvalidOid;
1271
1272 CatalogTupleUpdate(trigRel, &tuple->t_self, newtup);
1273
1274 deleteDependencyRecordsForClass(TriggerRelationId, childTrigId,
1275 TriggerRelationId,
1277 deleteDependencyRecordsForClass(TriggerRelationId, childTrigId,
1278 RelationRelationId,
1280 }
1281
1282 heap_freetuple(newtup);
1283 systable_endscan(tgscan);
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, 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

int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN