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/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)
 
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 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 3625 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3626 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3627 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3620 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3624 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3623 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3621 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3619 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3628 of file trigger.c.

◆ CHUNK_DATA_START

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

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

◆ for_each_chunk_from

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

Definition at line 3729 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:3707
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3679
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3641

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

◆ GetTriggerSharedData

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

Definition at line 3688 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:3628
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3625
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3626
#define AFTER_TRIGGER_CP_UPDATE
Definition: trigger.c:3627

Definition at line 3679 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3641 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3629 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3641 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3641 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3641 of file trigger.c.

◆ SetConstraintState

Definition at line 3572 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3551 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3617 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 4031 of file trigger.c.

4033 {
4034  Size eventsize = SizeofTriggerEvent(event);
4035  Size needed = eventsize + sizeof(AfterTriggerSharedData);
4037  AfterTriggerShared newshared;
4038  AfterTriggerEvent newevent;
4039 
4040  /*
4041  * If empty list or not enough room in the tail chunk, make a new chunk.
4042  * We assume here that a new shared record will always be needed.
4043  */
4044  chunk = events->tail;
4045  if (chunk == NULL ||
4046  chunk->endfree - chunk->freeptr < needed)
4047  {
4048  Size chunksize;
4049 
4050  /* Create event context if we didn't already */
4051  if (afterTriggers.event_cxt == NULL)
4054  "AfterTriggerEvents",
4056 
4057  /*
4058  * Chunk size starts at 1KB and is allowed to increase up to 1MB.
4059  * These numbers are fairly arbitrary, though there is a hard limit at
4060  * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
4061  * shared records using the available space in ate_flags. Another
4062  * constraint is that if the chunk size gets too huge, the search loop
4063  * below would get slow given a (not too common) usage pattern with
4064  * many distinct event types in a chunk. Therefore, we double the
4065  * preceding chunk size only if there weren't too many shared records
4066  * in the preceding chunk; otherwise we halve it. This gives us some
4067  * ability to adapt to the actual usage pattern of the current query
4068  * while still having large chunk sizes in typical usage. All chunk
4069  * sizes used should be MAXALIGN multiples, to ensure that the shared
4070  * records will be aligned safely.
4071  */
4072 #define MIN_CHUNK_SIZE 1024
4073 #define MAX_CHUNK_SIZE (1024*1024)
4074 
4075 #if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
4076 #error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
4077 #endif
4078 
4079  if (chunk == NULL)
4080  chunksize = MIN_CHUNK_SIZE;
4081  else
4082  {
4083  /* preceding chunk size... */
4084  chunksize = chunk->endptr - (char *) chunk;
4085  /* check number of shared records in preceding chunk */
4086  if ((chunk->endptr - chunk->endfree) <=
4087  (100 * sizeof(AfterTriggerSharedData)))
4088  chunksize *= 2; /* okay, double it */
4089  else
4090  chunksize /= 2; /* too many shared records */
4091  chunksize = Min(chunksize, MAX_CHUNK_SIZE);
4092  }
4094  chunk->next = NULL;
4095  chunk->freeptr = CHUNK_DATA_START(chunk);
4096  chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
4097  Assert(chunk->endfree - chunk->freeptr >= needed);
4098 
4099  if (events->head == NULL)
4100  events->head = chunk;
4101  else
4102  events->tail->next = chunk;
4103  events->tail = chunk;
4104  /* events->tailfree is now out of sync, but we'll fix it below */
4105  }
4106 
4107  /*
4108  * Try to locate a matching shared-data record already in the chunk. If
4109  * none, make a new one.
4110  */
4111  for (newshared = ((AfterTriggerShared) chunk->endptr) - 1;
4112  (char *) newshared >= chunk->endfree;
4113  newshared--)
4114  {
4115  if (newshared->ats_tgoid == evtshared->ats_tgoid &&
4116  newshared->ats_relid == evtshared->ats_relid &&
4117  newshared->ats_event == evtshared->ats_event &&
4118  newshared->ats_table == evtshared->ats_table &&
4119  newshared->ats_firing_id == 0)
4120  break;
4121  }
4122  if ((char *) newshared < chunk->endfree)
4123  {
4124  *newshared = *evtshared;
4125  newshared->ats_firing_id = 0; /* just to be sure */
4126  chunk->endfree = (char *) newshared;
4127  }
4128 
4129  /* Insert the data */
4130  newevent = (AfterTriggerEvent) chunk->freeptr;
4131  memcpy(newevent, event, eventsize);
4132  /* ... and link the new event to its shared record */
4133  newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
4134  newevent->ate_flags |= (char *) newshared - (char *) newevent;
4135 
4136  chunk->freeptr += eventsize;
4137  events->tailfree = chunk->freeptr;
4138 }
#define Min(x, y)
Definition: c.h:1004
#define Assert(condition)
Definition: c.h:858
size_t Size
Definition: c.h:605
uint64 chunk
MemoryContext TopTransactionContext
Definition: mcxt.c:154
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1180
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
struct AfterTriggerEventChunk * next
Definition: trigger.c:3700
TriggerFlags ate_flags
Definition: trigger.c:3645
AfterTriggerEventChunk * head
Definition: trigger.c:3712
AfterTriggerEventChunk * tail
Definition: trigger.c:3713
TriggerEvent ats_event
Definition: trigger.c:3633
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3637
CommandId ats_firing_id
Definition: trigger.c:3636
MemoryContext event_cxt
Definition: trigger.c:3821
struct AfterTriggerSharedData AfterTriggerSharedData
static AfterTriggersData afterTriggers
Definition: trigger.c:3878
#define AFTER_TRIGGER_OFFSET
Definition: trigger.c:3619
#define MAX_CHUNK_SIZE
#define MIN_CHUNK_SIZE

References AFTER_TRIGGER_OFFSET, afterTriggers, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_relid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, chunk, 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 5018 of file trigger.c.

5019 {
5020  /* Increase the query stack depth */
5022 }

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5301 of file trigger.c.

5302 {
5303  int my_level = GetCurrentTransactionNestLevel();
5304 
5305  /*
5306  * Allocate more space in the trans_stack if needed. (Note: because the
5307  * minimum nest level of a subtransaction is 2, we waste the first couple
5308  * entries of the array; not worth the notational effort to avoid it.)
5309  */
5310  while (my_level >= afterTriggers.maxtransdepth)
5311  {
5312  if (afterTriggers.maxtransdepth == 0)
5313  {
5314  /* Arbitrarily initialize for max of 8 subtransaction levels */
5317  8 * sizeof(AfterTriggersTransData));
5319  }
5320  else
5321  {
5322  /* repalloc will keep the stack in the same context */
5323  int new_alloc = afterTriggers.maxtransdepth * 2;
5324 
5327  new_alloc * sizeof(AfterTriggersTransData));
5328  afterTriggers.maxtransdepth = new_alloc;
5329  }
5330  }
5331 
5332  /*
5333  * Push the current information into the stack. The SET CONSTRAINTS state
5334  * is not saved until/unless changed. Likewise, we don't make a
5335  * per-subtransaction event context until needed.
5336  */
5337  afterTriggers.trans_stack[my_level].state = NULL;
5341 }
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1540
CommandId firing_counter
Definition: trigger.c:3818
AfterTriggersTransData * trans_stack
Definition: trigger.c:3829
AfterTriggerEventList events
Definition: trigger.c:3820
AfterTriggerEventList events
Definition: trigger.c:3844
SetConstraintState state
Definition: trigger.c:3843
CommandId firing_counter
Definition: trigger.c:3846
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:926

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

4987 {
4988  /*
4989  * Initialize after-trigger state structure to empty
4990  */
4991  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4993 
4994  /*
4995  * Verify that there is no leftover state remaining. If these assertions
4996  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4997  * up properly.
4998  */
4999  Assert(afterTriggers.state == NULL);
5000  Assert(afterTriggers.query_stack == NULL);
5002  Assert(afterTriggers.event_cxt == NULL);
5003  Assert(afterTriggers.events.head == NULL);
5004  Assert(afterTriggers.trans_stack == NULL);
5006 }
uint32 CommandId
Definition: c.h:666
SetConstraintState state
Definition: trigger.c:3819
AfterTriggersQueryData * query_stack
Definition: trigger.c:3824

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

3955 {
3956  Oid tgoid = evtshared->ats_tgoid;
3958  int i;
3959 
3960  /*
3961  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3962  * constraints declared NOT DEFERRABLE), the state is always false.
3963  */
3964  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3965  return false;
3966 
3967  /*
3968  * If constraint state exists, SET CONSTRAINTS might have been executed
3969  * either for this trigger or for all triggers.
3970  */
3971  if (state != NULL)
3972  {
3973  /* Check for SET CONSTRAINTS for this specific trigger. */
3974  for (i = 0; i < state->numstates; i++)
3975  {
3976  if (state->trigstates[i].sct_tgoid == tgoid)
3977  return state->trigstates[i].sct_tgisdeferred;
3978  }
3979 
3980  /* Check for SET CONSTRAINTS ALL. */
3981  if (state->all_isset)
3982  return state->all_isdeferred;
3983  }
3984 
3985  /*
3986  * Otherwise return the default state for the trigger.
3987  */
3988  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3989 }
int i
Definition: isn.c:73
unsigned int Oid
Definition: postgres_ext.h:31
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 3999 of file trigger.c.

4000 {
4001  Bitmapset *dst;
4002  MemoryContext oldcxt;
4003 
4004  if (src == NULL)
4005  return NULL;
4006 
4007  /* Create event context if we didn't already */
4008  if (afterTriggers.event_cxt == NULL)
4011  "AfterTriggerEvents",
4013 
4015 
4016  dst = bms_copy(src);
4017 
4018  MemoryContextSwitchTo(oldcxt);
4019 
4020  return dst;
4021 }
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:122
MemoryContextSwitchTo(old_ctx)

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

Referenced by AfterTriggerSaveEvent().

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 4208 of file trigger.c.

4209 {
4210  AfterTriggerEventChunk *target = qs->events.head;
4211  ListCell *lc;
4212 
4213  Assert(target && target->next);
4214 
4215  /*
4216  * First, update any pointers in the per-table data, so that they won't be
4217  * dangling. Resetting obsoleted pointers to NULL will make
4218  * cancel_prior_stmt_triggers start from the list head, which is fine.
4219  */
4220  foreach(lc, qs->tables)
4221  {
4223 
4224  if (table->after_trig_done &&
4225  table->after_trig_events.tail == target)
4226  {
4227  table->after_trig_events.head = NULL;
4228  table->after_trig_events.tail = NULL;
4229  table->after_trig_events.tailfree = NULL;
4230  }
4231  }
4232 
4233  /* Now we can flush the head chunk */
4234  qs->events.head = target->next;
4235  pfree(target);
4236 }
void pfree(void *pointer)
Definition: mcxt.c:1520
#define lfirst(lc)
Definition: pg_list.h:172
AfterTriggerEventList events
Definition: trigger.c:3835
AfterTriggerEventList after_trig_events
Definition: trigger.c:3857

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

