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/dbcommands.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, 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)
 
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)
 
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 AfterTriggerAbortQuery (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 3636 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3637 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3638 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3631 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3635 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3634 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3632 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3630 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3639 of file trigger.c.

◆ CHUNK_DATA_START

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

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

◆ for_each_chunk_from

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

Definition at line 3741 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:3719
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3691
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3653

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

◆ GetTriggerSharedData

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

Definition at line 3700 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:3639
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3636
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3637
#define AFTER_TRIGGER_CP_UPDATE
Definition: trigger.c:3638

Definition at line 3691 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3653 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3640 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3824 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3826 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3825 of file trigger.c.

◆ SetConstraintState

Definition at line 3583 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3562 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3628 of file trigger.c.

Function Documentation

◆ AfterTriggerAbortQuery()

void AfterTriggerAbortQuery ( void  )

Definition at line 5068 of file trigger.c.

5069{
5070 /* Revert the actions of AfterTriggerBeginQuery(). */
5072}
static AfterTriggersData afterTriggers
Definition: trigger.c:3890

References afterTriggers, and AfterTriggersData::query_depth.

Referenced by standard_ExecutorEnd().

◆ afterTriggerAddEvent()

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

Definition at line 4036 of file trigger.c.

4038{
4039 Size eventsize = SizeofTriggerEvent(event);
4040 Size needed = eventsize + sizeof(AfterTriggerSharedData);
4042 AfterTriggerShared newshared;
4043 AfterTriggerEvent newevent;
4044
4045 /*
4046 * If empty list or not enough room in the tail chunk, make a new chunk.
4047 * We assume here that a new shared record will always be needed.
4048 */
4049 chunk = events->tail;
4050 if (chunk == NULL ||
4051 chunk->endfree - chunk->freeptr < needed)
4052 {
4053 Size chunksize;
4054
4055 /* Create event context if we didn't already */
4056 if (afterTriggers.event_cxt == NULL)
4059 "AfterTriggerEvents",
4061
4062 /*
4063 * Chunk size starts at 1KB and is allowed to increase up to 1MB.
4064 * These numbers are fairly arbitrary, though there is a hard limit at
4065 * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
4066 * shared records using the available space in ate_flags. Another
4067 * constraint is that if the chunk size gets too huge, the search loop
4068 * below would get slow given a (not too common) usage pattern with
4069 * many distinct event types in a chunk. Therefore, we double the
4070 * preceding chunk size only if there weren't too many shared records
4071 * in the preceding chunk; otherwise we halve it. This gives us some
4072 * ability to adapt to the actual usage pattern of the current query
4073 * while still having large chunk sizes in typical usage. All chunk
4074 * sizes used should be MAXALIGN multiples, to ensure that the shared
4075 * records will be aligned safely.
4076 */
4077#define MIN_CHUNK_SIZE 1024
4078#define MAX_CHUNK_SIZE (1024*1024)
4079
4080#if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
4081#error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
4082#endif
4083
4084 if (chunk == NULL)
4085 chunksize = MIN_CHUNK_SIZE;
4086 else
4087 {
4088 /* preceding chunk size... */
4089 chunksize = chunk->endptr - (char *) chunk;
4090 /* check number of shared records in preceding chunk */
4091 if ((chunk->endptr - chunk->endfree) <=
4092 (100 * sizeof(AfterTriggerSharedData)))
4093 chunksize *= 2; /* okay, double it */
4094 else
4095 chunksize /= 2; /* too many shared records */
4096 chunksize = Min(chunksize, MAX_CHUNK_SIZE);
4097 }
4098 chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize);
4099 chunk->next = NULL;
4100 chunk->freeptr = CHUNK_DATA_START(chunk);
4101 chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
4102 Assert(chunk->endfree - chunk->freeptr >= needed);
4103
4104 if (events->tail == NULL)
4105 {
4106 Assert(events->head == NULL);
4107 events->head = chunk;
4108 }
4109 else
4110 events->tail->next = chunk;
4111 events->tail = chunk;
4112 /* events->tailfree is now out of sync, but we'll fix it below */
4113 }
4114
4115 /*
4116 * Try to locate a matching shared-data record already in the chunk. If
4117 * none, make a new one. The search begins with the most recently added
4118 * record, since newer ones are most likely to match.
4119 */
4120 for (newshared = (AfterTriggerShared) chunk->endfree;
4121 (char *) newshared < chunk->endptr;
4122 newshared++)
4123 {
4124 /* compare fields roughly by probability of them being different */
4125 if (newshared->ats_tgoid == evtshared->ats_tgoid &&
4126 newshared->ats_event == evtshared->ats_event &&
4127 newshared->ats_firing_id == 0 &&
4128 newshared->ats_table == evtshared->ats_table &&
4129 newshared->ats_relid == evtshared->ats_relid &&
4130 newshared->ats_rolid == evtshared->ats_rolid &&
4131 bms_equal(newshared->ats_modifiedcols,
4132 evtshared->ats_modifiedcols))
4133 break;
4134 }
4135 if ((char *) newshared >= chunk->endptr)
4136 {
4137 newshared = ((AfterTriggerShared) chunk->endfree) - 1;
4138 *newshared = *evtshared;
4139 /* now we must make a suitably-long-lived copy of the bitmap */
4141 newshared->ats_firing_id = 0; /* just to be sure */
4142 chunk->endfree = (char *) newshared;
4143 }
4144
4145 /* Insert the data */
4146 newevent = (AfterTriggerEvent) chunk->freeptr;
4147 memcpy(newevent, event, eventsize);
4148 /* ... and link the new event to its shared record */
4149 newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
4150 newevent->ate_flags |= (char *) newshared - (char *) newevent;
4151
4152 chunk->freeptr += eventsize;
4153 events->tailfree = chunk->freeptr;
4154}
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:142
#define Min(x, y)
Definition: c.h:975
size_t Size
Definition: c.h:576
Assert(PointerIsAligned(start, uint64))
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
MemoryContext TopTransactionContext
Definition: mcxt.c:154
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
struct AfterTriggerEventChunk * next
Definition: trigger.c:3712
TriggerFlags ate_flags
Definition: trigger.c:3657
AfterTriggerEventChunk * head
Definition: trigger.c:3724
AfterTriggerEventChunk * tail
Definition: trigger.c:3725
TriggerEvent ats_event
Definition: trigger.c:3644
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3649
CommandId ats_firing_id
Definition: trigger.c:3648
Bitmapset * ats_modifiedcols
Definition: trigger.c:3650
MemoryContext event_cxt
Definition: trigger.c:3833
struct AfterTriggerSharedData AfterTriggerSharedData
static Bitmapset * afterTriggerCopyBitmap(Bitmapset *src)
Definition: trigger.c:4011
#define MAX_CHUNK_SIZE
struct AfterTriggerSharedData * AfterTriggerShared
Definition: trigger.c:3640
#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 5053 of file trigger.c.

5054{
5055 /* Increase the query stack depth */
5057}

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5351 of file trigger.c.

5352{
5353 int my_level = GetCurrentTransactionNestLevel();
5354
5355 /*
5356 * Allocate more space in the trans_stack if needed. (Note: because the
5357 * minimum nest level of a subtransaction is 2, we waste the first couple
5358 * entries of the array; not worth the notational effort to avoid it.)
5359 */
5360 while (my_level >= afterTriggers.maxtransdepth)
5361 {
5363 {
5364 /* Arbitrarily initialize for max of 8 subtransaction levels */
5367 8 * sizeof(AfterTriggersTransData));
5369 }
5370 else
5371 {
5372 /* repalloc will keep the stack in the same context */
5373 int new_alloc = afterTriggers.maxtransdepth * 2;
5374
5377 new_alloc * sizeof(AfterTriggersTransData));
5378 afterTriggers.maxtransdepth = new_alloc;
5379 }
5380 }
5381
5382 /*
5383 * Push the current information into the stack. The SET CONSTRAINTS state
5384 * is not saved until/unless changed. Likewise, we don't make a
5385 * per-subtransaction event context until needed.
5386 */
5387 afterTriggers.trans_stack[my_level].state = NULL;
5391}
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1544
CommandId firing_counter
Definition: trigger.c:3830
AfterTriggersTransData * trans_stack
Definition: trigger.c:3841
AfterTriggerEventList events
Definition: trigger.c:3832
AfterTriggerEventList events
Definition: trigger.c:3856
SetConstraintState state
Definition: trigger.c:3855
CommandId firing_counter
Definition: trigger.c:3858
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 5021 of file trigger.c.

5022{
5023 /*
5024 * Initialize after-trigger state structure to empty
5025 */
5026 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5028
5029 /*
5030 * Verify that there is no leftover state remaining. If these assertions
5031 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5032 * up properly.
5033 */
5034 Assert(afterTriggers.state == NULL);
5041}
uint32 CommandId
Definition: c.h:637
SetConstraintState state
Definition: trigger.c:3831
AfterTriggersQueryData * query_stack
Definition: trigger.c:3836

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

3967{
3968 Oid tgoid = evtshared->ats_tgoid;
3970 int i;
3971
3972 /*
3973 * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3974 * constraints declared NOT DEFERRABLE), the state is always false.
3975 */
3976 if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3977 return false;
3978
3979 /*
3980 * If constraint state exists, SET CONSTRAINTS might have been executed
3981 * either for this trigger or for all triggers.
3982 */
3983 if (state != NULL)
3984 {
3985 /* Check for SET CONSTRAINTS for this specific trigger. */
3986 for (i = 0; i < state->numstates; i++)
3987 {
3988 if (state->trigstates[i].sct_tgoid == tgoid)
3989 return state->trigstates[i].sct_tgisdeferred;
3990 }
3991
3992 /* Check for SET CONSTRAINTS ALL. */
3993 if (state->all_isset)
3994 return state->all_isdeferred;
3995 }
3996
3997 /*
3998 * Otherwise return the default state for the trigger.
3999 */
4000 return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
4001}
int i
Definition: isn.c:74
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 4011 of file trigger.c.

4012{
4013 Bitmapset *dst;
4014 MemoryContext oldcxt;
4015
4016 if (src == NULL)
4017 return NULL;
4018
4020
4021 dst = bms_copy(src);
4022
4023 MemoryContextSwitchTo(oldcxt);
4024
4025 return dst;
4026}
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 4224 of file trigger.c.

4225{
4226 AfterTriggerEventChunk *target = qs->events.head;
4227 ListCell *lc;
4228
4229 Assert(target && target->next);
4230
4231 /*
4232 * First, update any pointers in the per-table data, so that they won't be
4233 * dangling. Resetting obsoleted pointers to NULL will make
4234 * cancel_prior_stmt_triggers start from the list head, which is fine.
4235 */
4236 foreach(lc, qs->tables)
4237 {
4239
4240 if (table->after_trig_done &&
4241 table->after_trig_events.tail == target)
4242 {
4243 table->after_trig_events.head = NULL;
4244 table->after_trig_events.tail = NULL;
4245 table->after_trig_events.tailfree = NULL;
4246 }
4247 }
4248
4249 /* Now we can flush the head chunk */
4250 qs->events.head = target->next;
4251 pfree(target);
4252}
void pfree(void *pointer)
Definition: mcxt.c:1524
#define lfirst(lc)
Definition: pg_list.h:172
AfterTriggerEventList events
Definition: trigger.c:3847
AfterTriggerEventList after_trig_events
Definition: trigger.c:3869

References AfterTriggersTableData::after_trig_done, AfterTriggersTableData::after_trig_events, Assert(), AfterTriggersQueryData::events, AfterTriggerEventList::head, lfirst, AfterTriggerEventChunk::next, pfree(), AfterTriggersQueryData::tables, AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.

Referenced by AfterTriggerEndQuery().

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 5088 of file trigger.c.

