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/index.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/defrem.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/execPartition.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 "parser/parsetree.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.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 3613 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3614 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3615 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3608 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3612 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3611 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3609 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3607 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3616 of file trigger.c.

◆ CHUNK_DATA_START

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

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

◆ for_each_chunk_from

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

Definition at line 3717 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:3695
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3667
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3629

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

◆ GetTriggerSharedData

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

Definition at line 3676 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:3616
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3613
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3614
#define AFTER_TRIGGER_CP_UPDATE
Definition: trigger.c:3615

Definition at line 3667 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3629 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3617 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3629 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3629 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3629 of file trigger.c.

◆ SetConstraintState

Definition at line 3560 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3539 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3605 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 4019 of file trigger.c.

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

5007 {
5008  /* Increase the query stack depth */
5010 }

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5289 of file trigger.c.

5290 {
5291  int my_level = GetCurrentTransactionNestLevel();
5292 
5293  /*
5294  * Allocate more space in the trans_stack if needed. (Note: because the
5295  * minimum nest level of a subtransaction is 2, we waste the first couple
5296  * entries of the array; not worth the notational effort to avoid it.)
5297  */
5298  while (my_level >= afterTriggers.maxtransdepth)
5299  {
5300  if (afterTriggers.maxtransdepth == 0)
5301  {
5302  /* Arbitrarily initialize for max of 8 subtransaction levels */
5305  8 * sizeof(AfterTriggersTransData));
5307  }
5308  else
5309  {
5310  /* repalloc will keep the stack in the same context */
5311  int new_alloc = afterTriggers.maxtransdepth * 2;
5312 
5315  new_alloc * sizeof(AfterTriggersTransData));
5316  afterTriggers.maxtransdepth = new_alloc;
5317  }
5318  }
5319 
5320  /*
5321  * Push the current information into the stack. The SET CONSTRAINTS state
5322  * is not saved until/unless changed. Likewise, we don't make a
5323  * per-subtransaction event context until needed.
5324  */
5325  afterTriggers.trans_stack[my_level].state = NULL;
5329 }
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1476
CommandId firing_counter
Definition: trigger.c:3806
AfterTriggersTransData * trans_stack
Definition: trigger.c:3817
AfterTriggerEventList events
Definition: trigger.c:3808
AfterTriggerEventList events
Definition: trigger.c:3832
SetConstraintState state
Definition: trigger.c:3831
CommandId firing_counter
Definition: trigger.c:3834
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:914

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

4975 {
4976  /*
4977  * Initialize after-trigger state structure to empty
4978  */
4979  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4981 
4982  /*
4983  * Verify that there is no leftover state remaining. If these assertions
4984  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4985  * up properly.
4986  */
4987  Assert(afterTriggers.state == NULL);
4988  Assert(afterTriggers.query_stack == NULL);
4990  Assert(afterTriggers.event_cxt == NULL);
4991  Assert(afterTriggers.events.head == NULL);
4992  Assert(afterTriggers.trans_stack == NULL);
4994 }
uint32 CommandId
Definition: c.h:655
SetConstraintState state
Definition: trigger.c:3807
AfterTriggersQueryData * query_stack
Definition: trigger.c:3812

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

3943 {
3944  Oid tgoid = evtshared->ats_tgoid;
3946  int i;
3947 
3948  /*
3949  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3950  * constraints declared NOT DEFERRABLE), the state is always false.
3951  */
3952  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3953  return false;
3954 
3955  /*
3956  * If constraint state exists, SET CONSTRAINTS might have been executed
3957  * either for this trigger or for all triggers.
3958  */
3959  if (state != NULL)
3960  {
3961  /* Check for SET CONSTRAINTS for this specific trigger. */
3962  for (i = 0; i < state->numstates; i++)
3963  {
3964  if (state->trigstates[i].sct_tgoid == tgoid)
3965  return state->trigstates[i].sct_tgisdeferred;
3966  }
3967 
3968  /* Check for SET CONSTRAINTS ALL. */
3969  if (state->all_isset)
3970  return state->all_isdeferred;
3971  }
3972 
3973  /*
3974  * Otherwise return the default state for the trigger.
3975  */
3976  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3977 }
int i
Definition: isn.c:73
unsigned int Oid
Definition: postgres_ext.h:31
Definition: regguts.h:323
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:107
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:108

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

Referenced by afterTriggerMarkEvents().