5039 {
5041 
5042  /* Must be inside a query, too */
5044 
5045  /*
5046  * If we never even got as far as initializing the event stack, there
5047  * certainly won't be any events, so exit quickly.
5048  */
5050  {
5052  return;
5053  }
5054 
5055  /*
5056  * Process all immediate-mode triggers queued by the query, and move the
5057  * deferred ones to the main list of deferred events.
5058  *
5059  * Notice that we decide which ones will be fired, and put the deferred
5060  * ones on the main list, before anything is actually fired. This ensures
5061  * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5062  * IMMEDIATE: all events we have decided to defer will be available for it
5063  * to fire.
5064  *
5065  * We loop in case a trigger queues more events at the same query level.
5066  * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5067  * will instead fire any triggers in a dedicated query level. Foreign key
5068  * enforcement triggers do add to the current query level, thanks to their
5069  * passing fire_triggers = false to SPI_execute_snapshot(). Other
5070  * C-language triggers might do likewise.
5071  *
5072  * If we find no firable events, we don't have to increment
5073  * firing_counter.
5074  */
5076 
5077  for (;;)
5078  {
5080  {
5081  CommandId firing_id = afterTriggers.firing_counter++;
5082  AfterTriggerEventChunk *oldtail = qs->events.tail;
5083 
5084  if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5085  break; /* all fired */
5086 
5087  /*
5088  * Firing a trigger could result in query_stack being repalloc'd,
5089  * so we must recalculate qs after each afterTriggerInvokeEvents
5090  * call. Furthermore, it's unsafe to pass delete_ok = true here,
5091  * because that could cause afterTriggerInvokeEvents to try to
5092  * access qs->events after the stack has been repalloc'd.
5093  */
5095 
5096  /*
5097  * We'll need to scan the events list again. To reduce the cost
5098  * of doing so, get rid of completely-fired chunks. We know that
5099  * all events were marked IN_PROGRESS or DONE at the conclusion of
5100  * afterTriggerMarkEvents, so any still-interesting events must
5101  * have been added after that, and so must be in the chunk that
5102  * was then the tail chunk, or in later chunks. So, zap all
5103  * chunks before oldtail. This is approximately the same set of
5104  * events we would have gotten rid of by passing delete_ok = true.
5105  */
5106  Assert(oldtail != NULL);
5107  while (qs->events.head != oldtail)
5109  }
5110  else
5111  break;
5112  }
5113 
5114  /* Release query-level-local storage, including tuplestores if any */
5116 
5118 }
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition: trigger.c:4208
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:5129
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4541
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4625

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

5350 {
5351  int my_level = GetCurrentTransactionNestLevel();
5353  AfterTriggerEvent event;
5355  CommandId subxact_firing_id;
5356 
5357  /*
5358  * Pop the prior state if needed.
5359  */
5360  if (isCommit)
5361  {
5362  Assert(my_level < afterTriggers.maxtransdepth);
5363  /* If we saved a prior state, we don't need it anymore */
5364  state = afterTriggers.trans_stack[my_level].state;
5365  if (state != NULL)
5366  pfree(state);
5367  /* this avoids double pfree if error later: */
5368  afterTriggers.trans_stack[my_level].state = NULL;
5371  }
5372  else
5373  {
5374  /*
5375  * Aborting. It is possible subxact start failed before calling
5376  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5377  * trans_stack levels that aren't there.
5378  */
5379  if (my_level >= afterTriggers.maxtransdepth)
5380  return;
5381 
5382  /*
5383  * Release query-level storage for queries being aborted, and restore
5384  * query_depth to its pre-subxact value. This assumes that a
5385  * subtransaction will not add events to query levels started in a
5386  * earlier transaction state.
5387  */
5389  {
5393  }
5396 
5397  /*
5398  * Restore the global deferred-event list to its former length,
5399  * discarding any events queued by the subxact.
5400  */
5402  &afterTriggers.trans_stack[my_level].events);
5403 
5404  /*
5405  * Restore the trigger state. If the saved state is NULL, then this
5406  * subxact didn't save it, so it doesn't need restoring.
5407  */
5408  state = afterTriggers.trans_stack[my_level].state;
5409  if (state != NULL)
5410  {
5413  }
5414  /* this avoids double pfree if error later: */
5415  afterTriggers.trans_stack[my_level].state = NULL;
5416 
5417  /*
5418  * Scan for any remaining deferred events that were marked DONE or IN
5419  * PROGRESS by this subxact or a child, and un-mark them. We can
5420  * recognize such events because they have a firing ID greater than or
5421  * equal to the firing_counter value we saved at subtransaction start.
5422  * (This essentially assumes that the current subxact includes all
5423  * subxacts started after it.)
5424  */
5425  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5427  {
5428  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5429 
5430  if (event->ate_flags &
5432  {
5433  if (evtshared->ats_firing_id >= subxact_firing_id)
5434  event->ate_flags &=
5436  }
5437  }
5438  }
5439 }
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3621
#define GetTriggerSharedData(evt)
Definition: trigger.c:3688
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:4168
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3620
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3725

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

5254 {
5255  /*
5256  * Forget the pending-events list.
5257  *
5258  * Since all the info is in TopTransactionContext or children thereof, we
5259  * don't really need to do anything to reclaim memory. However, the
5260  * pending-events list could be large, and so it's useful to discard it as
5261  * soon as possible --- especially if we are aborting because we ran out
5262  * of memory for the list!
5263  */
5265  {
5267  afterTriggers.event_cxt = NULL;
5268  afterTriggers.events.head = NULL;
5269  afterTriggers.events.tail = NULL;
5270  afterTriggers.events.tailfree = NULL;
5271  }
5272 
5273  /*
5274  * Forget any subtransaction state as well. Since this can't be very
5275  * large, we let the eventual reset of TopTransactionContext free the
5276  * memory instead of doing it here.
5277  */
5278  afterTriggers.trans_stack = NULL;
5280 
5281 
5282  /*
5283  * Forget the query stack and constraint-related state information. As
5284  * with the subtransaction state information, we don't bother freeing the
5285  * memory here.
5286  */
5287  afterTriggers.query_stack = NULL;
5289  afterTriggers.state = NULL;
5290 
5291  /* No more afterTriggers manipulation until next transaction starts. */
5293 }
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 5537 of file trigger.c.

5538 {
5539  int init_depth = afterTriggers.maxquerydepth;
5540 
5542 
5543  if (afterTriggers.maxquerydepth == 0)
5544  {
5545  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5546 
5549  new_alloc * sizeof(AfterTriggersQueryData));
5550  afterTriggers.maxquerydepth = new_alloc;
5551  }
5552  else
5553  {
5554  /* repalloc will keep the stack in the same context */
5555  int old_alloc = afterTriggers.maxquerydepth;
5556  int new_alloc = Max(afterTriggers.query_depth + 1,
5557  old_alloc * 2);
5558 
5561  new_alloc * sizeof(AfterTriggersQueryData));
5562  afterTriggers.maxquerydepth = new_alloc;
5563  }
5564 
5565  /* Initialize new array entries to empty */
5566  while (init_depth < afterTriggers.maxquerydepth)
5567  {
5569 
5570  qs->events.head = NULL;
5571  qs->events.tail = NULL;
5572  qs->events.tailfree = NULL;
5573  qs->fdw_tuplestore = NULL;
5574  qs->tables = NIL;
5575 
5576  ++init_depth;
5577  }
5578 }
#define Max(x, y)
Definition: c.h:998
#define NIL
Definition: pg_list.h:68
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3836

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

4280 {
4281  Relation rel = relInfo->ri_RelationDesc;
4282  Relation src_rel = src_relInfo->ri_RelationDesc;
4283  Relation dst_rel = dst_relInfo->ri_RelationDesc;
4284  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4285  Oid tgoid = evtshared->ats_tgoid;
4286  TriggerData LocTriggerData = {0};
4287  HeapTuple rettuple;
4288  int tgindx;
4289  bool should_free_trig = false;
4290  bool should_free_new = false;
4291 
4292  /*
4293  * Locate trigger in trigdesc.
4294  */
4295  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4296  {
4297  if (trigdesc->triggers[tgindx].tgoid == tgoid)
4298  {
4299  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4300  break;
4301  }
4302  }
4303  if (LocTriggerData.tg_trigger == NULL)
4304  elog(ERROR, "could not find trigger %u", tgoid);
4305 
4306  /*
4307  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4308  * to include time spent re-fetching tuples in the trigger cost.
4309  */
4310  if (instr)
4311  InstrStartNode(instr + tgindx);
4312 
4313  /*
4314  * Fetch the required tuple(s).
4315  */
4316  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4317  {
4319  {
4320  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4321 
4322  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4323  trig_tuple_slot1))
4324  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4325 
4326  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4328  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4329  trig_tuple_slot2))
4330  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4331  }
4332  /* fall through */
4334 
4335  /*
4336  * Store tuple in the slot so that tg_trigtuple does not reference
4337  * tuplestore memory. (It is formally possible for the trigger
4338  * function to queue trigger events that add to the same
4339  * tuplestore, which can push other tuples out of memory.) The
4340  * distinction is academic, because we start with a minimal tuple
4341  * that is stored as a heap tuple, constructed in different memory
4342  * context, in the slot anyway.
4343  */
4344  LocTriggerData.tg_trigslot = trig_tuple_slot1;
4345  LocTriggerData.tg_trigtuple =
4346  ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
4347 
4348  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4350  {
4351  LocTriggerData.tg_newslot = trig_tuple_slot2;
4352  LocTriggerData.tg_newtuple =
4353  ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new);
4354  }
4355  else
4356  {
4357  LocTriggerData.tg_newtuple = NULL;
4358  }
4359  break;
4360 
4361  default:
4362  if (ItemPointerIsValid(&(event->ate_ctid1)))
4363  {
4364  TupleTableSlot *src_slot = ExecGetTriggerOldSlot(estate,
4365  src_relInfo);
4366 
4367  if (!table_tuple_fetch_row_version(src_rel,
4368  &(event->ate_ctid1),
4369  SnapshotAny,
4370  src_slot))
4371  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4372 
4373  /*
4374  * Store the tuple fetched from the source partition into the
4375  * target (root partitioned) table slot, converting if needed.
4376  */
4377  if (src_relInfo != relInfo)
4378  {
4379  TupleConversionMap *map = ExecGetChildToRootMap(src_relInfo);
4380 
4381  LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4382  if (map)
4383  {
4385  src_slot,
4386  LocTriggerData.tg_trigslot);
4387  }
4388  else
4389  ExecCopySlot(LocTriggerData.tg_trigslot, src_slot);
4390  }
4391  else
4392  LocTriggerData.tg_trigslot = src_slot;
4393  LocTriggerData.tg_trigtuple =
4394  ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, &should_free_trig);
4395  }
4396  else
4397  {
4398  LocTriggerData.tg_trigtuple = NULL;
4399  }
4400 
4401  /* don't touch ctid2 if not there */
4403  (event->ate_flags & AFTER_TRIGGER_CP_UPDATE)) &&
4404  ItemPointerIsValid(&(event->ate_ctid2)))
4405  {
4406  TupleTableSlot *dst_slot = ExecGetTriggerNewSlot(estate,
4407  dst_relInfo);
4408 
4409  if (!table_tuple_fetch_row_version(dst_rel,
4410  &(event->ate_ctid2),
4411  SnapshotAny,
4412  dst_slot))
4413  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4414 
4415  /*
4416  * Store the tuple fetched from the destination partition into
4417  * the target (root partitioned) table slot, converting if
4418  * needed.
4419  */
4420  if (dst_relInfo != relInfo)
4421  {
4422  TupleConversionMap *map = ExecGetChildToRootMap(dst_relInfo);
4423 
4424  LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4425  if (map)
4426  {
4428  dst_slot,
4429  LocTriggerData.tg_newslot);
4430  }
4431  else
4432  ExecCopySlot(LocTriggerData.tg_newslot, dst_slot);
4433  }
4434  else
4435  LocTriggerData.tg_newslot = dst_slot;
4436  LocTriggerData.tg_newtuple =
4437  ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, &should_free_new);
4438  }
4439  else
4440  {
4441  LocTriggerData.tg_newtuple = NULL;
4442  }
4443  }
4444 
4445  /*
4446  * Set up the tuplestore information to let the trigger have access to
4447  * transition tables. When we first make a transition table available to
4448  * a trigger, mark it "closed" so that it cannot change anymore. If any
4449  * additional events of the same type get queued in the current trigger
4450  * query level, they'll go into new transition tables.
4451  */
4452  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4453  if (evtshared->ats_table)
4454  {
4455  if (LocTriggerData.tg_trigger->tgoldtable)
4456  {
4457  if (TRIGGER_FIRED_BY_UPDATE(evtshared->ats_event))
4458  LocTriggerData.tg_oldtable = evtshared->ats_table->old_upd_tuplestore;
4459  else
4460  LocTriggerData.tg_oldtable = evtshared->ats_table->old_del_tuplestore;
4461  evtshared->ats_table->closed = true;
4462  }
4463 
4464  if (LocTriggerData.tg_trigger->tgnewtable)
4465  {
4466  if (TRIGGER_FIRED_BY_INSERT(evtshared->ats_event))
4467  LocTriggerData.tg_newtable = evtshared->ats_table->new_ins_tuplestore;
4468  else
4469  LocTriggerData.tg_newtable = evtshared->ats_table->new_upd_tuplestore;
4470  evtshared->ats_table->closed = true;
4471  }
4472  }
4473 
4474  /*
4475  * Setup the remaining trigger information
4476  */
4477  LocTriggerData.type = T_TriggerData;
4478  LocTriggerData.tg_event =
4480  LocTriggerData.tg_relation = rel;
4481  if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4482  LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4483 
4484  MemoryContextReset(per_tuple_context);
4485 
4486  /*
4487  * Call the trigger and throw away any possibly returned updated tuple.
4488  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4489  */
4490  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4491  tgindx,
4492  finfo,
4493  NULL,
4494  per_tuple_context);
4495  if (rettuple != NULL &&
4496  rettuple != LocTriggerData.tg_trigtuple &&
4497  rettuple != LocTriggerData.tg_newtuple)
4498  heap_freetuple(rettuple);
4499 
4500  /*
4501  * Release resources
4502  */
4503  if (should_free_trig)
4504  heap_freetuple(LocTriggerData.tg_trigtuple);
4505  if (should_free_new)
4506  heap_freetuple(LocTriggerData.tg_newtuple);
4507 
4508  /* don't clear slots' contents if foreign table */
4509  if (trig_tuple_slot1 == NULL)
4510  {
4511  if (LocTriggerData.tg_trigslot)
4512  ExecClearTuple(LocTriggerData.tg_trigslot);
4513  if (LocTriggerData.tg_newslot)
4514  ExecClearTuple(LocTriggerData.tg_newslot);
4515  }
4516 
4517  /*
4518  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4519  * one "tuple returned" (really the number of firings).
4520  */
4521  if (instr)
4522  InstrStopNode(instr + tgindx, 1);
4523 }
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1731
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1138
TupleTableSlot * ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1160
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1206
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
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 SnapshotAny
Definition: snapmgr.h:33
ItemPointerData ate_ctid2
Definition: trigger.c:3647
ItemPointerData ate_ctid1
Definition: trigger.c:3646
Bitmapset * ats_modifiedcols
Definition: trigger.c:3638
Tuplestorestate * old_upd_tuplestore
Definition: trigger.c:3867
Tuplestorestate * new_upd_tuplestore
Definition: trigger.c:3869
Tuplestorestate * old_del_tuplestore
Definition: trigger.c:3871
Tuplestorestate * new_ins_tuplestore
Definition: trigger.c:3873
Relation ri_RelationDesc
Definition: execnodes.h:456
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:1260
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3624
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3918
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition: trigger.c:2302
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3623
#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:1078
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_table, AfterTriggerSharedData::ats_tgoid, TupleConversionMap::attrMap, AfterTriggersTableData::closed, elog, ERROR, ExecCallTriggerFunc(), ExecClearTuple(), ExecCopySlot(), ExecFetchSlotHeapTuple(), ExecGetChildToRootMap(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), execute_attr_map_slot(), GetCurrentFDWTuplestore(), GetTriggerSharedData, 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, 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 5197 of file trigger.c.