5089{
5091
5092 /* Must be inside a query, too */
5094
5095 /*
5096 * If we never even got as far as initializing the event stack, there
5097 * certainly won't be any events, so exit quickly.
5098 */
5100 {
5102 return;
5103 }
5104
5105 /*
5106 * Process all immediate-mode triggers queued by the query, and move the
5107 * deferred ones to the main list of deferred events.
5108 *
5109 * Notice that we decide which ones will be fired, and put the deferred
5110 * ones on the main list, before anything is actually fired. This ensures
5111 * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5112 * IMMEDIATE: all events we have decided to defer will be available for it
5113 * to fire.
5114 *
5115 * We loop in case a trigger queues more events at the same query level.
5116 * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5117 * will instead fire any triggers in a dedicated query level. Foreign key
5118 * enforcement triggers do add to the current query level, thanks to their
5119 * passing fire_triggers = false to SPI_execute_snapshot(). Other
5120 * C-language triggers might do likewise.
5121 *
5122 * If we find no firable events, we don't have to increment
5123 * firing_counter.
5124 */
5126
5127 for (;;)
5128 {
5130 {
5132 AfterTriggerEventChunk *oldtail = qs->events.tail;
5133
5134 if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5135 break; /* all fired */
5136
5137 /*
5138 * Firing a trigger could result in query_stack being repalloc'd,
5139 * so we must recalculate qs after each afterTriggerInvokeEvents
5140 * call. Furthermore, it's unsafe to pass delete_ok = true here,
5141 * because that could cause afterTriggerInvokeEvents to try to
5142 * access qs->events after the stack has been repalloc'd.
5143 */
5145
5146 /*
5147 * We'll need to scan the events list again. To reduce the cost
5148 * of doing so, get rid of completely-fired chunks. We know that
5149 * all events were marked IN_PROGRESS or DONE at the conclusion of
5150 * afterTriggerMarkEvents, so any still-interesting events must
5151 * have been added after that, and so must be in the chunk that
5152 * was then the tail chunk, or in later chunks. So, zap all
5153 * chunks before oldtail. This is approximately the same set of
5154 * events we would have gotten rid of by passing delete_ok = true.
5155 */
5156 Assert(oldtail != NULL);
5157 while (qs->events.head != oldtail)
5159 }
5160 else
5161 break;
5162 }
5163
5164 /* Release query-level-local storage, including tuplestores if any */
5166
5168}
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition: trigger.c:4224
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:5179
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4578
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4662

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

5400{
5401 int my_level = GetCurrentTransactionNestLevel();
5403 AfterTriggerEvent event;
5405 CommandId subxact_firing_id;
5406
5407 /*
5408 * Pop the prior state if needed.
5409 */
5410 if (isCommit)
5411 {
5413 /* If we saved a prior state, we don't need it anymore */
5415 if (state != NULL)
5416 pfree(state);
5417 /* this avoids double pfree if error later: */
5418 afterTriggers.trans_stack[my_level].state = NULL;
5421 }
5422 else
5423 {
5424 /*
5425 * Aborting. It is possible subxact start failed before calling
5426 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5427 * trans_stack levels that aren't there.
5428 */
5429 if (my_level >= afterTriggers.maxtransdepth)
5430 return;
5431
5432 /*
5433 * Release query-level storage for queries being aborted, and restore
5434 * query_depth to its pre-subxact value. This assumes that a
5435 * subtransaction will not add events to query levels started in a
5436 * earlier transaction state.
5437 */
5439 {
5443 }
5446
5447 /*
5448 * Restore the global deferred-event list to its former length,
5449 * discarding any events queued by the subxact.
5450 */
5452 &afterTriggers.trans_stack[my_level].events);
5453
5454 /*
5455 * Restore the trigger state. If the saved state is NULL, then this
5456 * subxact didn't save it, so it doesn't need restoring.
5457 */
5459 if (state != NULL)
5460 {
5463 }
5464 /* this avoids double pfree if error later: */
5465 afterTriggers.trans_stack[my_level].state = NULL;
5466
5467 /*
5468 * Scan for any remaining deferred events that were marked DONE or IN
5469 * PROGRESS by this subxact or a child, and un-mark them. We can
5470 * recognize such events because they have a firing ID greater than or
5471 * equal to the firing_counter value we saved at subtransaction start.
5472 * (This essentially assumes that the current subxact includes all
5473 * subxacts started after it.)
5474 */
5475 subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5477 {
5478 AfterTriggerShared evtshared = GetTriggerSharedData(event);
5479
5480 if (event->ate_flags &
5482 {
5483 if (evtshared->ats_firing_id >= subxact_firing_id)
5484 event->ate_flags &=
5486 }
5487 }
5488 }
5489}
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3632
#define GetTriggerSharedData(evt)
Definition: trigger.c:3700
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:4184
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3631
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3737

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

5304{
5305 /*
5306 * Forget the pending-events list.
5307 *
5308 * Since all the info is in TopTransactionContext or children thereof, we
5309 * don't really need to do anything to reclaim memory. However, the
5310 * pending-events list could be large, and so it's useful to discard it as
5311 * soon as possible --- especially if we are aborting because we ran out
5312 * of memory for the list!
5313 */
5315 {
5317 afterTriggers.event_cxt = NULL;
5318 afterTriggers.events.head = NULL;
5319 afterTriggers.events.tail = NULL;
5321 }
5322
5323 /*
5324 * Forget any subtransaction state as well. Since this can't be very
5325 * large, we let the eventual reset of TopTransactionContext free the
5326 * memory instead of doing it here.
5327 */
5330
5331
5332 /*
5333 * Forget the query stack and constraint-related state information. As
5334 * with the subtransaction state information, we don't bother freeing the
5335 * memory here.
5336 */
5339 afterTriggers.state = NULL;
5340
5341 /* No more afterTriggers manipulation until next transaction starts. */
5343}
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454

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

5588{
5589 int init_depth = afterTriggers.maxquerydepth;
5590
5592
5594 {
5595 int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5596
5599 new_alloc * sizeof(AfterTriggersQueryData));
5600 afterTriggers.maxquerydepth = new_alloc;
5601 }
5602 else
5603 {
5604 /* repalloc will keep the stack in the same context */
5605 int old_alloc = afterTriggers.maxquerydepth;
5606 int new_alloc = Max(afterTriggers.query_depth + 1,
5607 old_alloc * 2);
5608
5611 new_alloc * sizeof(AfterTriggersQueryData));
5612 afterTriggers.maxquerydepth = new_alloc;
5613 }
5614
5615 /* Initialize new array entries to empty */
5616 while (init_depth < afterTriggers.maxquerydepth)
5617 {
5619
5620 qs->events.head = NULL;
5621 qs->events.tail = NULL;
5622 qs->events.tailfree = NULL;
5623 qs->fdw_tuplestore = NULL;
5624 qs->tables = NIL;
5625
5626 ++init_depth;
5627 }
5628}
#define Max(x, y)
Definition: c.h:969
#define NIL
Definition: pg_list.h:68
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3848

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

4296{
4297 Relation rel = relInfo->ri_RelationDesc;
4298 Relation src_rel = src_relInfo->ri_RelationDesc;
4299 Relation dst_rel = dst_relInfo->ri_RelationDesc;
4300 AfterTriggerShared evtshared = GetTriggerSharedData(event);
4301 Oid tgoid = evtshared->ats_tgoid;
4302 TriggerData LocTriggerData = {0};
4303 Oid save_rolid;
4304 int save_sec_context;
4305 HeapTuple rettuple;
4306 int tgindx;
4307 bool should_free_trig = false;
4308 bool should_free_new = false;
4309
4310 /*
4311 * Locate trigger in trigdesc. It might not be present, and in fact the
4312 * trigdesc could be NULL, if the trigger was dropped since the event was
4313 * queued. In that case, silently do nothing.
4314 */
4315 if (trigdesc == NULL)
4316 return;
4317 for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4318 {
4319 if (trigdesc->triggers[tgindx].tgoid == tgoid)
4320 {
4321 LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4322 break;
4323 }
4324 }
4325 if (LocTriggerData.tg_trigger == NULL)
4326 return;
4327
4328 /*
4329 * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4330 * to include time spent re-fetching tuples in the trigger cost.
4331 */
4332 if (instr)
4333 InstrStartNode(instr + tgindx);
4334
4335 /*
4336 * Fetch the required tuple(s).
4337 */
4338 switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4339 {
4341 {
4342 Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4343
4344 if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4345 trig_tuple_slot1))
4346 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4347
4348 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4350 !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4351 trig_tuple_slot2))
4352 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4353 }
4354 /* fall through */
4356
4357 /*
4358 * Store tuple in the slot so that tg_trigtuple does not reference
4359 * tuplestore memory. (It is formally possible for the trigger
4360 * function to queue trigger events that add to the same
4361 * tuplestore, which can push other tuples out of memory.) The
4362 * distinction is academic, because we start with a minimal tuple
4363 * that is stored as a heap tuple, constructed in different memory
4364 * context, in the slot anyway.
4365 */
4366 LocTriggerData.tg_trigslot = trig_tuple_slot1;
4367 LocTriggerData.tg_trigtuple =
4368 ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
4369
4370 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4372 {
4373 LocTriggerData.tg_newslot = trig_tuple_slot2;
4374 LocTriggerData.tg_newtuple =
4375 ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new);
4376 }
4377 else
4378 {
4379 LocTriggerData.tg_newtuple = NULL;
4380 }
4381 break;
4382
4383 default:
4384 if (ItemPointerIsValid(&(event->ate_ctid1)))
4385 {
4386 TupleTableSlot *src_slot = ExecGetTriggerOldSlot(estate,
4387 src_relInfo);
4388
4389 if (!table_tuple_fetch_row_version(src_rel,
4390 &(event->ate_ctid1),
4392 src_slot))
4393 elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4394
4395 /*
4396 * Store the tuple fetched from the source partition into the
4397 * target (root partitioned) table slot, converting if needed.
4398 */
4399 if (src_relInfo != relInfo)
4400 {
4401 TupleConversionMap *map = ExecGetChildToRootMap(src_relInfo);
4402
4403 LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4404 if (map)
4405 {
4407 src_slot,
4408 LocTriggerData.tg_trigslot);
4409 }
4410 else
4411 ExecCopySlot(LocTriggerData.tg_trigslot, src_slot);
4412 }
4413 else
4414 LocTriggerData.tg_trigslot = src_slot;
4415 LocTriggerData.tg_trigtuple =
4416 ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, &should_free_trig);
4417 }
4418 else
4419 {
4420 LocTriggerData.tg_trigtuple = NULL;
4421 }
4422
4423 /* don't touch ctid2 if not there */
4425 (event->ate_flags & AFTER_TRIGGER_CP_UPDATE)) &&
4426 ItemPointerIsValid(&(event->ate_ctid2)))
4427 {
4428 TupleTableSlot *dst_slot = ExecGetTriggerNewSlot(estate,
4429 dst_relInfo);
4430
4431 if (!table_tuple_fetch_row_version(dst_rel,
4432 &(event->ate_ctid2),
4434 dst_slot))
4435 elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4436
4437 /*
4438 * Store the tuple fetched from the destination partition into
4439 * the target (root partitioned) table slot, converting if
4440 * needed.
4441 */
4442 if (dst_relInfo != relInfo)
4443 {
4444 TupleConversionMap *map = ExecGetChildToRootMap(dst_relInfo);
4445
4446 LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4447 if (map)
4448 {
4450 dst_slot,
4451 LocTriggerData.tg_newslot);
4452 }
4453 else
4454 ExecCopySlot(LocTriggerData.tg_newslot, dst_slot);
4455 }
4456 else
4457 LocTriggerData.tg_newslot = dst_slot;
4458 LocTriggerData.tg_newtuple =
4459 ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, &should_free_new);
4460 }
4461 else
4462 {
4463 LocTriggerData.tg_newtuple = NULL;
4464 }
4465 }
4466
4467 /*
4468 * Set up the tuplestore information to let the trigger have access to
4469 * transition tables. When we first make a transition table available to
4470 * a trigger, mark it "closed" so that it cannot change anymore. If any
4471 * additional events of the same type get queued in the current trigger
4472 * query level, they'll go into new transition tables.
4473 */
4474 LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4475 if (evtshared->ats_table)
4476 {
4477 if (LocTriggerData.tg_trigger->tgoldtable)
4478 {
4479 if (TRIGGER_FIRED_BY_UPDATE(evtshared->ats_event))
4480 LocTriggerData.tg_oldtable = evtshared->ats_table->old_upd_tuplestore;
4481 else
4482 LocTriggerData.tg_oldtable = evtshared->ats_table->old_del_tuplestore;
4483 evtshared->ats_table->closed = true;
4484 }
4485
4486 if (LocTriggerData.tg_trigger->tgnewtable)
4487 {
4488 if (TRIGGER_FIRED_BY_INSERT(evtshared->ats_event))
4489 LocTriggerData.tg_newtable = evtshared->ats_table->new_ins_tuplestore;
4490 else
4491 LocTriggerData.tg_newtable = evtshared->ats_table->new_upd_tuplestore;
4492 evtshared->ats_table->closed = true;
4493 }
4494 }
4495
4496 /*
4497 * Setup the remaining trigger information
4498 */
4499 LocTriggerData.type = T_TriggerData;
4500 LocTriggerData.tg_event =
4502 LocTriggerData.tg_relation = rel;
4503 if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4504 LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4505
4506 MemoryContextReset(per_tuple_context);
4507
4508 /*
4509 * If necessary, become the role that was active when the trigger got
4510 * queued. Note that the role might have been dropped since the trigger
4511 * was queued, but if that is a problem, we will get an error later.
4512 * Checking here would still leave a race condition.
4513 */
4514 GetUserIdAndSecContext(&save_rolid, &save_sec_context);
4515 if (save_rolid != evtshared->ats_rolid)
4517 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
4518
4519 /*
4520 * Call the trigger and throw away any possibly returned updated tuple.
4521 * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4522 */
4523 rettuple = ExecCallTriggerFunc(&LocTriggerData,
4524 tgindx,
4525 finfo,
4526 NULL,
4527 per_tuple_context);
4528 if (rettuple != NULL &&
4529 rettuple != LocTriggerData.tg_trigtuple &&
4530 rettuple != LocTriggerData.tg_newtuple)
4531 heap_freetuple(rettuple);
4532
4533 /* Restore the current role if necessary */
4534 if (save_rolid != evtshared->ats_rolid)
4535 SetUserIdAndSecContext(save_rolid, save_sec_context);
4536
4537 /*
4538 * Release resources
4539 */
4540 if (should_free_trig)
4541 heap_freetuple(LocTriggerData.tg_trigtuple);
4542 if (should_free_new)
4543 heap_freetuple(LocTriggerData.tg_newtuple);
4544
4545 /* don't clear slots' contents if foreign table */
4546 if (trig_tuple_slot1 == NULL)
4547 {
4548 if (LocTriggerData.tg_trigslot)
4549 ExecClearTuple(LocTriggerData.tg_trigslot);
4550 if (LocTriggerData.tg_newslot)
4551 ExecClearTuple(LocTriggerData.tg_newslot);
4552 }
4553
4554 /*
4555 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4556 * one "tuple returned" (really the number of firings).
4557 */
4558 if (instr)
4559 InstrStopNode(instr + tgindx, 1);
4560}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1831
TupleTableSlot * ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1228
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1302
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1206
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:383
#define SECURITY_LOCAL_USERID_CHANGE
Definition: miscadmin.h:317
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:663
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:670
#define SnapshotAny
Definition: snapmgr.h:33
ItemPointerData ate_ctid2
Definition: trigger.c:3659
ItemPointerData ate_ctid1
Definition: trigger.c:3658
Tuplestorestate * old_upd_tuplestore
Definition: trigger.c:3879
Tuplestorestate * new_upd_tuplestore
Definition: trigger.c:3881
Tuplestorestate * old_del_tuplestore
Definition: trigger.c:3883
Tuplestorestate * new_ins_tuplestore
Definition: trigger.c:3885
Relation ri_RelationDesc
Definition: execnodes.h:475
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:1258
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3635
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition: trigger.c:2307
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3930
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3634
#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:454
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:509

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