◆ afterTriggerCopyBitmap()

static Bitmapset* afterTriggerCopyBitmap ( Bitmapset src)
static

Definition at line 3987 of file trigger.c.

3988 {
3989  Bitmapset *dst;
3990  MemoryContext oldcxt;
3991 
3992  if (src == NULL)
3993  return NULL;
3994 
3995  /* Create event context if we didn't already */
3996  if (afterTriggers.event_cxt == NULL)
3999  "AfterTriggerEvents",
4001 
4003 
4004  dst = bms_copy(src);
4005 
4006  MemoryContextSwitchTo(oldcxt);
4007 
4008  return dst;
4009 }
Bitmapset * bms_copy(const Bitmapset *a)
Definition: bitmapset.c:80
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138

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

4197 {
4198  AfterTriggerEventChunk *target = qs->events.head;
4199  ListCell *lc;
4200 
4201  Assert(target && target->next);
4202 
4203  /*
4204  * First, update any pointers in the per-table data, so that they won't be
4205  * dangling. Resetting obsoleted pointers to NULL will make
4206  * cancel_prior_stmt_triggers start from the list head, which is fine.
4207  */
4208  foreach(lc, qs->tables)
4209  {
4211 
4212  if (table->after_trig_done &&
4213  table->after_trig_events.tail == target)
4214  {
4215  table->after_trig_events.head = NULL;
4216  table->after_trig_events.tail = NULL;
4217  table->after_trig_events.tailfree = NULL;
4218  }
4219  }
4220 
4221  /* Now we can flush the head chunk */
4222  qs->events.head = target->next;
4223  pfree(target);
4224 }
void pfree(void *pointer)
Definition: mcxt.c:1456
#define lfirst(lc)
Definition: pg_list.h:172
AfterTriggerEventList events
Definition: trigger.c:3823
AfterTriggerEventList after_trig_events
Definition: trigger.c:3845

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

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

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

5338 {
5339  int my_level = GetCurrentTransactionNestLevel();
5341  AfterTriggerEvent event;
5342  AfterTriggerEventChunk *chunk;
5343  CommandId subxact_firing_id;
5344 
5345  /*
5346  * Pop the prior state if needed.
5347  */
5348  if (isCommit)
5349  {
5350  Assert(my_level < afterTriggers.maxtransdepth);
5351  /* If we saved a prior state, we don't need it anymore */
5352  state = afterTriggers.trans_stack[my_level].state;
5353  if (state != NULL)
5354  pfree(state);
5355  /* this avoids double pfree if error later: */
5356  afterTriggers.trans_stack[my_level].state = NULL;
5359  }
5360  else
5361  {
5362  /*
5363  * Aborting. It is possible subxact start failed before calling
5364  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5365  * trans_stack levels that aren't there.
5366  */
5367  if (my_level >= afterTriggers.maxtransdepth)
5368  return;
5369 
5370  /*
5371  * Release query-level storage for queries being aborted, and restore
5372  * query_depth to its pre-subxact value. This assumes that a
5373  * subtransaction will not add events to query levels started in a
5374  * earlier transaction state.
5375  */
5377  {
5381  }
5384 
5385  /*
5386  * Restore the global deferred-event list to its former length,
5387  * discarding any events queued by the subxact.
5388  */
5390  &afterTriggers.trans_stack[my_level].events);
5391 
5392  /*
5393  * Restore the trigger state. If the saved state is NULL, then this
5394  * subxact didn't save it, so it doesn't need restoring.
5395  */
5396  state = afterTriggers.trans_stack[my_level].state;
5397  if (state != NULL)
5398  {
5401  }
5402  /* this avoids double pfree if error later: */
5403  afterTriggers.trans_stack[my_level].state = NULL;
5404 
5405  /*
5406  * Scan for any remaining deferred events that were marked DONE or IN
5407  * PROGRESS by this subxact or a child, and un-mark them. We can
5408  * recognize such events because they have a firing ID greater than or
5409  * equal to the firing_counter value we saved at subtransaction start.
5410  * (This essentially assumes that the current subxact includes all
5411  * subxacts started after it.)
5412  */
5413  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5415  {
5416  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5417 
5418  if (event->ate_flags &
5420  {
5421  if (evtshared->ats_firing_id >= subxact_firing_id)
5422  event->ate_flags &=
5424  }
5425  }
5426  }
5427 }
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3609
#define GetTriggerSharedData(evt)
Definition: trigger.c:3676
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:4156
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3608
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3713

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AfterTriggerFreeQuery(), afterTriggerRestoreEventList(), afterTriggers, Assert(), AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_firing_id, AfterTriggersData::events, AfterTriggersTransData::events, AfterTriggersTransData::firing_counter, for_each_event_chunk, GetCurrentTransactionNestLevel(), GetTriggerSharedData, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, pfree(), AfterTriggersData::query_depth, AfterTriggersTransData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, AfterTriggersTransData::state, and AfterTriggersData::trans_stack.