5198 {
5199  AfterTriggerEventList *events;
5200  bool snap_pushed = false;
5201 
5202  /* Must not be inside a query */
5204 
5205  /*
5206  * If there are any triggers to fire, make sure we have set a snapshot for
5207  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5208  * can't assume ActiveSnapshot is valid on entry.)
5209  */
5210  events = &afterTriggers.events;
5211  if (events->head != NULL)
5212  {
5214  snap_pushed = true;
5215  }
5216 
5217  /*
5218  * Run all the remaining triggers. Loop until they are all gone, in case
5219  * some trigger queues more for us to do.
5220  */
5221  while (afterTriggerMarkEvents(events, NULL, false))
5222  {
5223  CommandId firing_id = afterTriggers.firing_counter++;
5224 
5225  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5226  break; /* all fired */
5227  }
5228 
5229  /*
5230  * We don't bother freeing the event list, since it will go away anyway
5231  * (and more efficiently than via pfree) in AfterTriggerEndXact.
5232  */
5233 
5234  if (snap_pushed)
5236 }
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:216
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:648
void PopActiveSnapshot(void)
Definition: snapmgr.c:743

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

4148 {
4150 
4151  while ((chunk = events->head) != NULL)
4152  {
4153  events->head = chunk->next;
4154  pfree(chunk);
4155  }
4156  events->tail = NULL;
4157  events->tailfree = NULL;
4158 }

References chunk, AfterTriggerEventList::head, pfree(), AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 5129 of file trigger.c.

5130 {
5131  Tuplestorestate *ts;
5132  List *tables;
5133  ListCell *lc;
5134 
5135  /* Drop the trigger events */
5137 
5138  /* Drop FDW tuplestore if any */
5139  ts = qs->fdw_tuplestore;
5140  qs->fdw_tuplestore = NULL;
5141  if (ts)
5142  tuplestore_end(ts);
5143 
5144  /* Release per-table subsidiary storage */
5145  tables = qs->tables;
5146  foreach(lc, tables)
5147  {
5149 
5150  ts = table->old_upd_tuplestore;
5151  table->old_upd_tuplestore = NULL;
5152  if (ts)
5153  tuplestore_end(ts);
5154  ts = table->new_upd_tuplestore;
5155  table->new_upd_tuplestore = NULL;
5156  if (ts)
5157  tuplestore_end(ts);
5158  ts = table->old_del_tuplestore;
5159  table->old_del_tuplestore = NULL;
5160  if (ts)
5161  tuplestore_end(ts);
5162  ts = table->new_ins_tuplestore;
5163  table->new_ins_tuplestore = NULL;
5164  if (ts)
5165  tuplestore_end(ts);
5166  if (table->storeslot)
5167  {
5168  TupleTableSlot *slot = table->storeslot;
5169 
5170  table->storeslot = NULL;
5172  }
5173  }
5174 
5175  /*
5176  * Now free the AfterTriggersTableData structs and list cells. Reset list
5177  * pointer first; if list_free_deep somehow gets an error, better to leak
5178  * that storage than have an infinite loop.
5179  */
5180  qs->tables = NIL;
5181  list_free_deep(tables);
5182 }
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1341
void list_free_deep(List *list)
Definition: list.c:1560
TupleTableSlot * storeslot
Definition: trigger.c:3875
Definition: pg_list.h:54
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4147
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453

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

4629 {
4630  bool all_fired = true;
4632  MemoryContext per_tuple_context;
4633  bool local_estate = false;
4634  ResultRelInfo *rInfo = NULL;
4635  Relation rel = NULL;
4636  TriggerDesc *trigdesc = NULL;
4637  FmgrInfo *finfo = NULL;
4638  Instrumentation *instr = NULL;
4639  TupleTableSlot *slot1 = NULL,
4640  *slot2 = NULL;
4641 
4642  /* Make a local EState if need be */
4643  if (estate == NULL)
4644  {
4645  estate = CreateExecutorState();
4646  local_estate = true;
4647  }
4648 
4649  /* Make a per-tuple memory context for trigger function calls */
4650  per_tuple_context =
4652  "AfterTriggerTupleContext",
4654 
4655  for_each_chunk(chunk, *events)
4656  {
4657  AfterTriggerEvent event;
4658  bool all_fired_in_chunk = true;
4659 
4660  for_each_event(event, chunk)
4661  {
4662  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4663 
4664  /*
4665  * Is it one for me to fire?
4666  */
4667  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4668  evtshared->ats_firing_id == firing_id)
4669  {
4670  ResultRelInfo *src_rInfo,
4671  *dst_rInfo;
4672 
4673  /*
4674  * So let's fire it... but first, find the correct relation if
4675  * this is not the same relation as before.
4676  */
4677  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4678  {
4679  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4680  NULL);
4681  rel = rInfo->ri_RelationDesc;
4682  /* Catch calls with insufficient relcache refcounting */
4684  trigdesc = rInfo->ri_TrigDesc;
4685  finfo = rInfo->ri_TrigFunctions;
4686  instr = rInfo->ri_TrigInstrument;
4687  if (slot1 != NULL)
4688  {
4691  slot1 = slot2 = NULL;
4692  }
4693  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4694  {
4695  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4697  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4699  }
4700  if (trigdesc == NULL) /* should not happen */
4701  elog(ERROR, "relation %u has no triggers",
4702  evtshared->ats_relid);
4703  }
4704 
4705  /*
4706  * Look up source and destination partition result rels of a
4707  * cross-partition update event.
4708  */
4709  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4711  {
4712  Assert(OidIsValid(event->ate_src_part) &&
4713  OidIsValid(event->ate_dst_part));
4714  src_rInfo = ExecGetTriggerResultRel(estate,
4715  event->ate_src_part,
4716  rInfo);
4717  dst_rInfo = ExecGetTriggerResultRel(estate,
4718  event->ate_dst_part,
4719  rInfo);
4720  }
4721  else
4722  src_rInfo = dst_rInfo = rInfo;
4723 
4724  /*
4725  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4726  * still set, so recursive examinations of the event list
4727  * won't try to re-fire it.
4728  */
4729  AfterTriggerExecute(estate, event, rInfo,
4730  src_rInfo, dst_rInfo,
4731  trigdesc, finfo, instr,
4732  per_tuple_context, slot1, slot2);
4733 
4734  /*
4735  * Mark the event as done.
4736  */
4737  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4738  event->ate_flags |= AFTER_TRIGGER_DONE;
4739  }
4740  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4741  {
4742  /* something remains to be done */
4743  all_fired = all_fired_in_chunk = false;
4744  }
4745  }
4746 
4747  /* Clear the chunk if delete_ok and nothing left of interest */
4748  if (delete_ok && all_fired_in_chunk)
4749  {
4750  chunk->freeptr = CHUNK_DATA_START(chunk);
4751  chunk->endfree = chunk->endptr;
4752 
4753  /*
4754  * If it's last chunk, must sync event list's tailfree too. Note
4755  * that delete_ok must NOT be passed as true if there could be
4756  * additional AfterTriggerEventList values pointing at this event
4757  * list, since we'd fail to fix their copies of tailfree.
4758  */
4759  if (chunk == events->tail)
4760  events->tailfree = chunk->freeptr;
4761  }
4762  }
4763  if (slot1 != NULL)
4764  {
4767  }
4768 
4769  /* Release working resources */
4770  MemoryContextDelete(per_tuple_context);
4771 
4772  if (local_estate)
4773  {
4774  ExecCloseResultRelations(estate);
4775  ExecResetTupleTable(estate->es_tupleTable, false);
4776  FreeExecutorState(estate);
4777  }
4778 
4779  return all_fired;
4780 }
#define OidIsValid(objectId)
Definition: c.h:775
void ExecCloseResultRelations(EState *estate)
Definition: execMain.c:1517
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid, ResultRelInfo *rootRelInfo)
Definition: execMain.c:1296
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1278
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:86
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1325
EState * CreateExecutorState(void)
Definition: execUtils.c:88
void FreeExecutorState(EState *estate)
Definition: execUtils.c:189
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define RelationHasReferenceCountZero(relation)
Definition: rel.h:489
#define RelationGetRelid(relation)
Definition: rel.h:505
List * es_tupleTable
Definition: execnodes.h:669
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:495
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:486
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:489
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:4270
#define for_each_event(eptr, cptr)
Definition: trigger.c:3720
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3718

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, CHUNK_DATA_START, CreateExecutorState(), CurrentMemoryContext, elog, ERROR, EState::es_tupleTable, ExecCloseResultRelations(), ExecDropSingleTupleTableSlot(), ExecGetTriggerResultRel(), ExecResetTupleTable(), for_each_chunk, for_each_event, FreeExecutorState(), 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 4541 of file trigger.c.