5248{
5249 AfterTriggerEventList *events;
5250 bool snap_pushed = false;
5251
5252 /* Must not be inside a query */
5254
5255 /*
5256 * If there are any triggers to fire, make sure we have set a snapshot for
5257 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5258 * can't assume ActiveSnapshot is valid on entry.)
5259 */
5260 events = &afterTriggers.events;
5261 if (events->head != NULL)
5262 {
5264 snap_pushed = true;
5265 }
5266
5267 /*
5268 * Run all the remaining triggers. Loop until they are all gone, in case
5269 * some trigger queues more for us to do.
5270 */
5271 while (afterTriggerMarkEvents(events, NULL, false))
5272 {
5274
5275 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5276 break; /* all fired */
5277 }
5278
5279 /*
5280 * We don't bother freeing the event list, since it will go away anyway
5281 * (and more efficiently than via pfree) in AfterTriggerEndXact.
5282 */
5283
5284 if (snap_pushed)
5286}
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:669
void PopActiveSnapshot(void)
Definition: snapmgr.c:762

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

4164{
4166
4167 while ((chunk = events->head) != NULL)
4168 {
4169 events->head = chunk->next;
4170 pfree(chunk);
4171 }
4172 events->tail = NULL;
4173 events->tailfree = NULL;
4174}

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

5180{
5181 Tuplestorestate *ts;
5182 List *tables;
5183 ListCell *lc;
5184
5185 /* Drop the trigger events */
5187
5188 /* Drop FDW tuplestore if any */
5189 ts = qs->fdw_tuplestore;
5190 qs->fdw_tuplestore = NULL;
5191 if (ts)
5192 tuplestore_end(ts);
5193
5194 /* Release per-table subsidiary storage */
5195 tables = qs->tables;
5196 foreach(lc, tables)
5197 {
5199
5200 ts = table->old_upd_tuplestore;
5201 table->old_upd_tuplestore = NULL;
5202 if (ts)
5203 tuplestore_end(ts);
5204 ts = table->new_upd_tuplestore;
5205 table->new_upd_tuplestore = NULL;
5206 if (ts)
5207 tuplestore_end(ts);
5208 ts = table->old_del_tuplestore;
5209 table->old_del_tuplestore = NULL;
5210 if (ts)
5211 tuplestore_end(ts);
5212 ts = table->new_ins_tuplestore;
5213 table->new_ins_tuplestore = NULL;
5214 if (ts)
5215 tuplestore_end(ts);
5216 if (table->storeslot)
5217 {
5218 TupleTableSlot *slot = table->storeslot;
5219
5220 table->storeslot = NULL;
5222 }
5223 }
5224
5225 /*
5226 * Now free the AfterTriggersTableData structs and list cells. Reset list
5227 * pointer first; if list_free_deep somehow gets an error, better to leak
5228 * that storage than have an infinite loop.
5229 */
5230 qs->tables = NIL;
5231 list_free_deep(tables);
5232}
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1441
void list_free_deep(List *list)
Definition: list.c:1560
TupleTableSlot * storeslot
Definition: trigger.c:3887
Definition: pg_list.h:54
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4163
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:492

References afterTriggerFreeEventList(), AfterTriggersQueryData::events, ExecDropSingleTupleTableSlot(), AfterTriggersQueryData::fdw_tuplestore, lfirst, list_free_deep(), AfterTriggersTableData::new_ins_tuplestore, AfterTriggersTableData::new_upd_tuplestore, NIL, AfterTriggersTableData::old_del_tuplestore, AfterTriggersTableData::old_upd_tuplestore, AfterTriggersTableData::storeslot, 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 4662 of file trigger.c.

4666{
4667 bool all_fired = true;
4669 MemoryContext per_tuple_context;
4670 bool local_estate = false;
4671 ResultRelInfo *rInfo = NULL;
4672 Relation rel = NULL;
4673 TriggerDesc *trigdesc = NULL;
4674 FmgrInfo *finfo = NULL;
4675 Instrumentation *instr = NULL;
4676 TupleTableSlot *slot1 = NULL,
4677 *slot2 = NULL;
4678
4679 /* Make a local EState if need be */
4680 if (estate == NULL)
4681 {
4682 estate = CreateExecutorState();
4683 local_estate = true;
4684 }
4685
4686 /* Make a per-tuple memory context for trigger function calls */
4687 per_tuple_context =
4689 "AfterTriggerTupleContext",
4691
4692 for_each_chunk(chunk, *events)
4693 {
4694 AfterTriggerEvent event;
4695 bool all_fired_in_chunk = true;
4696
4697 for_each_event(event, chunk)
4698 {
4699 AfterTriggerShared evtshared = GetTriggerSharedData(event);
4700
4701 /*
4702 * Is it one for me to fire?
4703 */
4704 if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4705 evtshared->ats_firing_id == firing_id)
4706 {
4707 ResultRelInfo *src_rInfo,
4708 *dst_rInfo;
4709
4710 /*
4711 * So let's fire it... but first, find the correct relation if
4712 * this is not the same relation as before.
4713 */
4714 if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4715 {
4716 rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4717 NULL);
4718 rel = rInfo->ri_RelationDesc;
4719 /* Catch calls with insufficient relcache refcounting */
4721 trigdesc = rInfo->ri_TrigDesc;
4722 /* caution: trigdesc could be NULL here */
4723 finfo = rInfo->ri_TrigFunctions;
4724 instr = rInfo->ri_TrigInstrument;
4725 if (slot1 != NULL)
4726 {
4729 slot1 = slot2 = NULL;
4730 }
4731 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4732 {
4733 slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4735 slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4737 }
4738 }
4739
4740 /*
4741 * Look up source and destination partition result rels of a
4742 * cross-partition update event.
4743 */
4744 if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4746 {
4747 Assert(OidIsValid(event->ate_src_part) &&
4748 OidIsValid(event->ate_dst_part));
4749 src_rInfo = ExecGetTriggerResultRel(estate,
4750 event->ate_src_part,
4751 rInfo);
4752 dst_rInfo = ExecGetTriggerResultRel(estate,
4753 event->ate_dst_part,
4754 rInfo);
4755 }
4756 else
4757 src_rInfo = dst_rInfo = rInfo;
4758
4759 /*
4760 * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4761 * still set, so recursive examinations of the event list
4762 * won't try to re-fire it.
4763 */
4764 AfterTriggerExecute(estate, event, rInfo,
4765 src_rInfo, dst_rInfo,
4766 trigdesc, finfo, instr,
4767 per_tuple_context, slot1, slot2);
4768
4769 /*
4770 * Mark the event as done.
4771 */
4772 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4773 event->ate_flags |= AFTER_TRIGGER_DONE;
4774 }
4775 else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4776 {
4777 /* something remains to be done */
4778 all_fired = all_fired_in_chunk = false;
4779 }
4780 }
4781
4782 /* Clear the chunk if delete_ok and nothing left of interest */
4783 if (delete_ok && all_fired_in_chunk)
4784 {
4785 chunk->freeptr = CHUNK_DATA_START(chunk);
4786 chunk->endfree = chunk->endptr;
4787
4788 /*
4789 * If it's last chunk, must sync event list's tailfree too. Note
4790 * that delete_ok must NOT be passed as true if there could be
4791 * additional AfterTriggerEventList values pointing at this event
4792 * list, since we'd fail to fix their copies of tailfree.
4793 */
4794 if (chunk == events->tail)
4795 events->tailfree = chunk->freeptr;
4796 }
4797 }
4798 if (slot1 != NULL)
4799 {
4802 }
4803
4804 /* Release working resources */
4805 MemoryContextDelete(per_tuple_context);
4806
4807 if (local_estate)
4808 {
4810 ExecResetTupleTable(estate->es_tupleTable, false);
4811 FreeExecutorState(estate);
4812 }
4813
4814 return all_fired;
4815}
#define OidIsValid(objectId)
Definition: c.h:746
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid, ResultRelInfo *rootRelInfo)
Definition: execMain.c:1426
void ExecCloseResultRelations(EState *estate)
Definition: execMain.c:1647
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1378
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1425
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:86
void FreeExecutorState(EState *estate)
Definition: execUtils.c:193
EState * CreateExecutorState(void)
Definition: execUtils.c:88
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define RelationHasReferenceCountZero(relation)
Definition: rel.h:497
#define RelationGetRelid(relation)
Definition: rel.h:513
List * es_tupleTable
Definition: execnodes.h:704
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:519
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:510
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:513
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:4286
#define for_each_event(eptr, cptr)
Definition: trigger.c:3732
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3730

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