Referenced by AbortSubTransaction(), and CommitSubTransaction().

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

Definition at line 5241 of file trigger.c.

5242 {
5243  /*
5244  * Forget the pending-events list.
5245  *
5246  * Since all the info is in TopTransactionContext or children thereof, we
5247  * don't really need to do anything to reclaim memory. However, the
5248  * pending-events list could be large, and so it's useful to discard it as
5249  * soon as possible --- especially if we are aborting because we ran out
5250  * of memory for the list!
5251  */
5253  {
5255  afterTriggers.event_cxt = NULL;
5256  afterTriggers.events.head = NULL;
5257  afterTriggers.events.tail = NULL;
5258  afterTriggers.events.tailfree = NULL;
5259  }
5260 
5261  /*
5262  * Forget any subtransaction state as well. Since this can't be very
5263  * large, we let the eventual reset of TopTransactionContext free the
5264  * memory instead of doing it here.
5265  */
5266  afterTriggers.trans_stack = NULL;
5268 
5269 
5270  /*
5271  * Forget the query stack and constraint-related state information. As
5272  * with the subtransaction state information, we don't bother freeing the
5273  * memory here.
5274  */
5275  afterTriggers.query_stack = NULL;
5277  afterTriggers.state = NULL;
5278 
5279  /* No more afterTriggers manipulation until next transaction starts. */
5281 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:403

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

5526 {
5527  int init_depth = afterTriggers.maxquerydepth;
5528 
5530 
5531  if (afterTriggers.maxquerydepth == 0)
5532  {
5533  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5534 
5537  new_alloc * sizeof(AfterTriggersQueryData));
5538  afterTriggers.maxquerydepth = new_alloc;
5539  }
5540  else
5541  {
5542  /* repalloc will keep the stack in the same context */
5543  int old_alloc = afterTriggers.maxquerydepth;
5544  int new_alloc = Max(afterTriggers.query_depth + 1,
5545  old_alloc * 2);
5546 
5549  new_alloc * sizeof(AfterTriggersQueryData));
5550  afterTriggers.maxquerydepth = new_alloc;
5551  }
5552 
5553  /* Initialize new array entries to empty */
5554  while (init_depth < afterTriggers.maxquerydepth)
5555  {
5557 
5558  qs->events.head = NULL;
5559  qs->events.tail = NULL;
5560  qs->events.tailfree = NULL;
5561  qs->fdw_tuplestore = NULL;
5562  qs->tables = NIL;
5563 
5564  ++init_depth;
5565  }
5566 }
#define Max(x, y)
Definition: c.h:987
#define NIL
Definition: pg_list.h:68
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3824

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

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

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

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

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

4136 {
4137  AfterTriggerEventChunk *chunk;
4138 
4139  while ((chunk = events->head) != NULL)
4140  {
4141  events->head = chunk->next;
4142  pfree(chunk);
4143  }
4144  events->tail = NULL;
4145  events->tailfree = NULL;
4146 }

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 5117 of file trigger.c.