4544 {
4545  bool found = false;
4546  bool deferred_found = false;
4547  AfterTriggerEvent event;
4549 
4550  for_each_event_chunk(event, chunk, *events)
4551  {
4552  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4553  bool defer_it = false;
4554 
4555  if (!(event->ate_flags &
4557  {
4558  /*
4559  * This trigger hasn't been called or scheduled yet. Check if we
4560  * should call it now.
4561  */
4562  if (immediate_only && afterTriggerCheckState(evtshared))
4563  {
4564  defer_it = true;
4565  }
4566  else
4567  {
4568  /*
4569  * Mark it as to be fired in this firing cycle.
4570  */
4572  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4573  found = true;
4574  }
4575  }
4576 
4577  /*
4578  * If it's deferred, move it to move_list, if requested.
4579  */
4580  if (defer_it && move_list != NULL)
4581  {
4582  deferred_found = true;
4583  /* add it to move_list */
4584  afterTriggerAddEvent(move_list, event, evtshared);
4585  /* mark original copy "done" so we don't do it again */
4586  event->ate_flags |= AFTER_TRIGGER_DONE;
4587  }
4588  }
4589 
4590  /*
4591  * We could allow deferred triggers if, before the end of the
4592  * security-restricted operation, we were to verify that a SET CONSTRAINTS
4593  * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4594  */
4595  if (deferred_found && InSecurityRestrictedOperation())
4596  ereport(ERROR,
4597  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4598  errmsg("cannot fire deferred trigger within security-restricted operation")));
4599 
4600  return found;
4601 }
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ereport(elevel,...)
Definition: elog.h:149
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:662
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:4031
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3954

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, afterTriggerAddEvent(), afterTriggerCheckState(), afterTriggers, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_firing_id, chunk, 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 5974 of file trigger.c.

5975 {
5976  AfterTriggerEvent event;
5978  int depth;
5979 
5980  /* Scan queued events */
5982  {
5983  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5984 
5985  /*
5986  * We can ignore completed events. (Even if a DONE flag is rolled
5987  * back by subxact abort, it's OK because the effects of the TRUNCATE
5988  * or whatever must get rolled back too.)
5989  */
5990  if (event->ate_flags & AFTER_TRIGGER_DONE)
5991  continue;
5992 
5993  if (evtshared->ats_relid == relid)
5994  return true;
5995  }
5996 
5997  /*
5998  * Also scan events queued by incomplete queries. This could only matter
5999  * if TRUNCATE/etc is executed by a function or trigger within an updating
6000  * query on the same relation, which is pretty perverse, but let's check.
6001  */
6002  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6003  {
6005  {
6006  AfterTriggerShared evtshared = GetTriggerSharedData(event);
6007 
6008  if (event->ate_flags & AFTER_TRIGGER_DONE)
6009  continue;
6010 
6011  if (evtshared->ats_relid == relid)
6012  return true;
6013  }
6014  }
6015 
6016  return false;
6017 }

References AFTER_TRIGGER_DONE, afterTriggers, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_relid, chunk, 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 4168 of file trigger.c.

4170 {
4172  AfterTriggerEventChunk *next_chunk;
4173 
4174  if (old_events->tail == NULL)
4175  {
4176  /* restoring to a completely empty state, so free everything */
4177  afterTriggerFreeEventList(events);
4178  }
4179  else
4180  {
4181  *events = *old_events;
4182  /* free any chunks after the last one we want to keep */
4183  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4184  {
4185  next_chunk = chunk->next;
4186  pfree(chunk);
4187  }
4188  /* and clean up the tail chunk to be the right length */
4189  events->tail->next = NULL;
4190  events->tail->freeptr = events->tailfree;
4191 
4192  /*
4193  * We don't make any effort to remove now-unused shared data records.
4194  * They might still be useful, anyway.
4195  */
4196  }
4197 }

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

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

5660 {
5661  int my_level = GetCurrentTransactionNestLevel();
5662 
5663  /* If we haven't already done so, initialize our state. */
5664  if (afterTriggers.state == NULL)
5666 
5667  /*
5668  * If in a subtransaction, and we didn't save the current state already,
5669  * save it so it can be restored if the subtransaction aborts.
5670  */
5671  if (my_level > 1 &&
5672  afterTriggers.trans_stack[my_level].state == NULL)
5673  {
5674  afterTriggers.trans_stack[my_level].state =
5676  }
5677 
5678  /*
5679  * Handle SET CONSTRAINTS ALL ...
5680  */
5681  if (stmt->constraints == NIL)
5682  {
5683  /*
5684  * Forget any previous SET CONSTRAINTS commands in this transaction.
5685  */
5687 
5688  /*
5689  * Set the per-transaction ALL state to known.
5690  */
5691  afterTriggers.state->all_isset = true;
5692  afterTriggers.state->all_isdeferred = stmt->deferred;
5693  }
5694  else
5695  {
5696  Relation conrel;
5697  Relation tgrel;
5698  List *conoidlist = NIL;
5699  List *tgoidlist = NIL;
5700  ListCell *lc;
5701 
5702  /*
5703  * Handle SET CONSTRAINTS constraint-name [, ...]
5704  *
5705  * First, identify all the named constraints and make a list of their
5706  * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5707  * the same name within a schema, the specifications are not
5708  * necessarily unique. Our strategy is to target all matching
5709  * constraints within the first search-path schema that has any
5710  * matches, but disregard matches in schemas beyond the first match.
5711  * (This is a bit odd but it's the historical behavior.)
5712  *
5713  * A constraint in a partitioned table may have corresponding
5714  * constraints in the partitions. Grab those too.
5715  */
5716  conrel = table_open(ConstraintRelationId, AccessShareLock);
5717 
5718  foreach(lc, stmt->constraints)
5719  {
5720  RangeVar *constraint = lfirst(lc);
5721  bool found;
5722  List *namespacelist;
5723  ListCell *nslc;
5724 
5725  if (constraint->catalogname)
5726  {
5727  if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5728  ereport(ERROR,
5729  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5730  errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5731  constraint->catalogname, constraint->schemaname,
5732  constraint->relname)));
5733  }
5734 
5735  /*
5736  * If we're given the schema name with the constraint, look only
5737  * in that schema. If given a bare constraint name, use the
5738  * search path to find the first matching constraint.
5739  */
5740  if (constraint->schemaname)
5741  {
5742  Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5743  false);
5744 
5745  namespacelist = list_make1_oid(namespaceId);
5746  }
5747  else
5748  {
5749  namespacelist = fetch_search_path(true);
5750  }
5751 
5752  found = false;
5753  foreach(nslc, namespacelist)
5754  {
5755  Oid namespaceId = lfirst_oid(nslc);
5756  SysScanDesc conscan;
5757  ScanKeyData skey[2];
5758  HeapTuple tup;
5759 
5760  ScanKeyInit(&skey[0],
5761  Anum_pg_constraint_conname,
5762  BTEqualStrategyNumber, F_NAMEEQ,
5763  CStringGetDatum(constraint->relname));
5764  ScanKeyInit(&skey[1],
5765  Anum_pg_constraint_connamespace,
5766  BTEqualStrategyNumber, F_OIDEQ,
5767  ObjectIdGetDatum(namespaceId));
5768 
5769  conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5770  true, NULL, 2, skey);
5771 
5772  while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5773  {
5775 
5776  if (con->condeferrable)
5777  conoidlist = lappend_oid(conoidlist, con->oid);
5778  else if (stmt->deferred)
5779  ereport(ERROR,
5780  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5781  errmsg("constraint \"%s\" is not deferrable",
5782  constraint->relname)));
5783  found = true;
5784  }
5785 
5786  systable_endscan(conscan);
5787 
5788  /*
5789  * Once we've found a matching constraint we do not search
5790  * later parts of the search path.
5791  */
5792  if (found)
5793  break;
5794  }
5795 
5796  list_free(namespacelist);
5797 
5798  /*
5799  * Not found ?
5800  */
5801  if (!found)
5802  ereport(ERROR,
5803  (errcode(ERRCODE_UNDEFINED_OBJECT),
5804  errmsg("constraint \"%s\" does not exist",
5805  constraint->relname)));
5806  }
5807 
5808  /*
5809  * Scan for any possible descendants of the constraints. We append
5810  * whatever we find to the same list that we're scanning; this has the
5811  * effect that we create new scans for those, too, so if there are
5812  * further descendents, we'll also catch them.
5813  */
5814  foreach(lc, conoidlist)
5815  {
5816  Oid parent = lfirst_oid(lc);
5817  ScanKeyData key;
5818  SysScanDesc scan;
5819  HeapTuple tuple;
5820 
5821  ScanKeyInit(&key,
5822  Anum_pg_constraint_conparentid,
5823  BTEqualStrategyNumber, F_OIDEQ,
5824  ObjectIdGetDatum(parent));
5825 
5826  scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5827 
5828  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5829  {
5831 
5832  conoidlist = lappend_oid(conoidlist, con->oid);
5833  }
5834 
5835  systable_endscan(scan);
5836  }
5837 
5838  table_close(conrel, AccessShareLock);
5839 
5840  /*
5841  * Now, locate the trigger(s) implementing each of these constraints,
5842  * and make a list of their OIDs.
5843  */
5844  tgrel = table_open(TriggerRelationId, AccessShareLock);
5845 
5846  foreach(lc, conoidlist)
5847  {
5848  Oid conoid = lfirst_oid(lc);
5849  ScanKeyData skey;
5850  SysScanDesc tgscan;
5851  HeapTuple htup;
5852 
5853  ScanKeyInit(&skey,
5854  Anum_pg_trigger_tgconstraint,
5855  BTEqualStrategyNumber, F_OIDEQ,
5856  ObjectIdGetDatum(conoid));
5857 
5858  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5859  NULL, 1, &skey);
5860 
5861  while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5862  {
5863  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5864 
5865  /*
5866  * Silently skip triggers that are marked as non-deferrable in
5867  * pg_trigger. This is not an error condition, since a
5868  * deferrable RI constraint may have some non-deferrable
5869  * actions.
5870  */
5871  if (pg_trigger->tgdeferrable)
5872  tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid);
5873  }
5874 
5875  systable_endscan(tgscan);
5876  }
5877 
5878  table_close(tgrel, AccessShareLock);
5879 
5880  /*
5881  * Now we can set the trigger states of individual triggers for this
5882  * xact.
5883  */
5884  foreach(lc, tgoidlist)
5885  {
5886  Oid tgoid = lfirst_oid(lc);
5888  bool found = false;
5889  int i;
5890 
5891  for (i = 0; i < state->numstates; i++)
5892  {
5893  if (state->trigstates[i].sct_tgoid == tgoid)
5894  {
5895  state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5896  found = true;
5897  break;
5898  }
5899  }
5900  if (!found)
5901  {
5903  SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5904  }
5905  }
5906  }
5907 
5908  /*
5909  * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5910  * checks against that constraint must be made when the SET CONSTRAINTS
5911  * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5912  * apply retroactively. We've updated the constraints state, so scan the
5913  * list of previously deferred events to fire any that have now become
5914  * immediate.
5915  *
5916  * Obviously, if this was SET ... DEFERRED then it can't have converted
5917  * any unfired events to immediate, so we need do nothing in that case.
5918  */
5919  if (!stmt->deferred)
5920  {
5922  bool snapshot_set = false;
5923 
5924  while (afterTriggerMarkEvents(events, NULL, true))
5925  {
5926  CommandId firing_id = afterTriggers.firing_counter++;
5927 
5928  /*
5929  * Make sure a snapshot has been established in case trigger
5930  * functions need one. Note that we avoid setting a snapshot if
5931  * we don't find at least one trigger that has to be fired now.
5932  * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
5933  * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
5934  * at the start of a transaction it's not possible for any trigger
5935  * events to be queued yet.)
5936  */
5937  if (!snapshot_set)
5938  {
5940  snapshot_set = true;
5941  }
5942 
5943  /*
5944  * We can delete fired events if we are at top transaction level,
5945  * but we'd better not if inside a subtransaction, since the
5946  * subtransaction could later get rolled back.
5947  */
5948  if (afterTriggerInvokeEvents(events, firing_id, NULL,
5949  !IsSubTransaction()))
5950  break; /* all fired */
5951  }
5952 
5953  if (snapshot_set)
5955  }
5956 }
char * get_database_name(Oid dbid)
Definition: dbcommands.c:3153
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:596
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:503
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:384
Oid MyDatabaseId
Definition: globals.c:91
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
#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:3370
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4795
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:252
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
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:82
char * catalogname
Definition: primnodes.h:76
char * schemaname
Definition: primnodes.h:79
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:5609
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition: trigger.c:5629
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:5584
bool IsSubTransaction(void)
Definition: xact.c:5001

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