4581{
4582 bool found = false;
4583 bool deferred_found = false;
4584 AfterTriggerEvent event;
4586
4587 for_each_event_chunk(event, chunk, *events)
4588 {
4589 AfterTriggerShared evtshared = GetTriggerSharedData(event);
4590 bool defer_it = false;
4591
4592 if (!(event->ate_flags &
4594 {
4595 /*
4596 * This trigger hasn't been called or scheduled yet. Check if we
4597 * should call it now.
4598 */
4599 if (immediate_only && afterTriggerCheckState(evtshared))
4600 {
4601 defer_it = true;
4602 }
4603 else
4604 {
4605 /*
4606 * Mark it as to be fired in this firing cycle.
4607 */
4609 event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4610 found = true;
4611 }
4612 }
4613
4614 /*
4615 * If it's deferred, move it to move_list, if requested.
4616 */
4617 if (defer_it && move_list != NULL)
4618 {
4619 deferred_found = true;
4620 /* add it to move_list */
4621 afterTriggerAddEvent(move_list, event, evtshared);
4622 /* mark original copy "done" so we don't do it again */
4623 event->ate_flags |= AFTER_TRIGGER_DONE;
4624 }
4625 }
4626
4627 /*
4628 * We could allow deferred triggers if, before the end of the
4629 * security-restricted operation, we were to verify that a SET CONSTRAINTS
4630 * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4631 */
4632 if (deferred_found && InSecurityRestrictedOperation())
4633 ereport(ERROR,
4634 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4635 errmsg("cannot fire deferred trigger within security-restricted operation")));
4636
4637 return found;
4638}
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ereport(elevel,...)
Definition: elog.h:149
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:690
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:4036
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3966

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

6025{
6026 AfterTriggerEvent event;
6028 int depth;
6029
6030 /* Scan queued events */
6032 {
6033 AfterTriggerShared evtshared = GetTriggerSharedData(event);
6034
6035 /*
6036 * We can ignore completed events. (Even if a DONE flag is rolled
6037 * back by subxact abort, it's OK because the effects of the TRUNCATE
6038 * or whatever must get rolled back too.)
6039 */
6040 if (event->ate_flags & AFTER_TRIGGER_DONE)
6041 continue;
6042
6043 if (evtshared->ats_relid == relid)
6044 return true;
6045 }
6046
6047 /*
6048 * Also scan events queued by incomplete queries. This could only matter
6049 * if TRUNCATE/etc is executed by a function or trigger within an updating
6050 * query on the same relation, which is pretty perverse, but let's check.
6051 */
6052 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6053 {
6055 {
6056 AfterTriggerShared evtshared = GetTriggerSharedData(event);
6057
6058 if (event->ate_flags & AFTER_TRIGGER_DONE)
6059 continue;
6060
6061 if (evtshared->ats_relid == relid)
6062 return true;
6063 }
6064 }
6065
6066 return false;
6067}

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

4186{
4188 AfterTriggerEventChunk *next_chunk;
4189
4190 if (old_events->tail == NULL)
4191 {
4192 /* restoring to a completely empty state, so free everything */
4194 }
4195 else
4196 {
4197 *events = *old_events;
4198 /* free any chunks after the last one we want to keep */
4199 for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4200 {
4201 next_chunk = chunk->next;
4202 pfree(chunk);
4203 }
4204 /* and clean up the tail chunk to be the right length */
4205 events->tail->next = NULL;
4206 events->tail->freeptr = events->tailfree;
4207
4208 /*
4209 * We don't make any effort to remove now-unused shared data records.
4210 * They might still be useful, anyway.
4211 */
4212 }
4213}

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

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

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

5710{
5711 int my_level = GetCurrentTransactionNestLevel();
5712
5713 /* If we haven't already done so, initialize our state. */
5714 if (afterTriggers.state == NULL)
5716
5717 /*
5718 * If in a subtransaction, and we didn't save the current state already,
5719 * save it so it can be restored if the subtransaction aborts.
5720 */
5721 if (my_level > 1 &&
5722 afterTriggers.trans_stack[my_level].state == NULL)
5723 {
5724 afterTriggers.trans_stack[my_level].state =
5726 }
5727
5728 /*
5729 * Handle SET CONSTRAINTS ALL ...
5730 */
5731 if (stmt->constraints == NIL)
5732 {
5733 /*
5734 * Forget any previous SET CONSTRAINTS commands in this transaction.
5735 */
5737
5738 /*
5739 * Set the per-transaction ALL state to known.
5740 */
5743 }
5744 else
5745 {
5746 Relation conrel;
5747 Relation tgrel;
5748 List *conoidlist = NIL;
5749 List *tgoidlist = NIL;
5750 ListCell *lc;
5751
5752 /*
5753 * Handle SET CONSTRAINTS constraint-name [, ...]
5754 *
5755 * First, identify all the named constraints and make a list of their
5756 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5757 * the same name within a schema, the specifications are not
5758 * necessarily unique. Our strategy is to target all matching
5759 * constraints within the first search-path schema that has any
5760 * matches, but disregard matches in schemas beyond the first match.
5761 * (This is a bit odd but it's the historical behavior.)
5762 *
5763 * A constraint in a partitioned table may have corresponding
5764 * constraints in the partitions. Grab those too.
5765 */
5766 conrel = table_open(ConstraintRelationId, AccessShareLock);
5767
5768 foreach(lc, stmt->constraints)
5769 {
5770 RangeVar *constraint = lfirst(lc);
5771 bool found;
5772 List *namespacelist;
5773 ListCell *nslc;
5774
5775 if (constraint->catalogname)
5776 {
5777 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5778 ereport(ERROR,
5779 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5780 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5781 constraint->catalogname, constraint->schemaname,
5782 constraint->relname)));
5783 }
5784
5785 /*
5786 * If we're given the schema name with the constraint, look only
5787 * in that schema. If given a bare constraint name, use the
5788 * search path to find the first matching constraint.
5789 */
5790 if (constraint->schemaname)
5791 {
5792 Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5793 false);
5794
5795 namespacelist = list_make1_oid(namespaceId);
5796 }
5797 else
5798 {
5799 namespacelist = fetch_search_path(true);
5800 }
5801
5802 found = false;
5803 foreach(nslc, namespacelist)
5804 {
5805 Oid namespaceId = lfirst_oid(nslc);
5806 SysScanDesc conscan;
5807 ScanKeyData skey[2];
5808 HeapTuple tup;
5809
5810 ScanKeyInit(&skey[0],
5811 Anum_pg_constraint_conname,
5812 BTEqualStrategyNumber, F_NAMEEQ,
5813 CStringGetDatum(constraint->relname));
5814 ScanKeyInit(&skey[1],
5815 Anum_pg_constraint_connamespace,
5816 BTEqualStrategyNumber, F_OIDEQ,
5817 ObjectIdGetDatum(namespaceId));
5818
5819 conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5820 true, NULL, 2, skey);
5821
5822 while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5823 {
5825
5826 if (con->condeferrable)
5827 conoidlist = lappend_oid(conoidlist, con->oid);
5828 else if (stmt->deferred)
5829 ereport(ERROR,
5830 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5831 errmsg("constraint \"%s\" is not deferrable",
5832 constraint->relname)));
5833 found = true;
5834 }
5835
5836 systable_endscan(conscan);
5837
5838 /*
5839 * Once we've found a matching constraint we do not search
5840 * later parts of the search path.
5841 */
5842 if (found)
5843 break;
5844 }
5845
5846 list_free(namespacelist);
5847
5848 /*
5849 * Not found ?
5850 */
5851 if (!found)
5852 ereport(ERROR,
5853 (errcode(ERRCODE_UNDEFINED_OBJECT),
5854 errmsg("constraint \"%s\" does not exist",
5855 constraint->relname)));
5856 }
5857
5858 /*
5859 * Scan for any possible descendants of the constraints. We append
5860 * whatever we find to the same list that we're scanning; this has the
5861 * effect that we create new scans for those, too, so if there are
5862 * further descendents, we'll also catch them.
5863 */
5864 foreach(lc, conoidlist)
5865 {
5866 Oid parent = lfirst_oid(lc);
5868 SysScanDesc scan;
5869 HeapTuple tuple;
5870
5872 Anum_pg_constraint_conparentid,
5873 BTEqualStrategyNumber, F_OIDEQ,
5874 ObjectIdGetDatum(parent));
5875
5876 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5877
5878 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5879 {
5881
5882 conoidlist = lappend_oid(conoidlist, con->oid);
5883 }
5884
5885 systable_endscan(scan);
5886 }
5887
5889
5890 /*
5891 * Now, locate the trigger(s) implementing each of these constraints,
5892 * and make a list of their OIDs.
5893 */
5894 tgrel = table_open(TriggerRelationId, AccessShareLock);
5895
5896 foreach(lc, conoidlist)
5897 {
5898 Oid conoid = lfirst_oid(lc);
5899 ScanKeyData skey;
5900 SysScanDesc tgscan;
5901 HeapTuple htup;
5902
5903 ScanKeyInit(&skey,
5904 Anum_pg_trigger_tgconstraint,
5905 BTEqualStrategyNumber, F_OIDEQ,
5906 ObjectIdGetDatum(conoid));
5907
5908 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5909 NULL, 1, &skey);
5910
5911 while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5912 {
5913 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5914
5915 /*
5916 * Silently skip triggers that are marked as non-deferrable in
5917 * pg_trigger. This is not an error condition, since a
5918 * deferrable RI constraint may have some non-deferrable
5919 * actions.
5920 */
5921 if (pg_trigger->tgdeferrable)
5922 tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid);
5923 }
5924
5925 systable_endscan(tgscan);
5926 }
5927
5929
5930 /*
5931 * Now we can set the trigger states of individual triggers for this
5932 * xact.
5933 */
5934 foreach(lc, tgoidlist)
5935 {
5936 Oid tgoid = lfirst_oid(lc);
5938 bool found = false;
5939 int i;
5940
5941 for (i = 0; i < state->numstates; i++)
5942 {
5943 if (state->trigstates[i].sct_tgoid == tgoid)
5944 {
5945 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5946 found = true;
5947 break;
5948 }
5949 }
5950 if (!found)
5951 {
5953 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5954 }
5955 }
5956 }
5957
5958 /*
5959 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5960 * checks against that constraint must be made when the SET CONSTRAINTS
5961 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5962 * apply retroactively. We've updated the constraints state, so scan the
5963 * list of previously deferred events to fire any that have now become
5964 * immediate.
5965 *
5966 * Obviously, if this was SET ... DEFERRED then it can't have converted
5967 * any unfired events to immediate, so we need do nothing in that case.
5968 */
5969 if (!stmt->deferred)
5970 {
5972 bool snapshot_set = false;
5973
5974 while (afterTriggerMarkEvents(events, NULL, true))
5975 {
5977
5978 /*
5979 * Make sure a snapshot has been established in case trigger
5980 * functions need one. Note that we avoid setting a snapshot if
5981 * we don't find at least one trigger that has to be fired now.
5982 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
5983 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
5984 * at the start of a transaction it's not possible for any trigger
5985 * events to be queued yet.)
5986 */
5987 if (!snapshot_set)
5988 {
5990 snapshot_set = true;
5991 }
5992
5993 /*
5994 * We can delete fired events if we are at top transaction level,
5995 * but we'd better not if inside a subtransaction, since the
5996 * subtransaction could later get rolled back.
5997 */
5998 if (afterTriggerInvokeEvents(events, firing_id, NULL,
5999 !IsSubTransaction()))
6000 break; /* all fired */
6001 }
6002
6003 if (snapshot_set)
6005 }
6006}
char * get_database_name(Oid dbid)
Definition: dbcommands.c:3188
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:93
#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
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:3385
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4819
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:257
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:355
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:5659
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition: trigger.c:5679
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:5634
bool IsSubTransaction(void)
Definition: xact.c:5044

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

