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

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3627 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3628 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3621 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3625 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3624 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3622 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3620 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3629 of file trigger.c.

◆ CHUNK_DATA_START

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

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

◆ for_each_chunk_from

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

Definition at line 3730 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:3708
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3680
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3642

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

◆ GetTriggerSharedData

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

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

Definition at line 3680 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3642 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3630 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3642 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3642 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3642 of file trigger.c.

◆ SetConstraintState

Definition at line 3573 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3552 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3618 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 4032 of file trigger.c.

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

5025 {
5026  /* Increase the query stack depth */
5028 }

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5307 of file trigger.c.

5308 {
5309  int my_level = GetCurrentTransactionNestLevel();
5310 
5311  /*
5312  * Allocate more space in the trans_stack if needed. (Note: because the
5313  * minimum nest level of a subtransaction is 2, we waste the first couple
5314  * entries of the array; not worth the notational effort to avoid it.)
5315  */
5316  while (my_level >= afterTriggers.maxtransdepth)
5317  {
5318  if (afterTriggers.maxtransdepth == 0)
5319  {
5320  /* Arbitrarily initialize for max of 8 subtransaction levels */
5323  8 * sizeof(AfterTriggersTransData));
5325  }
5326  else
5327  {
5328  /* repalloc will keep the stack in the same context */
5329  int new_alloc = afterTriggers.maxtransdepth * 2;
5330 
5333  new_alloc * sizeof(AfterTriggersTransData));
5334  afterTriggers.maxtransdepth = new_alloc;
5335  }
5336  }
5337 
5338  /*
5339  * Push the current information into the stack. The SET CONSTRAINTS state
5340  * is not saved until/unless changed. Likewise, we don't make a
5341  * per-subtransaction event context until needed.
5342  */
5343  afterTriggers.trans_stack[my_level].state = NULL;
5347 }
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1541
CommandId firing_counter
Definition: trigger.c:3819
AfterTriggersTransData * trans_stack
Definition: trigger.c:3830
AfterTriggerEventList events
Definition: trigger.c:3821
AfterTriggerEventList events
Definition: trigger.c:3845
SetConstraintState state
Definition: trigger.c:3844
CommandId firing_counter
Definition: trigger.c:3847
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:928

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

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

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

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

4001 {
4002  Bitmapset *dst;
4003  MemoryContext oldcxt;
4004 
4005  if (src == NULL)
4006  return NULL;
4007 
4008  /* Create event context if we didn't already */
4009  if (afterTriggers.event_cxt == NULL)
4012  "AfterTriggerEvents",
4014 
4016 
4017  dst = bms_copy(src);
4018 
4019  MemoryContextSwitchTo(oldcxt);
4020 
4021  return dst;
4022 }
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 4212 of file trigger.c.

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

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

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

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

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

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

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

5544 {
5545  int init_depth = afterTriggers.maxquerydepth;
5546 
5548 
5549  if (afterTriggers.maxquerydepth == 0)
5550  {
5551  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5552 
5555  new_alloc * sizeof(AfterTriggersQueryData));
5556  afterTriggers.maxquerydepth = new_alloc;
5557  }
5558  else
5559  {
5560  /* repalloc will keep the stack in the same context */
5561  int old_alloc = afterTriggers.maxquerydepth;
5562  int new_alloc = Max(afterTriggers.query_depth + 1,
5563  old_alloc * 2);
5564 
5567  new_alloc * sizeof(AfterTriggersQueryData));
5568  afterTriggers.maxquerydepth = new_alloc;
5569  }
5570 
5571  /* Initialize new array entries to empty */
5572  while (init_depth < afterTriggers.maxquerydepth)
5573  {
5575 
5576  qs->events.head = NULL;
5577  qs->events.tail = NULL;
5578  qs->events.tailfree = NULL;
5579  qs->fdw_tuplestore = NULL;
5580  qs->tables = NIL;
5581 
5582  ++init_depth;
5583  }
5584 }
#define Max(x, y)
Definition: c.h:989
#define NIL
Definition: pg_list.h:68
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3837

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

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

References AFTER_TRIGGER_2CTID, AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_TUP_BITS, AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_modifiedcols, AfterTriggerSharedData::ats_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 5203 of file trigger.c.

5204 {
5205  AfterTriggerEventList *events;
5206  bool snap_pushed = false;
5207 
5208  /* Must not be inside a query */
5210 
5211  /*
5212  * If there are any triggers to fire, make sure we have set a snapshot for
5213  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5214  * can't assume ActiveSnapshot is valid on entry.)
5215  */
5216  events = &afterTriggers.events;
5217  if (events->head != NULL)
5218  {
5220  snap_pushed = true;
5221  }
5222 
5223  /*
5224  * Run all the remaining triggers. Loop until they are all gone, in case
5225  * some trigger queues more for us to do.
5226  */
5227  while (afterTriggerMarkEvents(events, NULL, false))
5228  {
5229  CommandId firing_id = afterTriggers.firing_counter++;
5230 
5231  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5232  break; /* all fired */
5233  }
5234 
5235  /*
5236  * We don't bother freeing the event list, since it will go away anyway
5237  * (and more efficiently than via pfree) in AfterTriggerEndXact.
5238  */
5239 
5240  if (snap_pushed)
5242 }
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 4151 of file trigger.c.