6580 {
6581  /*
6582  * Must flush the plan cache when changing replication role; but don't
6583  * flush unnecessarily.
6584  */
6586  ResetPlanCache();
6587 }
#define newval
void ResetPlanCache(void)
Definition: plancache.c:2187
int SessionReplicationRole
Definition: trigger.c:63

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6458 of file trigger.c.

6459 {
6460  bool result;
6461  AfterTriggersTableData *table;
6462 
6463  /* Check state, like AfterTriggerSaveEvent. */
6464  if (afterTriggers.query_depth < 0)
6465  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6466 
6467  /* Be sure we have enough space to record events at this query depth. */
6470 
6471  /*
6472  * We keep this state in the AfterTriggersTableData that also holds
6473  * transition tables for the relation + operation. In this way, if we are
6474  * forced to make a new set of transition tables because more tuples get
6475  * entered after we've already fired triggers, we will allow a new set of
6476  * statement triggers to get queued.
6477  */
6478  table = GetAfterTriggersTableData(relid, cmdType);
6479  result = table->before_trig_done;
6480  table->before_trig_done = true;
6481  return result;
6482 }
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4796

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

6505 {
6506  AfterTriggersTableData *table;
6508 
6509  /*
6510  * We keep this state in the AfterTriggersTableData that also holds
6511  * transition tables for the relation + operation. In this way, if we are
6512  * forced to make a new set of transition tables because more tuples get
6513  * entered after we've already fired triggers, we will allow a new set of
6514  * statement triggers to get queued without canceling the old ones.
6515  */
6516  table = GetAfterTriggersTableData(relid, cmdType);
6517 
6518  if (table->after_trig_done)
6519  {
6520  /*
6521  * We want to start scanning from the tail location that existed just
6522  * before we inserted any statement triggers. But the events list
6523  * might've been entirely empty then, in which case scan from the
6524  * current head.
6525  */
6526  AfterTriggerEvent event;
6528 
6529  if (table->after_trig_events.tail)
6530  {
6531  chunk = table->after_trig_events.tail;
6532  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6533  }
6534  else
6535  {
6536  chunk = qs->events.head;
6537  event = NULL;
6538  }
6539 
6541  {
6542  if (event == NULL)
6544  for_each_event_from(event, chunk)
6545  {
6546  AfterTriggerShared evtshared = GetTriggerSharedData(event);
6547 
6548  /*
6549  * Exit loop when we reach events that aren't AS triggers for
6550  * the target relation.
6551  */
6552  if (evtshared->ats_relid != relid)
6553  goto done;
6554  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6555  goto done;
6556  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6557  goto done;
6558  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6559  goto done;
6560  /* OK, mark it DONE */
6561  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6562  event->ate_flags |= AFTER_TRIGGER_DONE;
6563  }
6564  /* signal we must reinitialize event ptr for next chunk */
6565  event = NULL;
6566  }
6567  }
6568 done:
6569 
6570  /* In any case, save current insertion point for next time */
6571  table->after_trig_done = true;
6572  table->after_trig_events = qs->events;
6573 }
#define for_each_chunk_from(cptr)
Definition: trigger.c:3729
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3731
#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, AFTER_TRIGGER_IN_PROGRESS, afterTriggers, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_relid, chunk, 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().

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

Definition at line 2085 of file trigger.c.

2086 {
2087  TriggerDesc *newdesc;
2088  Trigger *trigger;
2089  int i;
2090 
2091  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2092  return NULL;
2093 
2094  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
2095  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2096 
2097  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2098  memcpy(trigger, trigdesc->triggers,
2099  trigdesc->numtriggers * sizeof(Trigger));
2100  newdesc->triggers = trigger;
2101 
2102  for (i = 0; i < trigdesc->numtriggers; i++)
2103  {
2104  trigger->tgname = pstrdup(trigger->tgname);
2105  if (trigger->tgnattr > 0)
2106  {
2107  int16 *newattr;
2108 
2109  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2110  memcpy(newattr, trigger->tgattr,
2111  trigger->tgnattr * sizeof(int16));
2112  trigger->tgattr = newattr;
2113  }
2114  if (trigger->tgnargs > 0)
2115  {
2116  char **newargs;
2117  int16 j;
2118 
2119  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2120  for (j = 0; j < trigger->tgnargs; j++)
2121  newargs[j] = pstrdup(trigger->tgargs[j]);
2122  trigger->tgargs = newargs;
2123  }
2124  if (trigger->tgqual)
2125  trigger->tgqual = pstrdup(trigger->tgqual);
2126  if (trigger->tgoldtable)
2127  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2128  if (trigger->tgnewtable)
2129  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2130  trigger++;
2131  }
2132 
2133  return newdesc;
2134 }
signed short int16
Definition: c.h:493
int j
Definition: isn.c:74
char * pstrdup(const char *in)
Definition: mcxt.c:1695
void * palloc(Size size)
Definition: mcxt.c:1316
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 158 of file trigger.c.

162 {
163  return
164  CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
165  constraintOid, indexOid, funcoid,
166  parentTriggerOid, whenClause, isInternal,
167  in_partition, TRIGGER_FIRES_ON_ORIGIN);
168 }
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:175
#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 175 of file trigger.c.