6631{
6632 /*
6633 * Must flush the plan cache when changing replication role; but don't
6634 * flush unnecessarily.
6635 */
6638}
#define newval
void ResetPlanCache(void)
Definition: plancache.c:2332
int SessionReplicationRole
Definition: trigger.c:64

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6509 of file trigger.c.

6510{
6511 bool result;
6513
6514 /* Check state, like AfterTriggerSaveEvent. */
6515 if (afterTriggers.query_depth < 0)
6516 elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6517
6518 /* Be sure we have enough space to record events at this query depth. */
6521
6522 /*
6523 * We keep this state in the AfterTriggersTableData that also holds
6524 * transition tables for the relation + operation. In this way, if we are
6525 * forced to make a new set of transition tables because more tuples get
6526 * entered after we've already fired triggers, we will allow a new set of
6527 * statement triggers to get queued.
6528 */
6529 table = GetAfterTriggersTableData(relid, cmdType);
6530 result = table->before_trig_done;
6531 table->before_trig_done = true;
6532 return result;
6533}
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4831

References AfterTriggerEnlargeQueryState(), afterTriggers, AfterTriggersTableData::before_trig_done, elog, ERROR, GetAfterTriggersTableData(), AfterTriggersData::maxquerydepth, and AfterTriggersData::query_depth.

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

6556{
6559
6560 /*
6561 * We keep this state in the AfterTriggersTableData that also holds
6562 * transition tables for the relation + operation. In this way, if we are
6563 * forced to make a new set of transition tables because more tuples get
6564 * entered after we've already fired triggers, we will allow a new set of
6565 * statement triggers to get queued without canceling the old ones.
6566 */
6567 table = GetAfterTriggersTableData(relid, cmdType);
6568
6569 if (table->after_trig_done)
6570 {
6571 /*
6572 * We want to start scanning from the tail location that existed just
6573 * before we inserted any statement triggers. But the events list
6574 * might've been entirely empty then, in which case scan from the
6575 * current head.
6576 */
6577 AfterTriggerEvent event;
6579
6580 if (table->after_trig_events.tail)
6581 {
6582 chunk = table->after_trig_events.tail;
6584 }
6585 else
6586 {
6587 chunk = qs->events.head;
6588 event = NULL;
6589 }
6590
6591 for_each_chunk_from(chunk)
6592 {
6593 if (event == NULL)
6594 event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6595 for_each_event_from(event, chunk)
6596 {
6597 AfterTriggerShared evtshared = GetTriggerSharedData(event);
6598
6599 /*
6600 * Exit loop when we reach events that aren't AS triggers for
6601 * the target relation.
6602 */
6603 if (evtshared->ats_relid != relid)
6604 goto done;
6605 if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6606 goto done;
6607 if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6608 goto done;
6609 if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6610 goto done;
6611 /* OK, mark it DONE */
6612 event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6613 event->ate_flags |= AFTER_TRIGGER_DONE;
6614 }
6615 /* signal we must reinitialize event ptr for next chunk */
6616 event = NULL;
6617 }
6618 }
6619done:
6620
6621 /* In any case, save current insertion point for next time */
6622 table->after_trig_done = true;
6623 table->after_trig_events = qs->events;
6624}
#define for_each_chunk_from(cptr)
Definition: trigger.c:3741
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3743
#define TRIGGER_FIRED_FOR_STATEMENT(event)
Definition: trigger.h:125
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:131

References AfterTriggersTableData::after_trig_done, AfterTriggersTableData::after_trig_events, 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, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, 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 6660 of file trigger.c.

6661{
6662 if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
6663 return tuple;
6664
6665 for (int i = 0; i < tupdesc->natts; i++)
6666 {
6667 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
6668 {
6669 if (!heap_attisnull(tuple, i + 1, tupdesc))
6670 {
6671 int replCol = i + 1;
6672 Datum replValue = 0;
6673 bool replIsnull = true;
6674
6675 tuple = heap_modify_tuple_by_cols(tuple, tupdesc, 1, &replCol, &replValue, &replIsnull);
6676 }
6677 }
6678 }
6679
6680 return tuple;
6681}
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
uintptr_t Datum
Definition: postgres.h:69
bool has_generated_virtual
Definition: tupdesc.h:47
TupleConstr * constr
Definition: tupdesc.h:135
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:154

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:497
int j
Definition: isn.c:75
char * pstrdup(const char *in)
Definition: mcxt.c:1699
void * palloc(Size size)
Definition: mcxt.c:1317
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] = 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:2622
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3804
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4007
#define InvalidAttrNumber
Definition: attnum.h:23
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define NameStr(name)
Definition: c.h:717
bool IsSystemRelation(Relation relation)
Definition: catalog.c:73
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:419
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:1203
int errhint(const char *fmt,...)
Definition: elog.c:1317
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:641
bool allowSystemTableMods
Definition: globals.c:129
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:1655
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:2011
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2086
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1738
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:3594
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:80
#define copyObject(obj)
Definition: nodes.h:226
#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:794
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:2144
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:2331
#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:327
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:177
static Datum BoolGetDatum(bool X)
Definition: postgres.h:107
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
#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:539
#define RelationGetRelationName(relation)
Definition: rel.h:547
#define RelationGetNamespace(relation)
Definition: rel.h:554
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
Node * whenClause
Definition: parsenodes.h:3103
ItemPointerData t_self
Definition: htup.h:65
Definition: nodes.h:131
const char * p_sourcetext
Definition: parse_node.h:209
List * p_rtable
Definition: parse_node.h:212
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:686
#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
Datum byteain(PG_FUNCTION_ARGS)
Definition: varlena.c:298
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(), 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:1621
#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 2781 of file trigger.c.

2787{
2788 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2789
2790 if ((trigdesc && trigdesc->trig_delete_after_row) ||
2791 (transition_capture && transition_capture->tcs_delete_old_table))
2792 {
2793 TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2794
2795 Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2796 if (fdw_trigtuple == NULL)
2797 GetTupleForTrigger(estate,
2798 NULL,
2799 relinfo,
2800 tupleid,
2802 slot,
2803 NULL,
2804 NULL,
2805 NULL);
2806 else
2807 ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2808
2809 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2811 true, slot, NULL, NIL, NULL,
2812 transition_capture,
2813 is_crosspart_update);
2814 }
2815}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1656
@ LockTupleExclusive
Definition: lockoptions.h:58
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:6111
static bool GetTupleForTrigger(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **epqslot, TM_Result *tmresultp, TM_FailureData *tmfdp)
Definition: trigger.c:3295

References AfterTriggerSaveEvent(), Assert(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), GetTupleForTrigger(), HeapTupleIsValid, ItemPointerIsValid(), LockTupleExclusive, NIL, 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 2541 of file trigger.c.

2544{
2545 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2546
2547 if ((trigdesc && trigdesc->trig_insert_after_row) ||
2548 (transition_capture && transition_capture->tcs_insert_new_table))
2549 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2551 true, NULL, slot,
2552 recheckIndexes, NULL,
2553 transition_capture,
2554 false);
2555}

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

3115{
3116 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3117
3118 if ((trigdesc && trigdesc->trig_update_after_row) ||
3119 (transition_capture &&
3120 (transition_capture->tcs_update_old_table ||
3121 transition_capture->tcs_update_new_table)))
3122 {
3123 /*
3124 * Note: if the UPDATE is converted into a DELETE+INSERT as part of
3125 * update-partition-key operation, then this function is also called
3126 * separately for DELETE and INSERT to capture transition table rows.
3127 * In such case, either old tuple or new tuple can be NULL.
3128 */
3129 TupleTableSlot *oldslot;
3130 ResultRelInfo *tupsrc;
3131
3132 Assert((src_partinfo != NULL && dst_partinfo != NULL) ||
3133 !is_crosspart_update);
3134
3135 tupsrc = src_partinfo ? src_partinfo : relinfo;
3136 oldslot = ExecGetTriggerOldSlot(estate, tupsrc);
3137
3138 if (fdw_trigtuple == NULL && ItemPointerIsValid(tupleid))
3139 GetTupleForTrigger(estate,
3140 NULL,
3141 tupsrc,
3142 tupleid,
3144 oldslot,
3145 NULL,
3146 NULL,
3147 NULL);
3148 else if (fdw_trigtuple != NULL)
3149 ExecForceStoreHeapTuple(fdw_trigtuple, oldslot, false);
3150 else
3151 ExecClearTuple(oldslot);
3152
3153 AfterTriggerSaveEvent(estate, relinfo,
3154 src_partinfo, dst_partinfo,
3156 true,
3157 oldslot, newslot, recheckIndexes,
3158 ExecGetAllUpdatedCols(relinfo, estate),
3159 transition_capture,
3160 is_crosspart_update);
3161 }
3162}
Bitmapset * ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition: execUtils.c:1420

References AfterTriggerSaveEvent(), Assert(), ExecClearTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), GetTupleForTrigger(), ItemPointerIsValid(), LockTupleExclusive, 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 2670 of file trigger.c.

2672{
2673 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2674
2675 if (trigdesc && trigdesc->trig_delete_after_statement)
2676 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2678 false, NULL, NULL, NIL, NULL, transition_capture,
2679 false);
2680}
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 2450 of file trigger.c.

2452{
2453 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2454
2455 if (trigdesc && trigdesc->trig_insert_after_statement)
2456 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2458 false, NULL, NULL, NIL, NULL, transition_capture,
2459 false);
2460}
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 3278 of file trigger.c.

3279{
3280 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3281
3282 if (trigdesc && trigdesc->trig_truncate_after_statement)
3283 AfterTriggerSaveEvent(estate, relinfo,
3284 NULL, NULL,
3286 false, NULL, NULL, NIL, NULL, NULL,
3287 false);
3288}
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 2923 of file trigger.c.

2925{
2926 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2927
2928 /* statement-level triggers operate on the parent table */
2929 Assert(relinfo->ri_RootResultRelInfo == NULL);
2930
2931 if (trigdesc && trigdesc->trig_update_after_statement)
2932 AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2934 false, NULL, NULL, NIL,
2935 ExecGetAllUpdatedCols(relinfo, estate),
2936 transition_capture,
2937 false);
2938}
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:609
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 
)

Definition at line 2690 of file trigger.c.

2697{
2698 TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2699 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2700 bool result = true;
2701 TriggerData LocTriggerData = {0};
2702 HeapTuple trigtuple;
2703 bool should_free = false;
2704 int i;
2705
2706 Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2707 if (fdw_trigtuple == NULL)
2708 {
2709 TupleTableSlot *epqslot_candidate = NULL;
2710
2711 if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2712 LockTupleExclusive, slot, &epqslot_candidate,
2713 tmresult, tmfd))
2714 return false;
2715
2716 /*
2717 * If the tuple was concurrently updated and the caller of this
2718 * function requested for the updated tuple, skip the trigger
2719 * execution.
2720 */
2721 if (epqslot_candidate != NULL && epqslot != NULL)
2722 {
2723 *epqslot = epqslot_candidate;
2724 return false;
2725 }
2726
2727 trigtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2728 }
2729 else
2730 {
2731 trigtuple = fdw_trigtuple;
2732 ExecForceStoreHeapTuple(trigtuple, slot, false);
2733 }
2734
2735 LocTriggerData.type = T_TriggerData;
2736 LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
2739 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2740 for (i = 0; i < trigdesc->numtriggers; i++)
2741 {
2742 HeapTuple newtuple;
2743 Trigger *trigger = &trigdesc->triggers[i];
2744
2745 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2746 TRIGGER_TYPE_ROW,
2747 TRIGGER_TYPE_BEFORE,
2748 TRIGGER_TYPE_DELETE))
2749 continue;
2750 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2751 NULL, slot, NULL))
2752 continue;
2753
2754 LocTriggerData.tg_trigslot = slot;
2755 LocTriggerData.tg_trigtuple = trigtuple;
2756 LocTriggerData.tg_trigger = trigger;
2757 newtuple = ExecCallTriggerFunc(&LocTriggerData,
2758 i,
2759 relinfo->ri_TrigFunctions,
2760 relinfo->ri_TrigInstrument,
2761 GetPerTupleMemoryContext(estate));
2762 if (newtuple == NULL)
2763 {
2764 result = false; /* tell caller to suppress delete */
2765 break;
2766 }
2767 if (newtuple != trigtuple)
2768 heap_freetuple(newtuple);
2769 }
2770 if (should_free)
2771 heap_freetuple(trigtuple);
2772
2773 return result;
2774}
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:642
#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 2463 of file trigger.c.

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

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 
)