5118 {
5119  Tuplestorestate *ts;
5120  List *tables;
5121  ListCell *lc;
5122 
5123  /* Drop the trigger events */
5125 
5126  /* Drop FDW tuplestore if any */
5127  ts = qs->fdw_tuplestore;
5128  qs->fdw_tuplestore = NULL;
5129  if (ts)
5130  tuplestore_end(ts);
5131 
5132  /* Release per-table subsidiary storage */
5133  tables = qs->tables;
5134  foreach(lc, tables)
5135  {
5137 
5138  ts = table->old_upd_tuplestore;
5139  table->old_upd_tuplestore = NULL;
5140  if (ts)
5141  tuplestore_end(ts);
5142  ts = table->new_upd_tuplestore;
5143  table->new_upd_tuplestore = NULL;
5144  if (ts)
5145  tuplestore_end(ts);
5146  ts = table->old_del_tuplestore;
5147  table->old_del_tuplestore = NULL;
5148  if (ts)
5149  tuplestore_end(ts);
5150  ts = table->new_ins_tuplestore;
5151  table->new_ins_tuplestore = NULL;
5152  if (ts)
5153  tuplestore_end(ts);
5154  if (table->storeslot)
5155  {
5156  TupleTableSlot *slot = table->storeslot;
5157 
5158  table->storeslot = NULL;
5160  }
5161  }
5162 
5163  /*
5164  * Now free the AfterTriggersTableData structs and list cells. Reset list
5165  * pointer first; if list_free_deep somehow gets an error, better to leak
5166  * that storage than have an infinite loop.
5167  */
5168  qs->tables = NIL;
5169  list_free_deep(tables);
5170 }
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1255
void list_free_deep(List *list)
Definition: list.c:1559
TupleTableSlot * storeslot
Definition: trigger.c:3863
Definition: pg_list.h:54
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4135
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453

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

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

◆ afterTriggerInvokeEvents()

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

Definition at line 4613 of file trigger.c.

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

References AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AFTER_TRIGGER_TUP_BITS, AfterTriggerExecute(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), AfterTriggerEventData::ate_dst_part, AfterTriggerEventData::ate_flags, AfterTriggerEventData::ate_src_part, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_relid, CHUNK_DATA_START, CreateExecutorState(), CurrentMemoryContext, elog(), AfterTriggerEventChunk::endfree, AfterTriggerEventChunk::endptr, ERROR, EState::es_tupleTable, ExecCloseResultRelations(), ExecDropSingleTupleTableSlot(), ExecGetTriggerResultRel(), ExecResetTupleTable(), for_each_chunk, for_each_event, FreeExecutorState(), AfterTriggerEventChunk::freeptr, GetTriggerSharedData, MakeSingleTupleTableSlot(), MemoryContextDelete(), OidIsValid, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, RelationHasReferenceCountZero, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TTSOpsMinimalTuple.

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

◆ afterTriggerMarkEvents()

static bool afterTriggerMarkEvents ( AfterTriggerEventList events,
AfterTriggerEventList move_list,
bool  immediate_only 
)
static

Definition at line 4529 of file trigger.c.

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

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

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

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

Definition at line 5962 of file trigger.c.