180 {
181  int16 tgtype;
182  int ncolumns;
183  int16 *columns;
184  int2vector *tgattr;
185  List *whenRtable;
186  char *qual;
187  Datum values[Natts_pg_trigger];
188  bool nulls[Natts_pg_trigger];
189  Relation rel;
190  AclResult aclresult;
191  Relation tgrel;
192  Relation pgrel;
193  HeapTuple tuple = NULL;
194  Oid funcrettype;
195  Oid trigoid = InvalidOid;
196  char internaltrigname[NAMEDATALEN];
197  char *trigname;
198  Oid constrrelid = InvalidOid;
199  ObjectAddress myself,
200  referenced;
201  char *oldtablename = NULL;
202  char *newtablename = NULL;
203  bool partition_recurse;
204  bool trigger_exists = false;
205  Oid existing_constraint_oid = InvalidOid;
206  bool existing_isInternal = false;
207  bool existing_isClone = false;
208 
209  if (OidIsValid(relOid))
210  rel = table_open(relOid, ShareRowExclusiveLock);
211  else
212  rel = table_openrv(stmt->relation, ShareRowExclusiveLock);
213 
214  /*
215  * Triggers must be on tables or views, and there are additional
216  * relation-type-specific restrictions.
217  */
218  if (rel->rd_rel->relkind == RELKIND_RELATION)
219  {
220  /* Tables can't have INSTEAD OF triggers */
221  if (stmt->timing != TRIGGER_TYPE_BEFORE &&
222  stmt->timing != TRIGGER_TYPE_AFTER)
223  ereport(ERROR,
224  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
225  errmsg("\"%s\" is a table",
227  errdetail("Tables cannot have INSTEAD OF triggers.")));
228  }
229  else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
230  {
231  /* Partitioned tables can't have INSTEAD OF triggers */
232  if (stmt->timing != TRIGGER_TYPE_BEFORE &&
233  stmt->timing != TRIGGER_TYPE_AFTER)
234  ereport(ERROR,
235  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
236  errmsg("\"%s\" is a table",
238  errdetail("Tables cannot have INSTEAD OF triggers.")));
239 
240  /*
241  * FOR EACH ROW triggers have further restrictions
242  */
243  if (stmt->row)
244  {
245  /*
246  * Disallow use of transition tables.
247  *
248  * Note that we have another restriction about transition tables
249  * in partitions; search for 'has_superclass' below for an
250  * explanation. The check here is just to protect from the fact
251  * that if we allowed it here, the creation would succeed for a
252  * partitioned table with no partitions, but would be blocked by
253  * the other restriction when the first partition was created,
254  * which is very unfriendly behavior.
255  */
256  if (stmt->transitionRels != NIL)
257  ereport(ERROR,
258  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
259  errmsg("\"%s\" is a partitioned table",
261  errdetail("ROW triggers with transition tables are not supported on partitioned tables.")));
262  }
263  }
264  else if (rel->rd_rel->relkind == RELKIND_VIEW)
265  {
266  /*
267  * Views can have INSTEAD OF triggers (which we check below are
268  * row-level), or statement-level BEFORE/AFTER triggers.
269  */
270  if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row)
271  ereport(ERROR,
272  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
273  errmsg("\"%s\" is a view",
275  errdetail("Views cannot have row-level BEFORE or AFTER triggers.")));
276  /* Disallow TRUNCATE triggers on VIEWs */
277  if (TRIGGER_FOR_TRUNCATE(stmt->events))
278  ereport(ERROR,
279  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
280  errmsg("\"%s\" is a view",
282  errdetail("Views cannot have TRUNCATE triggers.")));
283  }
284  else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
285  {
286  if (stmt->timing != TRIGGER_TYPE_BEFORE &&
287  stmt->timing != TRIGGER_TYPE_AFTER)
288  ereport(ERROR,
289  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
290  errmsg("\"%s\" is a foreign table",
292  errdetail("Foreign tables cannot have INSTEAD OF triggers.")));
293 
294  /*
295  * We disallow constraint triggers to protect the assumption that
296  * triggers on FKs can't be deferred. See notes with AfterTriggers
297  * data structures, below.
298  */
299  if (stmt->isconstraint)
300  ereport(ERROR,
301  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
302  errmsg("\"%s\" is a foreign table",
304  errdetail("Foreign tables cannot have constraint triggers.")));
305  }
306  else
307  ereport(ERROR,
308  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
309  errmsg("relation \"%s\" cannot have triggers",
311  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
312 
314  ereport(ERROR,
315  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
316  errmsg("permission denied: \"%s\" is a system catalog",
317  RelationGetRelationName(rel))));
318 
319  if (stmt->isconstraint)
320  {
321  /*
322  * We must take a lock on the target relation to protect against
323  * concurrent drop. It's not clear that AccessShareLock is strong
324  * enough, but we certainly need at least that much... otherwise, we
325  * might end up creating a pg_constraint entry referencing a
326  * nonexistent table.
327  */
328  if (OidIsValid(refRelOid))
329  {
330  LockRelationOid(refRelOid, AccessShareLock);
331  constrrelid = refRelOid;
332  }
333  else if (stmt->constrrel != NULL)
334  constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock,
335  false);
336  }
337 
338  /* permission checks */
339  if (!isInternal)
340  {
341  aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
342  ACL_TRIGGER);
343  if (aclresult != ACLCHECK_OK)
344  aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
346 
347  if (OidIsValid(constrrelid))
348  {
349  aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
350  ACL_TRIGGER);
351  if (aclresult != ACLCHECK_OK)
352  aclcheck_error(aclresult, get_relkind_objtype(get_rel_relkind(constrrelid)),
353  get_rel_name(constrrelid));
354  }
355  }
356 
357  /*
358  * When called on a partitioned table to create a FOR EACH ROW trigger
359  * that's not internal, we create one trigger for each partition, too.
360  *
361  * For that, we'd better hold lock on all of them ahead of time.
362  */
363  partition_recurse = !isInternal && stmt->row &&
364  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
365  if (partition_recurse)
367  ShareRowExclusiveLock, NULL));
368 
369  /* Compute tgtype */
370  TRIGGER_CLEAR_TYPE(tgtype);
371  if (stmt->row)
372  TRIGGER_SETT_ROW(tgtype);
373  tgtype |= stmt->timing;
374  tgtype |= stmt->events;
375 
376  /* Disallow ROW-level TRUNCATE triggers */
377  if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
378  ereport(ERROR,
379  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
380  errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
381 
382  /* INSTEAD triggers must be row-level, and can't have WHEN or columns */
383  if (TRIGGER_FOR_INSTEAD(tgtype))
384  {
385  if (!TRIGGER_FOR_ROW(tgtype))
386  ereport(ERROR,
387  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
388  errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
389  if (stmt->whenClause)
390  ereport(ERROR,
391  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
392  errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
393  if (stmt->columns != NIL)
394  ereport(ERROR,
395  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
396  errmsg("INSTEAD OF triggers cannot have column lists")));
397  }
398 
399  /*
400  * We don't yet support naming ROW transition variables, but the parser
401  * recognizes the syntax so we can give a nicer message here.
402  *
403  * Per standard, REFERENCING TABLE names are only allowed on AFTER
404  * triggers. Per standard, REFERENCING ROW names are not allowed with FOR
405  * EACH STATEMENT. Per standard, each OLD/NEW, ROW/TABLE permutation is
406  * only allowed once. Per standard, OLD may not be specified when
407  * creating a trigger only for INSERT, and NEW may not be specified when
408  * creating a trigger only for DELETE.
409  *
410  * Notice that the standard allows an AFTER ... FOR EACH ROW trigger to
411  * reference both ROW and TABLE transition data.
412  */
413  if (stmt->transitionRels != NIL)
414  {
415  List *varList = stmt->transitionRels;
416  ListCell *lc;
417 
418  foreach(lc, varList)
419  {
421 
422  if (!(tt->isTable))
423  ereport(ERROR,
424  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
425  errmsg("ROW variable naming in the REFERENCING clause is not supported"),
426  errhint("Use OLD TABLE or NEW TABLE for naming transition tables.")));
427 
428  /*
429  * Because of the above test, we omit further ROW-related testing
430  * below. If we later allow naming OLD and NEW ROW variables,
431  * adjustments will be needed below.
432  */
433 
434  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
435  ereport(ERROR,
436  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
437  errmsg("\"%s\" is a foreign table",
439  errdetail("Triggers on foreign tables cannot have transition tables.")));
440 
441  if (rel->rd_rel->relkind == RELKIND_VIEW)
442  ereport(ERROR,
443  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
444  errmsg("\"%s\" is a view",
446  errdetail("Triggers on views cannot have transition tables.")));
447 
448  /*
449  * We currently don't allow row-level triggers with transition
450  * tables on partition or inheritance children. Such triggers
451  * would somehow need to see tuples converted to the format of the
452  * table they're attached to, and it's not clear which subset of
453  * tuples each child should see. See also the prohibitions in
454  * ATExecAttachPartition() and ATExecAddInherit().
455  */
456  if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id))
457  {
458  /* Use appropriate error message. */
459  if (rel->rd_rel->relispartition)
460  ereport(ERROR,
461  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
462  errmsg("ROW triggers with transition tables are not supported on partitions")));
463  else
464  ereport(ERROR,
465  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
466  errmsg("ROW triggers with transition tables are not supported on inheritance children")));
467  }
468 
469  if (stmt->timing != TRIGGER_TYPE_AFTER)
470  ereport(ERROR,
471  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
472  errmsg("transition table name can only be specified for an AFTER trigger")));
473 
474  if (TRIGGER_FOR_TRUNCATE(tgtype))
475  ereport(ERROR,
476  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
477  errmsg("TRUNCATE triggers with transition tables are not supported")));
478 
479  /*
480  * We currently don't allow multi-event triggers ("INSERT OR
481  * UPDATE") with transition tables, because it's not clear how to
482  * handle INSERT ... ON CONFLICT statements which can fire both
483  * INSERT and UPDATE triggers. We show the inserted tuples to
484  * INSERT triggers and the updated tuples to UPDATE triggers, but
485  * it's not yet clear what INSERT OR UPDATE trigger should see.
486  * This restriction could be lifted if we can decide on the right
487  * semantics in a later release.
488  */
489  if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) +
490  (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) +
491  (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1)
492  ereport(ERROR,
493  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
494  errmsg("transition tables cannot be specified for triggers with more than one event")));
495 
496  /*
497  * We currently don't allow column-specific triggers with
498  * transition tables. Per spec, that seems to require
499  * accumulating separate transition tables for each combination of
500  * columns, which is a lot of work for a rather marginal feature.
501  */
502  if (stmt->columns != NIL)
503  ereport(ERROR,
504  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
505  errmsg("transition tables cannot be specified for triggers with column lists")));
506 
507  /*
508  * We disallow constraint triggers with transition tables, to
509  * protect the assumption that such triggers can't be deferred.
510  * See notes with AfterTriggers data structures, below.
511  *
512  * Currently this is enforced by the grammar, so just Assert here.
513  */
514  Assert(!stmt->isconstraint);
515 
516  if (tt->isNew)
517  {
518  if (!(TRIGGER_FOR_INSERT(tgtype) ||
519  TRIGGER_FOR_UPDATE(tgtype)))
520  ereport(ERROR,
521  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
522  errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger")));
523 
524  if (newtablename != NULL)
525  ereport(ERROR,
526  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
527  errmsg("NEW TABLE cannot be specified multiple times")));
528 
529  newtablename = tt->name;
530  }
531  else
532  {
533  if (!(TRIGGER_FOR_DELETE(tgtype) ||
534  TRIGGER_FOR_UPDATE(tgtype)))
535  ereport(ERROR,
536  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
537  errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger")));
538 
539  if (oldtablename != NULL)
540  ereport(ERROR,
541  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
542  errmsg("OLD TABLE cannot be specified multiple times")));
543 
544  oldtablename = tt->name;
545  }
546  }
547 
548  if (newtablename != NULL && oldtablename != NULL &&
549  strcmp(newtablename, oldtablename) == 0)
550  ereport(ERROR,
551  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
552  errmsg("OLD TABLE name and NEW TABLE name cannot be the same")));
553  }
554 
555  /*
556  * Parse the WHEN clause, if any and we weren't passed an already
557  * transformed one.
558  *
559  * Note that as a side effect, we fill whenRtable when parsing. If we got
560  * an already parsed clause, this does not occur, which is what we want --
561  * no point in adding redundant dependencies below.
562  */
563  if (!whenClause && stmt->whenClause)
564  {
565  ParseState *pstate;
566  ParseNamespaceItem *nsitem;
567  List *varList;
568  ListCell *lc;
569 
570  /* Set up a pstate to parse with */
571  pstate = make_parsestate(NULL);
572  pstate->p_sourcetext = queryString;
573 
574  /*
575  * Set up nsitems for OLD and NEW references.
576  *
577  * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
578  */
579  nsitem = addRangeTableEntryForRelation(pstate, rel,
581  makeAlias("old", NIL),
582  false, false);
583  addNSItemToQuery(pstate, nsitem, false, true, true);
584  nsitem = addRangeTableEntryForRelation(pstate, rel,
586  makeAlias("new", NIL),
587  false, false);
588  addNSItemToQuery(pstate, nsitem, false, true, true);
589 
590  /* Transform expression. Copy to be sure we don't modify original */
591  whenClause = transformWhereClause(pstate,
592  copyObject(stmt->whenClause),
594  "WHEN");
595  /* we have to fix its collations too */
596  assign_expr_collations(pstate, whenClause);
597 
598  /*
599  * Check for disallowed references to OLD/NEW.
600  *
601  * NB: pull_var_clause is okay here only because we don't allow
602  * subselects in WHEN clauses; it would fail to examine the contents
603  * of subselects.
604  */
605  varList = pull_var_clause(whenClause, 0);
606  foreach(lc, varList)
607  {
608  Var *var = (Var *) lfirst(lc);
609 
610  switch (var->varno)
611  {
612  case PRS2_OLD_VARNO:
613  if (!TRIGGER_FOR_ROW(tgtype))
614  ereport(ERROR,
615  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
616  errmsg("statement trigger's WHEN condition cannot reference column values"),
617  parser_errposition(pstate, var->location)));
618  if (TRIGGER_FOR_INSERT(tgtype))
619  ereport(ERROR,
620  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
621  errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
622  parser_errposition(pstate, var->location)));
623  /* system columns are okay here */
624  break;
625  case PRS2_NEW_VARNO:
626  if (!TRIGGER_FOR_ROW(tgtype))
627  ereport(ERROR,
628  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
629  errmsg("statement trigger's WHEN condition cannot reference column values"),
630  parser_errposition(pstate, var->location)));
631  if (TRIGGER_FOR_DELETE(tgtype))
632  ereport(ERROR,
633  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
634  errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
635  parser_errposition(pstate, var->location)));
636  if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
637  ereport(ERROR,
638  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
639  errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
640  parser_errposition(pstate, var->location)));
641  if (TRIGGER_FOR_BEFORE(tgtype) &&
642  var->varattno == 0 &&
643  RelationGetDescr(rel)->constr &&
644  RelationGetDescr(rel)->constr->has_generated_stored)
645  ereport(ERROR,
646  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
647  errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
648  errdetail("A whole-row reference is used and the table contains generated columns."),
649  parser_errposition(pstate, var->location)));
650  if (TRIGGER_FOR_BEFORE(tgtype) &&
651  var->varattno > 0 &&
652  TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attgenerated)
653  ereport(ERROR,
654  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
655  errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
656  errdetail("Column \"%s\" is a generated column.",
657  NameStr(TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attname)),
658  parser_errposition(pstate, var->location)));
659  break;
660  default:
661  /* can't happen without add_missing_from, so just elog */
662  elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
663  break;
664  }
665  }
666 
667  /* we'll need the rtable for recordDependencyOnExpr */
668  whenRtable = pstate->p_rtable;
669 
670  qual = nodeToString(whenClause);
671 
672  free_parsestate(pstate);
673  }
674  else if (!whenClause)
675  {
676  whenClause = NULL;
677  whenRtable = NIL;
678  qual = NULL;
679  }
680  else
681  {
682  qual = nodeToString(whenClause);
683  whenRtable = NIL;
684  }
685 
686  /*
687  * Find and validate the trigger function.
688  */
689  if (!OidIsValid(funcoid))
690  funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
691  if (!isInternal)
692  {
693  aclresult = object_aclcheck(ProcedureRelationId, funcoid, GetUserId(), ACL_EXECUTE);
694  if (aclresult != ACLCHECK_OK)
695  aclcheck_error(aclresult, OBJECT_FUNCTION,
696  NameListToString(stmt->funcname));
697  }
698  funcrettype = get_func_rettype(funcoid);
699  if (funcrettype != TRIGGEROID)
700  ereport(ERROR,
701  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
702  errmsg("function %s must return type %s",
703  NameListToString(stmt->funcname), "trigger")));
704 
705  /*
706  * Scan pg_trigger to see if there is already a trigger of the same name.
707  * Skip this for internally generated triggers, since we'll modify the
708  * name to be unique below.
709  *
710  * NOTE that this is cool only because we have ShareRowExclusiveLock on
711  * the relation, so the trigger set won't be changing underneath us.
712  */
713  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
714  if (!isInternal)
715  {
716  ScanKeyData skeys[2];
717  SysScanDesc tgscan;
718 
719  ScanKeyInit(&skeys[0],
720  Anum_pg_trigger_tgrelid,
721  BTEqualStrategyNumber, F_OIDEQ,
723 
724  ScanKeyInit(&skeys[1],
725  Anum_pg_trigger_tgname,
726  BTEqualStrategyNumber, F_NAMEEQ,
727  CStringGetDatum(stmt->trigname));
728 
729  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
730  NULL, 2, skeys);
731 
732  /* There should be at most one matching tuple */
733  if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
734  {
735  Form_pg_trigger oldtrigger = (Form_pg_trigger) GETSTRUCT(tuple);
736 
737  trigoid = oldtrigger->oid;
738  existing_constraint_oid = oldtrigger->tgconstraint;
739  existing_isInternal = oldtrigger->tgisinternal;
740  existing_isClone = OidIsValid(oldtrigger->tgparentid);
741  trigger_exists = true;
742  /* copy the tuple to use in CatalogTupleUpdate() */
743  tuple = heap_copytuple(tuple);
744  }
745  systable_endscan(tgscan);
746  }
747 
748  if (!trigger_exists)
749  {
750  /* Generate the OID for the new trigger. */
751  trigoid = GetNewOidWithIndex(tgrel, TriggerOidIndexId,
752  Anum_pg_trigger_oid);
753  }
754  else
755  {
756  /*
757  * If OR REPLACE was specified, we'll replace the old trigger;
758  * otherwise complain about the duplicate name.
759  */
760  if (!stmt->replace)
761  ereport(ERROR,
763  errmsg("trigger \"%s\" for relation \"%s\" already exists",
764  stmt->trigname, RelationGetRelationName(rel))));
765 
766  /*
767  * An internal trigger or a child trigger (isClone) cannot be replaced
768  * by a user-defined trigger. However, skip this test when
769  * in_partition, because then we're recursing from a partitioned table
770  * and the check was made at the parent level.
771  */
772  if ((existing_isInternal || existing_isClone) &&
773  !isInternal && !in_partition)
774  ereport(ERROR,
776  errmsg("trigger \"%s\" for relation \"%s\" is an internal or a child trigger",
777  stmt->trigname, RelationGetRelationName(rel))));
778 
779  /*
780  * It is not allowed to replace with a constraint trigger; gram.y
781  * should have enforced this already.
782  */
783  Assert(!stmt->isconstraint);
784 
785  /*
786  * It is not allowed to replace an existing constraint trigger,
787  * either. (The reason for these restrictions is partly that it seems
788  * difficult to deal with pending trigger events in such cases, and
789  * partly that the command might imply changing the constraint's
790  * properties as well, which doesn't seem nice.)
791  */
792  if (OidIsValid(existing_constraint_oid))
793  ereport(ERROR,
795  errmsg("trigger \"%s\" for relation \"%s\" is a constraint trigger",
796  stmt->trigname, RelationGetRelationName(rel))));
797  }
798 
799  /*
800  * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
801  * corresponding pg_constraint entry.
802  */
803  if (stmt->isconstraint && !OidIsValid(constraintOid))
804  {
805  /* Internal callers should have made their own constraints */
806  Assert(!isInternal);
807  constraintOid = CreateConstraintEntry(stmt->trigname,
809  CONSTRAINT_TRIGGER,
810  stmt->deferrable,
811  stmt->initdeferred,
812  true,
813  InvalidOid, /* no parent */
814  RelationGetRelid(rel),
815  NULL, /* no conkey */
816  0,
817  0,
818  InvalidOid, /* no domain */
819  InvalidOid, /* no index */
820  InvalidOid, /* no foreign key */
821  NULL,
822  NULL,
823  NULL,
824  NULL,
825  0,
826  ' ',
827  ' ',
828  NULL,
829  0,
830  ' ',
831  NULL, /* no exclusion */
832  NULL, /* no check constraint */
833  NULL,
834  true, /* islocal */
835  0, /* inhcount */
836  true, /* noinherit */
837  false, /* conperiod */
838  isInternal); /* is_internal */
839  }
840 
841  /*
842  * If trigger is internally generated, modify the provided trigger name to
843  * ensure uniqueness by appending the trigger OID. (Callers will usually
844  * supply a simple constant trigger name in these cases.)
845  */
846  if (isInternal)
847  {
848  snprintf(internaltrigname, sizeof(internaltrigname),
849  "%s_%u", stmt->trigname, trigoid);
850  trigname = internaltrigname;
851  }
852  else
853  {
854  /* user-defined trigger; use the specified trigger name as-is */
855  trigname = stmt->trigname;
856  }
857 
858  /*
859  * Build the new pg_trigger tuple.
860  */
861  memset(nulls, false, sizeof(nulls));
862 
863  values[Anum_pg_trigger_oid - 1] = ObjectIdGetDatum(trigoid);
864  values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
865  values[Anum_pg_trigger_tgparentid - 1] = ObjectIdGetDatum(parentTriggerOid);
866  values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
867  CStringGetDatum(trigname));
868  values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
869  values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
870  values[Anum_pg_trigger_tgenabled - 1] = trigger_fires_when;
871  values[Anum_pg_trigger_tgisinternal - 1] = BoolGetDatum(isInternal);
872  values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
873  values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid);
874  values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
875  values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
876  values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
877 
878  if (stmt->args)
879  {
880  ListCell *le;
881  char *args;
882  int16 nargs = list_length(stmt->args);
883  int len = 0;
884 
885  foreach(le, stmt->args)
886  {
887  char *ar = strVal(lfirst(le));
888 
889  len += strlen(ar) + 4;
890  for (; *ar; ar++)
891  {
892  if (*ar == '\\')
893  len++;
894  }
895  }
896  args = (char *) palloc(len + 1);
897  args[0] = '\0';
898  foreach(le, stmt->args)
899  {
900  char *s = strVal(lfirst(le));
901  char *d = args + strlen(args);
902 
903  while (*s)
904  {
905  if (*s == '\\')
906  *d++ = '\\';
907  *d++ = *s++;
908  }
909  strcpy(d, "\\000");
910  }
911  values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
912  values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
914  }
915  else
916  {
917  values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
918  values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
919  CStringGetDatum(""));
920  }
921 
922  /* build column number array if it's a column-specific trigger */
923  ncolumns = list_length(stmt->columns);
924  if (ncolumns == 0)
925  columns = NULL;
926  else
927  {
928  ListCell *cell;
929  int i = 0;
930 
931  columns = (int16 *) palloc(ncolumns * sizeof(int16));
932  foreach(cell, stmt->columns)
933  {
934  char *name = strVal(lfirst(cell));
935  int16 attnum;
936  int j;
937 
938  /* Lookup column name. System columns are not allowed */
939  attnum = attnameAttNum(rel, name, false);
940  if (attnum == InvalidAttrNumber)
941  ereport(ERROR,
942  (errcode(ERRCODE_UNDEFINED_COLUMN),
943  errmsg("column \"%s\" of relation \"%s\" does not exist",
944  name, RelationGetRelationName(rel))));
945 
946  /* Check for duplicates */
947  for (j = i - 1; j >= 0; j--)
948  {
949  if (columns[j] == attnum)
950  ereport(ERROR,
951  (errcode(ERRCODE_DUPLICATE_COLUMN),
952  errmsg("column \"%s\" specified more than once",
953  name)));
954  }
955 
956  columns[i++] = attnum;
957  }
958  }
959  tgattr = buildint2vector(columns, ncolumns);
960  values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
961 
962  /* set tgqual if trigger has WHEN clause */
963  if (qual)
964  values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(qual);
965  else
966  nulls[Anum_pg_trigger_tgqual - 1] = true;
967 
968  if (oldtablename)
969  values[Anum_pg_trigger_tgoldtable - 1] = DirectFunctionCall1(namein,
970  CStringGetDatum(oldtablename));
971  else
972  nulls[Anum_pg_trigger_tgoldtable - 1] = true;
973  if (newtablename)
974  values[Anum_pg_trigger_tgnewtable - 1] = DirectFunctionCall1(namein,
975  CStringGetDatum(newtablename));
976  else
977  nulls[Anum_pg_trigger_tgnewtable - 1] = true;
978 
979  /*
980  * Insert or replace tuple in pg_trigger.
981  */
982  if (!trigger_exists)
983  {
984  tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
985  CatalogTupleInsert(tgrel, tuple);
986  }
987  else
988  {
989  HeapTuple newtup;
990 
991  newtup = heap_form_tuple(tgrel->rd_att, values, nulls);
992  CatalogTupleUpdate(tgrel, &tuple->t_self, newtup);
993  heap_freetuple(newtup);
994  }
995 
996  heap_freetuple(tuple); /* free either original or new tuple */
998 
999  pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
1000  pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
1001  pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1]));
1002  if (oldtablename)
1003  pfree(DatumGetPointer(values[Anum_pg_trigger_tgoldtable - 1]));
1004  if (newtablename)
1005  pfree(DatumGetPointer(values[Anum_pg_trigger_tgnewtable - 1]));
1006 
1007  /*
1008  * Update relation's pg_class entry; if necessary; and if not, send an SI
1009  * message to make other backends (and this one) rebuild relcache entries.
1010  */
1011  pgrel = table_open(RelationRelationId, RowExclusiveLock);
1012  tuple = SearchSysCacheCopy1(RELOID,
1014  if (!HeapTupleIsValid(tuple))
1015  elog(ERROR, "cache lookup failed for relation %u",
1016  RelationGetRelid(rel));
1017  if (!((Form_pg_class) GETSTRUCT(tuple))->relhastriggers)
1018  {
1019  ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
1020 
1021  CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
1022 
1024  }
1025  else
1027 
1028  heap_freetuple(tuple);
1029  table_close(pgrel, RowExclusiveLock);
1030 
1031  /*
1032  * If we're replacing a trigger, flush all the old dependencies before
1033  * recording new ones.
1034  */
1035  if (trigger_exists)
1036  deleteDependencyRecordsFor(TriggerRelationId, trigoid, true);
1037 
1038  /*
1039  * Record dependencies for trigger. Always place a normal dependency on
1040  * the function.
1041  */
1042  myself.classId = TriggerRelationId;
1043  myself.objectId = trigoid;
1044  myself.objectSubId = 0;
1045 
1046  referenced.classId = ProcedureRelationId;
1047  referenced.objectId = funcoid;
1048  referenced.objectSubId = 0;
1049  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1050 
1051  if (isInternal && OidIsValid(constraintOid))
1052  {
1053  /*
1054  * Internally-generated trigger for a constraint, so make it an
1055  * internal dependency of the constraint. We can skip depending on
1056  * the relation(s), as there'll be an indirect dependency via the
1057  * constraint.
1058  */
1059  referenced.classId = ConstraintRelationId;
1060  referenced.objectId = constraintOid;
1061  referenced.objectSubId = 0;
1062  recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1063  }
1064  else
1065  {
1066  /*
1067  * User CREATE TRIGGER, so place dependencies. We make trigger be
1068  * auto-dropped if its relation is dropped or if the FK relation is
1069  * dropped. (Auto drop is compatible with our pre-7.3 behavior.)
1070  */
1071  referenced.classId = RelationRelationId;
1072  referenced.objectId = RelationGetRelid(rel);
1073  referenced.objectSubId = 0;
1074  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1075 
1076  if (OidIsValid(constrrelid))
1077  {
1078  referenced.classId = RelationRelationId;
1079  referenced.objectId = constrrelid;
1080  referenced.objectSubId = 0;
1081  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1082  }
1083  /* Not possible to have an index dependency in this case */
1084  Assert(!OidIsValid(indexOid));
1085 
1086  /*
1087  * If it's a user-specified constraint trigger, make the constraint
1088  * internally dependent on the trigger instead of vice versa.
1089  */
1090  if (OidIsValid(constraintOid))
1091  {
1092  referenced.classId = ConstraintRelationId;
1093  referenced.objectId = constraintOid;
1094  referenced.objectSubId = 0;
1095  recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
1096  }
1097 
1098  /*
1099  * If it's a partition trigger, create the partition dependencies.
1100  */
1101  if (OidIsValid(parentTriggerOid))
1102  {
1103  ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
1104  recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
1105  ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
1106  recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
1107  }
1108  }
1109 
1110  /* If column-specific trigger, add normal dependencies on columns */
1111  if (columns != NULL)
1112  {
1113  int i;
1114 
1115  referenced.classId = RelationRelationId;
1116  referenced.objectId = RelationGetRelid(rel);
1117  for (i = 0; i < ncolumns; i++)
1118  {
1119  referenced.objectSubId = columns[i];
1120  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1121  }
1122  }
1123 
1124  /*
1125  * If it has a WHEN clause, add dependencies on objects mentioned in the
1126  * expression (eg, functions, as well as any columns used).
1127  */
1128  if (whenRtable != NIL)
1129  recordDependencyOnExpr(&myself, whenClause, whenRtable,
1131 
1132  /* Post creation hook for new trigger */
1133  InvokeObjectPostCreateHookArg(TriggerRelationId, trigoid, 0,
1134  isInternal);
1135 
1136  /*
1137  * Lastly, create the trigger on child relations, if needed.
1138  */
1139  if (partition_recurse)
1140  {
1141  PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1142  int i;
1143  MemoryContext oldcxt,
1144  perChildCxt;
1145 
1147  "part trig clone",
1149 
1150  /*
1151  * We don't currently expect to be called with a valid indexOid. If
1152  * that ever changes then we'll need to write code here to find the
1153  * corresponding child index.
1154  */
1155  Assert(!OidIsValid(indexOid));
1156 
1157  oldcxt = MemoryContextSwitchTo(perChildCxt);
1158 
1159  /* Iterate to create the trigger on each existing partition */
1160  for (i = 0; i < partdesc->nparts; i++)
1161  {
1162  CreateTrigStmt *childStmt;
1163  Relation childTbl;
1164  Node *qual;
1165 
1166  childTbl = table_open(partdesc->oids[i], ShareRowExclusiveLock);
1167 
1168  /*
1169  * Initialize our fabricated parse node by copying the original
1170  * one, then resetting fields that we pass separately.
1171  */
1172  childStmt = (CreateTrigStmt *) copyObject(stmt);
1173  childStmt->funcname = NIL;
1174  childStmt->whenClause = NULL;
1175 
1176  /* If there is a WHEN clause, create a modified copy of it */
1177  qual = copyObject(whenClause);
1178  qual = (Node *)
1180  childTbl, rel);
1181  qual = (Node *)
1183  childTbl, rel);
1184 
1185  CreateTriggerFiringOn(childStmt, queryString,
1186  partdesc->oids[i], refRelOid,
1188  funcoid, trigoid, qual,
1189  isInternal, true, trigger_fires_when);
1190 
1191  table_close(childTbl, NoLock);
1192 
1193  MemoryContextReset(perChildCxt);
1194  }
1195 
1196  MemoryContextSwitchTo(oldcxt);
1197  MemoryContextDelete(perChildCxt);
1198  }
1199 
1200  /* Keep lock on target rel until end of xact */
1201  table_close(rel, NoLock);
1202 
1203  return myself;
1204 }
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2688
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3876
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4079
#define InvalidAttrNumber
Definition: attnum.h:23
static Datum values[MAXATTR]
Definition: bootstrap.c:152
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define NameStr(name)
Definition: c.h:746
bool IsSystemRelation(Relation relation)
Definition: catalog.c:73
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:391
void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior)
Definition: dependency.c:1551
@ 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:1205
int errhint(const char *fmt,...)
Definition: elog.c:1319
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:642
bool allowSystemTableMods
Definition: globals.c:127
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:776
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
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:1396
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:108
#define NoLock
Definition: lockdefs.h:34
#define ShareRowExclusiveLock
Definition: lockdefs.h:41
#define RowExclusiveLock
Definition: lockdefs.h:38
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1981
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1906
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1633
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:389
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
Oid GetUserId(void)
Definition: miscinit.c:514
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:48
char * NameListToString(const List *names)
Definition: namespace.c:3579
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:80
#define copyObject(obj)
Definition: nodes.h:224
#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:791
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:2143
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:2280
#define ACL_EXECUTE
Definition: parsenodes.h:83
#define ACL_TRIGGER
Definition: parsenodes.h:82
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:70
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:153
#define NAMEDATALEN
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, 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, int conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
Definition: pg_constraint.c:50
const void size_t len
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:300
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:238
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define InvalidOid
Definition: postgres_ext.h:36
#define PRS2_OLD_VARNO
Definition: primnodes.h:244
#define PRS2_NEW_VARNO
Definition: primnodes.h:245
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
#define RelationGetNamespace(relation)
Definition: rel.h:546
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
Node * whenClause
Definition: parsenodes.h:3025
ItemPointerData t_self
Definition: htup.h:65
Definition: nodes.h:129
const char * p_sourcetext
Definition: parse_node.h:193
List * p_rtable
Definition: parse_node.h:194
Oid rd_id
Definition: rel.h:113
Definition: primnodes.h:248
ParseLoc location
Definition: primnodes.h:293
AttrNumber varattno
Definition: primnodes.h:260
int varno
Definition: primnodes.h:255
Definition: c.h:715
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:86
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:83
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define strVal(v)
Definition: value.h:82
List * pull_var_clause(Node *node, int flags)
Definition: var.c:607
Datum byteain(PG_FUNCTION_ARGS)
Definition: varlena.c:290
const char * name
void CommandCounterIncrement(void)
Definition: xact.c:1097

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(), 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(), and CreateTrigger().