Definition at line 2941 of file trigger.c.

2948{
2949 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2950 TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
2951 HeapTuple newtuple = NULL;
2952 HeapTuple trigtuple;
2953 bool should_free_trig = false;
2954 bool should_free_new = false;
2955 TriggerData LocTriggerData = {0};
2956 int i;
2957 Bitmapset *updatedCols;
2958 LockTupleMode lockmode;
2959
2960 /* Determine lock mode to use */
2961 lockmode = ExecUpdateLockMode(estate, relinfo);
2962
2963 Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2964 if (fdw_trigtuple == NULL)
2965 {
2966 TupleTableSlot *epqslot_candidate = NULL;
2967
2968 /* get a copy of the on-disk tuple we are planning to update */
2969 if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2970 lockmode, oldslot, &epqslot_candidate,
2971 tmresult, tmfd))
2972 return false; /* cancel the update action */
2973
2974 /*
2975 * In READ COMMITTED isolation level it's possible that target tuple
2976 * was changed due to concurrent update. In that case we have a raw
2977 * subplan output tuple in epqslot_candidate, and need to form a new
2978 * insertable tuple using ExecGetUpdateNewTuple to replace the one we
2979 * received in newslot. Neither we nor our callers have any further
2980 * interest in the passed-in tuple, so it's okay to overwrite newslot
2981 * with the newer data.
2982 */
2983 if (epqslot_candidate != NULL)
2984 {
2985 TupleTableSlot *epqslot_clean;
2986
2987 epqslot_clean = ExecGetUpdateNewTuple(relinfo, epqslot_candidate,
2988 oldslot);
2989
2990 /*
2991 * Typically, the caller's newslot was also generated by
2992 * ExecGetUpdateNewTuple, so that epqslot_clean will be the same
2993 * slot and copying is not needed. But do the right thing if it
2994 * isn't.
2995 */
2996 if (unlikely(newslot != epqslot_clean))
2997 ExecCopySlot(newslot, epqslot_clean);
2998
2999 /*
3000 * At this point newslot contains a virtual tuple that may
3001 * reference some fields of oldslot's tuple in some disk buffer.
3002 * If that tuple is in a different page than the original target
3003 * tuple, then our only pin on that buffer is oldslot's, and we're
3004 * about to release it. Hence we'd better materialize newslot to
3005 * ensure it doesn't contain references into an unpinned buffer.
3006 * (We'd materialize it below anyway, but too late for safety.)
3007 */
3008 ExecMaterializeSlot(newslot);
3009 }
3010
3011 /*
3012 * Here we convert oldslot to a materialized slot holding trigtuple.
3013 * Neither slot passed to the triggers will hold any buffer pin.
3014 */
3015 trigtuple = ExecFetchSlotHeapTuple(oldslot, true, &should_free_trig);
3016 }
3017 else
3018 {
3019 /* Put the FDW-supplied tuple into oldslot to unify the cases */
3020 ExecForceStoreHeapTuple(fdw_trigtuple, oldslot, false);
3021 trigtuple = fdw_trigtuple;
3022 }
3023
3024 LocTriggerData.type = T_TriggerData;
3025 LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
3028 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3029 updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
3030 LocTriggerData.tg_updatedcols = updatedCols;
3031 for (i = 0; i < trigdesc->numtriggers; i++)
3032 {
3033 Trigger *trigger = &trigdesc->triggers[i];
3034 HeapTuple oldtuple;
3035
3036 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3037 TRIGGER_TYPE_ROW,
3038 TRIGGER_TYPE_BEFORE,
3039 TRIGGER_TYPE_UPDATE))
3040 continue;
3041 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3042 updatedCols, oldslot, newslot))
3043 continue;
3044
3045 if (!newtuple)
3046 newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free_new);
3047
3048 LocTriggerData.tg_trigslot = oldslot;
3049 LocTriggerData.tg_trigtuple = trigtuple;
3050 LocTriggerData.tg_newtuple = oldtuple = newtuple;
3051 LocTriggerData.tg_newslot = newslot;
3052 LocTriggerData.tg_trigger = trigger;
3053 newtuple = ExecCallTriggerFunc(&LocTriggerData,
3054 i,
3055 relinfo->ri_TrigFunctions,
3056 relinfo->ri_TrigInstrument,
3057 GetPerTupleMemoryContext(estate));
3058
3059 if (newtuple == NULL)
3060 {
3061 if (should_free_trig)
3062 heap_freetuple(trigtuple);
3063 if (should_free_new)
3064 heap_freetuple(oldtuple);
3065 return false; /* "do nothing" */
3066 }
3067 else if (newtuple != oldtuple)
3068 {
3070
3071 ExecForceStoreHeapTuple(newtuple, newslot, false);
3072
3073 /*
3074 * If the tuple returned by the trigger / being stored, is the old
3075 * row version, and the heap tuple passed to the trigger was
3076 * allocated locally, materialize the slot. Otherwise we might
3077 * free it while still referenced by the slot.
3078 */
3079 if (should_free_trig && newtuple == trigtuple)
3080 ExecMaterializeSlot(newslot);
3081
3082 if (should_free_new)
3083 heap_freetuple(oldtuple);
3084
3085 /* signal tuple should be re-fetched if used */
3086 newtuple = NULL;
3087 }
3088 }
3089 if (should_free_trig)
3090 heap_freetuple(trigtuple);
3091
3092 return true;
3093}
#define unlikely(x)
Definition: c.h:347
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2491
LockTupleMode
Definition: lockoptions.h:50
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:472

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

2620{
2621 TriggerDesc *trigdesc;
2622 int i;
2623 TriggerData LocTriggerData = {0};
2624
2625 trigdesc = relinfo->ri_TrigDesc;
2626
2627 if (trigdesc == NULL)
2628 return;
2629 if (!trigdesc->trig_delete_before_statement)
2630 return;
2631
2632 /* no-op if we already fired BS triggers in this context */
2634 CMD_DELETE))
2635 return;
2636
2637 LocTriggerData.type = T_TriggerData;
2638 LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
2640 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2641 for (i = 0; i < trigdesc->numtriggers; i++)
2642 {
2643 Trigger *trigger = &trigdesc->triggers[i];
2644 HeapTuple newtuple;
2645
2646 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2647 TRIGGER_TYPE_STATEMENT,
2648 TRIGGER_TYPE_BEFORE,
2649 TRIGGER_TYPE_DELETE))
2650 continue;
2651 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2652 NULL, NULL, NULL))
2653 continue;
2654
2655 LocTriggerData.tg_trigger = trigger;
2656 newtuple = ExecCallTriggerFunc(&LocTriggerData,
2657 i,
2658 relinfo->ri_TrigFunctions,
2659 relinfo->ri_TrigInstrument,
2660 GetPerTupleMemoryContext(estate));
2661
2662 if (newtuple)
2663 ereport(ERROR,
2664 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
2665 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2666 }
2667}
bool trig_delete_before_statement
Definition: reltrigger.h:69
static bool before_stmt_triggers_fired(Oid relid, CmdType cmdType)
Definition: trigger.c:6509

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

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

3232{
3233 TriggerDesc *trigdesc;
3234 int i;
3235 TriggerData LocTriggerData = {0};
3236
3237 trigdesc = relinfo->ri_TrigDesc;
3238
3239 if (trigdesc == NULL)
3240 return;
3241 if (!trigdesc->trig_truncate_before_statement)
3242 return;
3243
3244 LocTriggerData.type = T_TriggerData;
3245 LocTriggerData.tg_event = TRIGGER_EVENT_TRUNCATE |
3247 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3248
3249 for (i = 0; i < trigdesc->numtriggers; i++)
3250 {
3251 Trigger *trigger = &trigdesc->triggers[i];
3252 HeapTuple newtuple;
3253
3254 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3255 TRIGGER_TYPE_STATEMENT,
3256 TRIGGER_TYPE_BEFORE,
3257 TRIGGER_TYPE_TRUNCATE))
3258 continue;
3259 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3260 NULL, NULL, NULL))
3261 continue;
3262
3263 LocTriggerData.tg_trigger = trigger;
3264 newtuple = ExecCallTriggerFunc(&LocTriggerData,
3265 i,
3266 relinfo->ri_TrigFunctions,
3267 relinfo->ri_TrigInstrument,
3268 GetPerTupleMemoryContext(estate));
3269
3270 if (newtuple)
3271 ereport(ERROR,
3272 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3273 errmsg("BEFORE STATEMENT trigger cannot return a value")));
3274 }
3275}
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 2865 of file trigger.c.

2866{
2867 TriggerDesc *trigdesc;
2868 int i;
2869 TriggerData LocTriggerData = {0};
2870 Bitmapset *updatedCols;
2871
2872 trigdesc = relinfo->ri_TrigDesc;
2873
2874 if (trigdesc == NULL)
2875 return;
2876 if (!trigdesc->trig_update_before_statement)
2877 return;
2878
2879 /* no-op if we already fired BS triggers in this context */
2881 CMD_UPDATE))
2882 return;
2883
2884 /* statement-level triggers operate on the parent table */
2885 Assert(relinfo->ri_RootResultRelInfo == NULL);
2886
2887 updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
2888
2889 LocTriggerData.type = T_TriggerData;
2890 LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
2892 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2893 LocTriggerData.tg_updatedcols = updatedCols;
2894 for (i = 0; i < trigdesc->numtriggers; i++)
2895 {
2896 Trigger *trigger = &trigdesc->triggers[i];
2897 HeapTuple newtuple;
2898
2899 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2900 TRIGGER_TYPE_STATEMENT,
2901 TRIGGER_TYPE_BEFORE,
2902 TRIGGER_TYPE_UPDATE))
2903 continue;
2904 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2905 updatedCols, NULL, NULL))
2906 continue;
2907
2908 LocTriggerData.tg_trigger = trigger;
2909 newtuple = ExecCallTriggerFunc(&LocTriggerData,
2910 i,
2911 relinfo->ri_TrigFunctions,
2912 relinfo->ri_TrigInstrument,
2913 GetPerTupleMemoryContext(estate));
2914
2915 if (newtuple)
2916 ereport(ERROR,
2917 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
2918 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2919 }
2920}
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 2307 of file trigger.c.

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

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

2820{
2821 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2822 TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2823 TriggerData LocTriggerData = {0};
2824 int i;
2825
2826 LocTriggerData.type = T_TriggerData;
2827 LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
2830 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2831
2832 ExecForceStoreHeapTuple(trigtuple, slot, false);
2833
2834 for (i = 0; i < trigdesc->numtriggers; i++)
2835 {
2836 HeapTuple rettuple;
2837 Trigger *trigger = &trigdesc->triggers[i];
2838
2839 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2840 TRIGGER_TYPE_ROW,
2841 TRIGGER_TYPE_INSTEAD,
2842 TRIGGER_TYPE_DELETE))
2843 continue;
2844 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2845 NULL, slot, NULL))
2846 continue;
2847
2848 LocTriggerData.tg_trigslot = slot;
2849 LocTriggerData.tg_trigtuple = trigtuple;
2850 LocTriggerData.tg_trigger = trigger;
2851 rettuple = ExecCallTriggerFunc(&LocTriggerData,
2852 i,
2853 relinfo->ri_TrigFunctions,
2854 relinfo->ri_TrigInstrument,
2855 GetPerTupleMemoryContext(estate));
2856 if (rettuple == NULL)
2857 return false; /* Delete was suppressed */
2858 if (rettuple != trigtuple)
2859 heap_freetuple(rettuple);
2860 }
2861 return true;
2862}
#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 2558 of file trigger.c.