4152 {
4154 
4155  while ((chunk = events->head) != NULL)
4156  {
4157  events->head = chunk->next;
4158  pfree(chunk);
4159  }
4160  events->tail = NULL;
4161  events->tailfree = NULL;
4162 }

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

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

References afterTriggerFreeEventList(), AfterTriggersQueryData::events, ExecDropSingleTupleTableSlot(), AfterTriggersQueryData::fdw_tuplestore, lfirst, list_free_deep(), AfterTriggersTableData::new_ins_tuplestore, AfterTriggersTableData::new_upd_tuplestore, NIL, AfterTriggersTableData::old_del_tuplestore, AfterTriggersTableData::old_upd_tuplestore, AfterTriggersTableData::storeslot, AfterTriggersQueryData::tables, and tuplestore_end().

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

◆ afterTriggerInvokeEvents()

static bool afterTriggerInvokeEvents ( AfterTriggerEventList events,
CommandId  firing_id,
EState estate,
bool  delete_ok 
)
static

Definition at line 4633 of file trigger.c.

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

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

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

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

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

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

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

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

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

References AFTER_TRIGGER_1CTID, AFTER_TRIGGER_2CTID, AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_INITDEFERRED, afterTriggerAddEvent(), 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 5665 of file trigger.c.

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

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

6586 {
6587  /*
6588  * Must flush the plan cache when changing replication role; but don't
6589  * flush unnecessarily.
6590  */
6592  ResetPlanCache();
6593 }
#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 6464 of file trigger.c.

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

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

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

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

References i, j, TriggerDesc::numtriggers, palloc(), pstrdup(), Trigger::tgargs, Trigger::tgattr, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgnewtable, Trigger::tgoldtable, Trigger::tgqual, and TriggerDesc::triggers.

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

◆ CreateTrigger()

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

Definition at line 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 = 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:2703
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3891
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4094
#define InvalidAttrNumber
Definition: attnum.h:23
static Datum values[MAXATTR]
Definition: bootstrap.c:150
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define NameStr(name)
Definition: c.h:737
bool IsSystemRelation(Relation relation)
Definition: catalog.c:73
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:419
void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior)
Definition: dependency.c:1553
@ DEPENDENCY_AUTO
Definition: dependency.h:34
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition: dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition: dependency.h:37
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errhint(const char *fmt,...)
Definition: elog.c:1317
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:641
bool allowSystemTableMods
Definition: globals.c:129
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c: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:1587
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:107
#define NoLock
Definition: lockdefs.h:34
#define ShareRowExclusiveLock
Definition: lockdefs.h:41
#define RowExclusiveLock
Definition: lockdefs.h:38
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2003
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1928
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1655
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:3594
#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:794
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
void assign_expr_collations(ParseState *pstate, Node *expr)
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
Definition: parse_func.c:2144
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:72
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:39
@ EXPR_KIND_TRIGGER_WHEN
Definition: parse_node.h:77
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
int attnameAttNum(Relation rd, const char *attname, bool sysColOK)
@ OBJECT_FUNCTION
Definition: parsenodes.h:2287
#define ACL_EXECUTE
Definition: parsenodes.h:83
#define ACL_TRIGGER
Definition: parsenodes.h:82
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:71
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:222
int16 attnum
Definition: pg_attribute.h:74
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
FormData_pg_class * Form_pg_class
Definition: pg_class.h: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, int16 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:45
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:301
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:377
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define snprintf
Definition: port.h: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:30
Node * whenClause
Definition: parsenodes.h:3042
ItemPointerData t_self
Definition: htup.h:65
Definition: nodes.h:129
const char * p_sourcetext
Definition: parse_node.h:209
List * p_rtable
Definition: parse_node.h:212
Oid rd_id
Definition: rel.h:113
Definition: primnodes.h:248
ParseLoc location
Definition: primnodes.h:293
AttrNumber varattno
Definition: primnodes.h:260
int varno
Definition: primnodes.h:255
Definition: c.h:706
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:91
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:612
Datum byteain(PG_FUNCTION_ARGS)
Definition: varlena.c:290
const char * name
void CommandCounterIncrement(void)
Definition: xact.c:1099

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

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

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

2781 {
2782  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2783 
2784  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2785  (transition_capture && transition_capture->tcs_delete_old_table))
2786  {
2787  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2788 
2789  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2790  if (fdw_trigtuple == NULL)
2791  GetTupleForTrigger(estate,
2792  NULL,
2793  relinfo,
2794  tupleid,
2796  slot,
2797  NULL,
2798  NULL,
2799  NULL);
2800  else
2801  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2802 
2803  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2805  true, slot, NULL, NIL, NULL,
2806  transition_capture,
2807  is_crosspart_update);
2808  }
2809 }
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:6067
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:3287

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

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

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

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

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

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

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

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

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

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

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

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

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