◆ EnableDisableTrigger()

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

Definition at line 1721 of file trigger.c.

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

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), CStringGetDatum(), 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().

◆ ExecARDeleteTriggers()

void ExecARDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TransitionCaptureState transition_capture,
bool  is_crosspart_update 
)

Definition at line 2774 of file trigger.c.

2780 {
2781  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2782 
2783  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2784  (transition_capture && transition_capture->tcs_delete_old_table))
2785  {
2786  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2787 
2788  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2789  if (fdw_trigtuple == NULL)
2790  GetTupleForTrigger(estate,
2791  NULL,
2792  relinfo,
2793  tupleid,
2795  slot,
2796  NULL,
2797  NULL,
2798  NULL);
2799  else
2800  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2801 
2802  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2804  true, slot, NULL, NIL, NULL,
2805  transition_capture,
2806  is_crosspart_update);
2807  }
2808 }
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1556
@ 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:6061
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:3286

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

2537 {
2538  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2539 
2540  if ((trigdesc && trigdesc->trig_insert_after_row) ||
2541  (transition_capture && transition_capture->tcs_insert_new_table))
2542  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2544  true, NULL, slot,
2545  recheckIndexes, NULL,
2546  transition_capture,
2547  false);
2548 }

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

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

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

2665 {
2666  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2667 
2668  if (trigdesc && trigdesc->trig_delete_after_statement)
2669  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2671  false, NULL, NULL, NIL, NULL, transition_capture,
2672  false);
2673 }
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 2445 of file trigger.c.