2560{
2561 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2562 HeapTuple newtuple = NULL;
2563 bool should_free;
2564 TriggerData LocTriggerData = {0};
2565 int i;
2566
2567 LocTriggerData.type = T_TriggerData;
2568 LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
2571 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2572 for (i = 0; i < trigdesc->numtriggers; i++)
2573 {
2574 Trigger *trigger = &trigdesc->triggers[i];
2575 HeapTuple oldtuple;
2576
2577 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2578 TRIGGER_TYPE_ROW,
2579 TRIGGER_TYPE_INSTEAD,
2580 TRIGGER_TYPE_INSERT))
2581 continue;
2582 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2583 NULL, NULL, slot))
2584 continue;
2585
2586 if (!newtuple)
2587 newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2588
2589 LocTriggerData.tg_trigslot = slot;
2590 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2591 LocTriggerData.tg_trigger = trigger;
2592 newtuple = ExecCallTriggerFunc(&LocTriggerData,
2593 i,
2594 relinfo->ri_TrigFunctions,
2595 relinfo->ri_TrigInstrument,
2596 GetPerTupleMemoryContext(estate));
2597 if (newtuple == NULL)
2598 {
2599 if (should_free)
2600 heap_freetuple(oldtuple);
2601 return false; /* "do nothing" */
2602 }
2603 else if (newtuple != oldtuple)
2604 {
2605 ExecForceStoreHeapTuple(newtuple, slot, false);
2606
2607 if (should_free)
2608 heap_freetuple(oldtuple);
2609
2610 /* signal tuple should be re-fetched if used */
2611 newtuple = NULL;
2612 }
2613 }
2614
2615 return true;
2616}

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

3167{
3168 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3169 TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
3170 HeapTuple newtuple = NULL;
3171 bool should_free;
3172 TriggerData LocTriggerData = {0};
3173 int i;
3174
3175 LocTriggerData.type = T_TriggerData;
3176 LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
3179 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3180
3181 ExecForceStoreHeapTuple(trigtuple, oldslot, false);
3182
3183 for (i = 0; i < trigdesc->numtriggers; i++)
3184 {
3185 Trigger *trigger = &trigdesc->triggers[i];
3186 HeapTuple oldtuple;
3187
3188 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3189 TRIGGER_TYPE_ROW,
3190 TRIGGER_TYPE_INSTEAD,
3191 TRIGGER_TYPE_UPDATE))
3192 continue;
3193 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3194 NULL, oldslot, newslot))
3195 continue;
3196
3197 if (!newtuple)
3198 newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free);
3199
3200 LocTriggerData.tg_trigslot = oldslot;
3201 LocTriggerData.tg_trigtuple = trigtuple;
3202 LocTriggerData.tg_newslot = newslot;
3203 LocTriggerData.tg_newtuple = oldtuple = newtuple;
3204
3205 LocTriggerData.tg_trigger = trigger;
3206 newtuple = ExecCallTriggerFunc(&LocTriggerData,
3207 i,
3208 relinfo->ri_TrigFunctions,
3209 relinfo->ri_TrigInstrument,
3210 GetPerTupleMemoryContext(estate));
3211 if (newtuple == NULL)
3212 {
3213 return false; /* "do nothing" */
3214 }
3215 else if (newtuple != oldtuple)
3216 {
3217 ExecForceStoreHeapTuple(newtuple, newslot, false);
3218
3219 if (should_free)
3220 heap_freetuple(oldtuple);
3221
3222 /* signal tuple should be re-fetched if used */
3223 newtuple = NULL;
3224 }
3225 }
3226
3227 return true;
3228}

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->tgoldtable != NULL || trigger->tgnewtable != NULL)
2288 return trigger->tgname;
2289 }
2290 }
2291
2292 return NULL;
2293}

References i, TriggerDesc::numtriggers, Trigger::tgname, Trigger::tgnewtable, Trigger::tgoldtable, 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 4868 of file trigger.c.

4870{
4871 /* Create it if not already done. */
4872 if (!table->storeslot)
4873 {
4874 MemoryContext oldcxt;
4875
4876 /*
4877 * We need this slot only until AfterTriggerEndQuery, but making it
4878 * last till end-of-subxact is good enough. It'll be freed by
4879 * AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4880 * a different lifespan, so we'd better make a copy of that.
4881 */
4883 tupdesc = CreateTupleDescCopy(tupdesc);
4885 MemoryContextSwitchTo(oldcxt);
4886 }
4887
4888 return table->storeslot;
4889}
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
MemoryContext CurTransactionContext
Definition: mcxt.c:155
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:234

References CreateTupleDescCopy(), CurTransactionContext, MakeSingleTupleTableSlot(), MemoryContextSwitchTo(), AfterTriggersTableData::storeslot, and TTSOpsVirtual.

Referenced by TransitionTableAddTuple().

◆ GetAfterTriggersTableData()

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

Definition at line 4831 of file trigger.c.

4832{
4835 MemoryContext oldcxt;
4836 ListCell *lc;
4837
4838 /* Caller should have ensured query_depth is OK. */
4842
4843 foreach(lc, qs->tables)
4844 {
4845 table = (AfterTriggersTableData *) lfirst(lc);
4846 if (table->relid == relid && table->cmdType == cmdType &&
4847 !table->closed)
4848 return table;
4849 }
4850
4852
4854 table->relid = relid;
4855 table->cmdType = cmdType;
4856 qs->tables = lappend(qs->tables, table);
4857
4858 MemoryContextSwitchTo(oldcxt);
4859
4860 return table;
4861}
List * lappend(List *list, void *datum)
Definition: list.c:339
void * palloc0(Size size)
Definition: mcxt.c:1347

References afterTriggers, Assert(), AfterTriggersTableData::closed, AfterTriggersTableData::cmdType, CurTransactionContext, lappend(), lfirst, AfterTriggersData::maxquerydepth, MemoryContextSwitchTo(), palloc0(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersTableData::relid, 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 5496 of file trigger.c.

5500{
5501 Tuplestorestate *tuplestore = NULL;
5502 bool delete_old_table = transition_capture->tcs_delete_old_table;
5503 bool update_old_table = transition_capture->tcs_update_old_table;
5504 bool update_new_table = transition_capture->tcs_update_new_table;
5505 bool insert_new_table = transition_capture->tcs_insert_new_table;
5506
5507 /*
5508 * For INSERT events NEW should be non-NULL, for DELETE events OLD should
5509 * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
5510 * non-NULL. But for UPDATE events fired for capturing transition tuples
5511 * during UPDATE partition-key row movement, OLD is NULL when the event is
5512 * for a row being inserted, whereas NEW is NULL when the event is for a
5513 * row being deleted.
5514 */
5515 Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
5516 TupIsNull(oldslot)));
5517 Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
5518 TupIsNull(newslot)));
5519
5520 if (!TupIsNull(oldslot))
5521 {
5522 Assert(TupIsNull(newslot));
5523 if (event == TRIGGER_EVENT_DELETE && delete_old_table)
5524 tuplestore = transition_capture->tcs_private->old_del_tuplestore;
5525 else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
5526 tuplestore = transition_capture->tcs_private->old_upd_tuplestore;
5527 }
5528 else if (!TupIsNull(newslot))
5529 {
5530 Assert(TupIsNull(oldslot));
5531 if (event == TRIGGER_EVENT_INSERT && insert_new_table)
5532 tuplestore = transition_capture->tcs_private->new_ins_tuplestore;
5533 else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
5534 tuplestore = transition_capture->tcs_private->new_upd_tuplestore;
5535 }
5536
5537 return tuplestore;
5538}

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

3931{
3932 Tuplestorestate *ret;
3933
3935 if (ret == NULL)
3936 {
3937 MemoryContext oldcxt;
3938 ResourceOwner saveResourceOwner;
3939
3940 /*
3941 * Make the tuplestore valid until end of subtransaction. We really
3942 * only need it until AfterTriggerEndQuery().
3943 */
3945 saveResourceOwner = CurrentResourceOwner;
3947
3948 ret = tuplestore_begin_heap(false, false, work_mem);
3949
3950 CurrentResourceOwner = saveResourceOwner;
3951 MemoryContextSwitchTo(oldcxt);
3952
3954 }
3955
3956 return ret;
3957}
int work_mem
Definition: globals.c:130
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,
TupleTableSlot **  epqslot,
TM_Result tmresultp,
TM_FailureData tmfdp 
)
static

Definition at line 3295 of file trigger.c.

3304{
3305 Relation relation = relinfo->ri_RelationDesc;
3306
3307 if (epqslot != NULL)
3308 {
3310 TM_FailureData tmfd;
3311 int lockflags = 0;
3312
3313 *epqslot = NULL;
3314
3315 /* caller must pass an epqstate if EvalPlanQual is possible */
3316 Assert(epqstate != NULL);
3317
3318 /*
3319 * lock tuple for update
3320 */
3323 test = table_tuple_lock(relation, tid, estate->es_snapshot, oldslot,
3324 estate->es_output_cid,
3325 lockmode, LockWaitBlock,
3326 lockflags,
3327 &tmfd);
3328
3329 /* Let the caller know about the status of this operation */
3330 if (tmresultp)
3331 *tmresultp = test;
3332 if (tmfdp)
3333 *tmfdp = tmfd;
3334
3335 switch (test)
3336 {
3337 case TM_SelfModified:
3338
3339 /*
3340 * The target tuple was already updated or deleted by the
3341 * current command, or by a later command in the current
3342 * transaction. We ignore the tuple in the former case, and
3343 * throw error in the latter case, for the same reasons
3344 * enumerated in ExecUpdate and ExecDelete in
3345 * nodeModifyTable.c.
3346 */
3347 if (tmfd.cmax != estate->es_output_cid)
3348 ereport(ERROR,
3349 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3350 errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
3351 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3352
3353 /* treat it as deleted; do not process */
3354 return false;
3355
3356 case TM_Ok:
3357 if (tmfd.traversed)
3358 {
3359 /*
3360 * Recheck the tuple using EPQ. For MERGE, we leave this
3361 * to the caller (it must do additional rechecking, and
3362 * might end up executing a different action entirely).
3363 */
3364 if (estate->es_plannedstmt->commandType == CMD_MERGE)
3365 {
3366 if (tmresultp)
3367 *tmresultp = TM_Updated;
3368 return false;
3369 }
3370
3371 *epqslot = EvalPlanQual(epqstate,
3372 relation,
3373 relinfo->ri_RangeTableIndex,
3374 oldslot);
3375
3376 /*
3377 * If PlanQual failed for updated tuple - we must not
3378 * process this tuple!
3379 */
3380 if (TupIsNull(*epqslot))
3381 {
3382 *epqslot = NULL;
3383 return false;
3384 }
3385 }
3386 break;
3387
3388 case TM_Updated:
3390 ereport(ERROR,
3392 errmsg("could not serialize access due to concurrent update")));
3393 elog(ERROR, "unexpected table_tuple_lock status: %u", test);
3394 break;
3395
3396 case TM_Deleted:
3398 ereport(ERROR,
3400 errmsg("could not serialize access due to concurrent delete")));
3401 /* tuple was deleted */
3402 return false;
3403
3404 case TM_Invisible:
3405 elog(ERROR, "attempted to lock invisible tuple");
3406 break;
3407
3408 default:
3409 elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
3410 return false; /* keep compiler quiet */
3411 }
3412 }
3413 else
3414 {
3415 /*
3416 * We expect the tuple to be present, thus very simple error handling
3417 * suffices.
3418 */
3419 if (!table_tuple_fetch_row_version(relation, tid, SnapshotAny,
3420 oldslot))
3421 elog(ERROR, "failed to fetch tuple for trigger");
3422 }
3423
3424 return true;
3425}
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition: execMain.c:2610
@ LockWaitBlock
Definition: lockoptions.h:39
@ CMD_MERGE
Definition: nodes.h:271
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition: pgbench.c:77
static void test(void)
PlannedStmt * es_plannedstmt
Definition: execnodes.h:660
CommandId es_output_cid
Definition: execnodes.h:674
Snapshot es_snapshot
Definition: execnodes.h:651
CmdType commandType
Definition: plannodes.h:53
Index ri_RangeTableIndex
Definition: execnodes.h:472
bool traversed
Definition: tableam.h:153
CommandId cmax
Definition: tableam.h:152
TM_Result
Definition: tableam.h:79
@ TM_Ok
Definition: tableam.h:84
@ TM_Deleted
Definition: tableam.h:99
@ TM_Updated
Definition: tableam.h:96
@ TM_SelfModified
Definition: tableam.h:90
@ TM_Invisible
Definition: tableam.h:87
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:1550
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:268
#define IsolationUsesXactSnapshot()
Definition: xact.h:51