5963 {
5964  AfterTriggerEvent event;
5965  AfterTriggerEventChunk *chunk;
5966  int depth;
5967 
5968  /* Scan queued events */
5970  {
5971  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5972 
5973  /*
5974  * We can ignore completed events. (Even if a DONE flag is rolled
5975  * back by subxact abort, it's OK because the effects of the TRUNCATE
5976  * or whatever must get rolled back too.)
5977  */
5978  if (event->ate_flags & AFTER_TRIGGER_DONE)
5979  continue;
5980 
5981  if (evtshared->ats_relid == relid)
5982  return true;
5983  }
5984 
5985  /*
5986  * Also scan events queued by incomplete queries. This could only matter
5987  * if TRUNCATE/etc is executed by a function or trigger within an updating
5988  * query on the same relation, which is pretty perverse, but let's check.
5989  */
5990  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5991  {
5993  {
5994  AfterTriggerShared evtshared = GetTriggerSharedData(event);
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  return false;
6005 }

References AFTER_TRIGGER_DONE, afterTriggers, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_relid, AfterTriggersData::events, AfterTriggersQueryData::events, for_each_event_chunk, GetTriggerSharedData, AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.

Referenced by CheckTableNotInUse().

◆ afterTriggerRestoreEventList()

static void afterTriggerRestoreEventList ( AfterTriggerEventList events,
const AfterTriggerEventList old_events 
)
static

Definition at line 4156 of file trigger.c.

4158 {
4159  AfterTriggerEventChunk *chunk;
4160  AfterTriggerEventChunk *next_chunk;
4161 
4162  if (old_events->tail == NULL)
4163  {
4164  /* restoring to a completely empty state, so free everything */
4165  afterTriggerFreeEventList(events);
4166  }
4167  else
4168  {
4169  *events = *old_events;
4170  /* free any chunks after the last one we want to keep */
4171  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4172  {
4173  next_chunk = chunk->next;
4174  pfree(chunk);
4175  }
4176  /* and clean up the tail chunk to be the right length */
4177  events->tail->next = NULL;
4178  events->tail->freeptr = events->tailfree;
4179 
4180  /*
4181  * We don't make any effort to remove now-unused shared data records.
4182  * They might still be useful, anyway.
4183  */
4184  }
4185 }

References afterTriggerFreeEventList(), AfterTriggerEventChunk::freeptr, AfterTriggerEventChunk::next, pfree(), AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.

Referenced by AfterTriggerEndSubXact().

◆ AfterTriggerSaveEvent()

static void AfterTriggerSaveEvent ( EState estate,
ResultRelInfo relinfo,
ResultRelInfo src_partinfo,
ResultRelInfo dst_partinfo,
int  event,
bool  row_trigger,
TupleTableSlot oldslot,
TupleTableSlot newslot,
List recheckIndexes,
Bitmapset modifiedCols,
TransitionCaptureState transition_capture,
bool  is_crosspart_update 
)
static

Definition at line 6049 of file trigger.c.

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

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

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

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

6568 {
6569  /*
6570  * Must flush the plan cache when changing replication role; but don't
6571  * flush unnecessarily.
6572  */
6574  ResetPlanCache();
6575 }
#define newval
void ResetPlanCache(void)
Definition: plancache.c:2164
int SessionReplicationRole
Definition: trigger.c:70

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6446 of file trigger.c.

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

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

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

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

169 {
170  return
171  CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
172  constraintOid, indexOid, funcoid,
173  parentTriggerOid, whenClause, isInternal,
174  in_partition, TRIGGER_FIRES_ON_ORIGIN);
175 }
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:182
#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 182 of file trigger.c.

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

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

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

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

2786 {
2787  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2788 
2789  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2790  (transition_capture && transition_capture->tcs_delete_old_table))
2791  {
2792  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2793 
2794  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2795  if (fdw_trigtuple == NULL)
2796  GetTupleForTrigger(estate,
2797  NULL,
2798  relinfo,
2799  tupleid,
2801  slot,
2802  NULL,
2803  NULL,
2804  NULL);
2805  else
2806  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2807 
2808  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2810  true, slot, NULL, NIL, NULL,
2811  transition_capture,
2812  is_crosspart_update);
2813  }
2814 }
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1470
@ 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:6049
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:3274

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

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

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

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

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

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

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

3258 {
3259  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3260 
3261  if (trigdesc && trigdesc->trig_truncate_after_statement)
3262  AfterTriggerSaveEvent(estate, relinfo,
3263  NULL, NULL,
3265  false, NULL, NULL, NIL, NULL, NULL,
3266  false);
3267 }
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 2922 of file trigger.c.

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

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

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

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

2947 {
2948  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2949  TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
2950  HeapTuple newtuple = NULL;
2951  HeapTuple trigtuple;
2952  bool should_free_trig = false;
2953  bool should_free_new = false;
2954  TriggerData LocTriggerData = {0};
2955  int i;
2956  Bitmapset *updatedCols;
2957  LockTupleMode lockmode;
2958 
2959  /* Determine lock mode to use */
2960  lockmode = ExecUpdateLockMode(estate, relinfo);
2961 
2962  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2963  if (fdw_trigtuple == NULL)
2964  {
2965  TupleTableSlot *epqslot_candidate = NULL;
2966 
2967  /* get a copy of the on-disk tuple we are planning to update */
2968  if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2969  lockmode, oldslot, &epqslot_candidate,
2970  tmresult, tmfd))
2971  return false; /* cancel the update action */
2972 
2973  /*
2974  * In READ COMMITTED isolation level it's possible that target tuple
2975  * was changed due to concurrent update. In that case we have a raw
2976  * subplan output tuple in epqslot_candidate, and need to form a new
2977  * insertable tuple using ExecGetUpdateNewTuple to replace the one we
2978  * received in newslot. Neither we nor our callers have any further
2979  * interest in the passed-in tuple, so it's okay to overwrite newslot
2980  * with the newer data.
2981  *
2982  * (Typically, newslot was also generated by ExecGetUpdateNewTuple, so
2983  * that epqslot_clean will be that same slot and the copy step below
2984  * is not needed.)
2985  */
2986  if (epqslot_candidate != NULL)
2987  {
2988  TupleTableSlot *epqslot_clean;
2989 
2990  epqslot_clean = ExecGetUpdateNewTuple(relinfo, epqslot_candidate,
2991  oldslot);</