2447 {
2448  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2449 
2450  if (trigdesc && trigdesc->trig_insert_after_statement)
2451  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2453  false, NULL, NULL, NIL, NULL, transition_capture,
2454  false);
2455 }
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 3269 of file trigger.c.

3270 {
3271  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3272 
3273  if (trigdesc && trigdesc->trig_truncate_after_statement)
3274  AfterTriggerSaveEvent(estate, relinfo,
3275  NULL, NULL,
3277  false, NULL, NULL, NIL, NULL, NULL,
3278  false);
3279 }
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 2916 of file trigger.c.

2918 {
2919  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2920 
2921  /* statement-level triggers operate on the parent table */
2922  Assert(relinfo->ri_RootResultRelInfo == NULL);
2923 
2924  if (trigdesc && trigdesc->trig_update_after_statement)
2925  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2927  false, NULL, NULL, NIL,
2928  ExecGetAllUpdatedCols(relinfo, estate),
2929  transition_capture,
2930  false);
2931 }
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:582
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 2683 of file trigger.c.

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

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

References ereport, errcode(), errdetail(), errmsg(), ERROR, ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecPartitionCheck(), get_namespace_name(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, 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 2934 of file trigger.c.

2941 {
2942  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2943  TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
2944  HeapTuple newtuple = NULL;
2945  HeapTuple trigtuple;
2946  bool should_free_trig = false;
2947  bool should_free_new = false;
2948  TriggerData LocTriggerData = {0};
2949  int i;
2950  Bitmapset *updatedCols;
2951  LockTupleMode lockmode;
2952 
2953  /* Determine lock mode to use */
2954  lockmode = ExecUpdateLockMode(estate, relinfo);
2955 
2956  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2957  if (fdw_trigtuple == NULL)
2958  {
2959  TupleTableSlot *epqslot_candidate = NULL;
2960 
2961  /* get a copy of the on-disk tuple we are planning to update */
2962  if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2963  lockmode, oldslot, &epqslot_candidate,
2964  tmresult, tmfd))
2965  return false; /* cancel the update action */
2966 
2967  /*
2968  * In READ COMMITTED isolation level it's possible that target tuple
2969  * was changed due to concurrent update. In that case we have a raw
2970  * subplan output tuple in epqslot_candidate, and need to form a new
2971  * insertable tuple using ExecGetUpdateNewTuple to replace the one we
2972  * received in newslot. Neither we nor our callers have any further
2973  * interest in the passed-in tuple, so it's okay to overwrite newslot