References Assert(), TM_FailureData::cmax, CMD_MERGE, PlannedStmt::commandType, elog, ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errhint(), errmsg(), ERROR, EState::es_output_cid, EState::es_plannedstmt, 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 4917 of file trigger.c.

4918{
4920 bool need_old_upd,
4921 need_new_upd,
4922 need_old_del,
4923 need_new_ins;
4925 MemoryContext oldcxt;
4926 ResourceOwner saveResourceOwner;
4927
4928 if (trigdesc == NULL)
4929 return NULL;
4930
4931 /* Detect which table(s) we need. */
4932 switch (cmdType)
4933 {
4934 case CMD_INSERT:
4935 need_old_upd = need_old_del = need_new_upd = false;
4936 need_new_ins = trigdesc->trig_insert_new_table;
4937 break;
4938 case CMD_UPDATE:
4939 need_old_upd = trigdesc->trig_update_old_table;
4940 need_new_upd = trigdesc->trig_update_new_table;
4941 need_old_del = need_new_ins = false;
4942 break;
4943 case CMD_DELETE:
4944 need_old_del = trigdesc->trig_delete_old_table;
4945 need_old_upd = need_new_upd = need_new_ins = false;
4946 break;
4947 case CMD_MERGE:
4948 need_old_upd = trigdesc->trig_update_old_table;
4949 need_new_upd = trigdesc->trig_update_new_table;
4950 need_old_del = trigdesc->trig_delete_old_table;
4951 need_new_ins = trigdesc->trig_insert_new_table;
4952 break;
4953 default:
4954 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
4955 /* keep compiler quiet */
4956 need_old_upd = need_new_upd = need_old_del = need_new_ins = false;
4957 break;
4958 }
4959 if (!need_old_upd && !need_new_upd && !need_new_ins && !need_old_del)
4960 return NULL;
4961
4962 /* Check state, like AfterTriggerSaveEvent. */
4963 if (afterTriggers.query_depth < 0)
4964 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
4965
4966 /* Be sure we have enough space to record events at this query depth. */
4969
4970 /*
4971 * Find or create an AfterTriggersTableData struct to hold the
4972 * tuplestore(s). If there's a matching struct but it's marked closed,
4973 * ignore it; we need a newer one.
4974 *
4975 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
4976 * allocated in the current (sub)transaction's CurTransactionContext, and
4977 * the tuplestores are managed by the (sub)transaction's resource owner.
4978 * This is sufficient lifespan because we do not allow triggers using
4979 * transition tables to be deferrable; they will be fired during
4980 * AfterTriggerEndQuery, after which it's okay to delete the data.
4981 */
4982 table = GetAfterTriggersTableData(relid, cmdType);
4983
4984 /* Now create required tuplestore(s), if we don't have them already. */
4986 saveResourceOwner = CurrentResourceOwner;
4988
4989 if (need_old_upd && table->old_upd_tuplestore == NULL)
4990 table->old_upd_tuplestore = tuplestore_begin_heap(false, false, work_mem);
4991 if (need_new_upd && table->new_upd_tuplestore == NULL)
4992 table->new_upd_tuplestore = tuplestore_begin_heap(false, false, work_mem);
4993 if (need_old_del && table->old_del_tuplestore == NULL)
4994 table->old_del_tuplestore = tuplestore_begin_heap(false, false, work_mem);
4995 if (need_new_ins && table->new_ins_tuplestore == NULL)
4996 table->new_ins_tuplestore = tuplestore_begin_heap(false, false, work_mem);
4997
4998 CurrentResourceOwner = saveResourceOwner;
4999 MemoryContextSwitchTo(oldcxt);
5000
5001 /* Now build the TransitionCaptureState struct, in caller's context */
5003 state->tcs_delete_old_table = need_old_del;
5004 state->tcs_update_old_table = need_old_upd;
5005 state->tcs_update_new_table = need_new_upd;
5006 state->tcs_insert_new_table = need_new_ins;
5007 state->tcs_private = table;
5008
5009 return state;
5010}
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(), AfterTriggersTableData::new_ins_tuplestore, AfterTriggersTableData::new_upd_tuplestore, AfterTriggersTableData::old_del_tuplestore, AfterTriggersTableData::old_upd_tuplestore, palloc0(), AfterTriggersData::query_depth, TriggerDesc::trig_delete_old_table, TriggerDesc::trig_insert_new_table, TriggerDesc::trig_update_new_table, TriggerDesc::trig_update_old_table, tuplestore_begin_heap(), and work_mem.

Referenced by CopyFrom(), and ExecSetupTransitionCaptureState().

◆ pg_trigger_depth()

Datum pg_trigger_depth ( PG_FUNCTION_ARGS  )

Definition at line 6644 of file trigger.c.

6645{
6647}
#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:4058
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:85
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221

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:860
long val
Definition: informix.c:689
MemoryContext CacheMemoryContext
Definition: mcxt.c:152
Datum nameout(PG_FUNCTION_ARGS)
Definition: name.c:71
static char * DatumGetCString(Datum X)
Definition: postgres.h:340
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:378
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:658
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
#define VARDATA_ANY(PTR)
Definition: varatt.h:324

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:441
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 5679 of file trigger.c.

5681{
5682 if (state->numstates >= state->numalloc)
5683 {
5684 int newalloc = state->numalloc * 2;
5685
5686 newalloc = Max(newalloc, 8); /* in case original has size 0 */
5689 offsetof(SetConstraintStateData, trigstates) +
5690 newalloc * sizeof(SetConstraintTriggerData));
5691 state->numalloc = newalloc;
5692 Assert(state->numstates < state->numalloc);
5693 }
5694
5695 state->trigstates[state->numstates].sct_tgoid = tgoid;
5696 state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5697 state->numstates++;
5698
5699 return state;
5700}
struct SetConstraintTriggerData SetConstraintTriggerData
SetConstraintStateData * SetConstraintState
Definition: trigger.c:3583

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCopy()

static SetConstraintState SetConstraintStateCopy ( SetConstraintState  origstate)
static

Definition at line 5659 of file trigger.c.

5660{
5662
5664
5665 state->all_isset = origstate->all_isset;
5666 state->all_isdeferred = origstate->all_isdeferred;
5667 state->numstates = origstate->numstates;
5668 memcpy(state->trigstates, origstate->trigstates,
5669 origstate->numstates * sizeof(SetConstraintTriggerData));
5670
5671 return state;
5672}
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3580

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

5635{
5637
5638 /* Behave sanely with numalloc == 0 */
5639 if (numalloc <= 0)
5640 numalloc = 1;
5641
5642 /*
5643 * We assume that zeroing will correctly initialize the state values.
5644 */
5647 offsetof(SetConstraintStateData, trigstates) +
5648 numalloc * sizeof(SetConstraintTriggerData));
5649
5650 state->numalloc = numalloc;
5651
5652 return state;
5653}
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1215

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

5553{
5554 TupleConversionMap *map;
5555
5556 /*
5557 * Nothing needs to be done if we don't have a tuplestore.
5558 */
5559 if (tuplestore == NULL)
5560 return;
5561
5562 if (original_insert_tuple)
5563 tuplestore_puttupleslot(tuplestore, original_insert_tuple);
5564 else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5565 {
5566 AfterTriggersTableData *table = transition_capture->tcs_private;
5567 TupleTableSlot *storeslot;
5568
5569 storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5570 execute_attr_map_slot(map->attrMap, slot, storeslot);
5571 tuplestore_puttupleslot(tuplestore, storeslot);
5572 }
5573 else
5574 tuplestore_puttupleslot(tuplestore, slot);
5575}
TupleDesc outdesc
Definition: tupconvert.h:27
static TupleTableSlot * GetAfterTriggersStoreSlot(AfterTriggersTableData *table, TupleDesc tupdesc)
Definition: trigger.c:4868

References TupleConversionMap::attrMap, ExecGetChildToRootMap(), execute_attr_map_slot(), GetAfterTriggersStoreSlot(), TupleConversionMap::outdesc, 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 3431 of file trigger.c.

3435{
3436 /* Check replication-role-dependent enable state */
3438 {
3439 if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN ||
3440 trigger->tgenabled == TRIGGER_DISABLED)
3441 return false;
3442 }
3443 else /* ORIGIN or LOCAL role */
3444 {
3445 if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA ||
3446 trigger->tgenabled == TRIGGER_DISABLED)
3447 return false;
3448 }
3449
3450 /*
3451 * Check for column-specific trigger (only possible for UPDATE, and in
3452 * fact we *must* ignore tgattr for other event types)
3453 */
3454 if (trigger->tgnattr > 0 && TRIGGER_FIRED_BY_UPDATE(event))
3455 {
3456 int i;
3457 bool modified;
3458
3459 modified = false;
3460 for (i = 0; i < trigger->tgnattr; i++)
3461 {
3463 modifiedCols))
3464 {
3465 modified = true;
3466 break;
3467 }
3468 }
3469 if (!modified)
3470 return false;
3471 }
3472
3473 /* Check for WHEN clause */
3474 if (trigger->tgqual)
3475 {
3476 ExprState **predicate;
3477 ExprContext *econtext;
3478 MemoryContext oldContext;
3479 int i;
3480
3481 Assert(estate != NULL);
3482
3483 /*
3484 * trigger is an element of relinfo->ri_TrigDesc->triggers[]; find the
3485 * matching element of relinfo->ri_TrigWhenExprs[]
3486 */
3487 i = trigger - relinfo->ri_TrigDesc->triggers;
3488 predicate = &relinfo->ri_TrigWhenExprs[i];
3489
3490 /*
3491 * If first time through for this WHEN expression, build expression
3492 * nodetrees for it. Keep them in the per-query memory context so
3493 * they'll survive throughout the query.
3494 */
3495 if (*predicate == NULL)
3496 {
3497 Node *tgqual;
3498
3499 oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
3500 tgqual = stringToNode(trigger->tgqual);
3503 /* Change references to OLD and NEW to INNER_VAR and OUTER_VAR */
3506 /* ExecPrepareQual wants implicit-AND form */
3507 tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
3508 *predicate = ExecPrepareQual((List *) tgqual, estate);
3509 MemoryContextSwitchTo(oldContext);
3510 }
3511
3512 /*
3513 * We will use the EState's per-tuple context for evaluating WHEN
3514 * expressions (creating it if it's not already there).
3515 */
3516 econtext = GetPerTupleExprContext(estate);
3517
3518 /*
3519 * Finally evaluate the expression, making the old and/or new tuples
3520 * available as INNER_VAR/OUTER_VAR respectively.
3521 */
3522 econtext->ecxt_innertuple = oldslot;
3523 econtext->ecxt_outertuple = newslot;
3524 if (!ExecQual(*predicate, econtext))
3525 return false;
3526 }
3527
3528 return true;
3529}
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:637
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:500
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:793
MemoryContext es_query_cxt
Definition: execnodes.h:702
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:270
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:272
ExprState ** ri_TrigWhenExprs
Definition: execnodes.h:516
#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 67 of file trigger.c.

Referenced by ExecCallTriggerFunc(), and pg_trigger_depth().

◆ SessionReplicationRole

int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN