PostgreSQL Source Code  git master
trigger.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/partition.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/bitmapset.h"
#include "nodes/makefuncs.h"
#include "optimizer/optimizer.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteManip.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc_hooks.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/plancache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tuplestore.h"
Include dependency graph for trigger.c:

Go to the source code of this file.

Data Structures

struct  SetConstraintTriggerData
 
struct  SetConstraintStateData
 
struct  AfterTriggerSharedData
 
struct  AfterTriggerEventData
 
struct  AfterTriggerEventDataNoOids
 
struct  AfterTriggerEventDataOneCtid
 
struct  AfterTriggerEventDataZeroCtids
 
struct  AfterTriggerEventChunk
 
struct  AfterTriggerEventList
 
struct  AfterTriggersData
 
struct  AfterTriggersQueryData
 
struct  AfterTriggersTransData
 
struct  AfterTriggersTableData
 

Macros

#define AFTER_TRIGGER_OFFSET   0x07FFFFFF /* must be low-order bits */
 
#define AFTER_TRIGGER_DONE   0x80000000
 
#define AFTER_TRIGGER_IN_PROGRESS   0x40000000
 
#define AFTER_TRIGGER_FDW_REUSE   0x00000000
 
#define AFTER_TRIGGER_FDW_FETCH   0x20000000
 
#define AFTER_TRIGGER_1CTID   0x10000000
 
#define AFTER_TRIGGER_2CTID   0x30000000
 
#define AFTER_TRIGGER_CP_UPDATE   0x08000000
 
#define AFTER_TRIGGER_TUP_BITS   0x38000000
 
#define SizeofTriggerEvent(evt)
 
#define GetTriggerSharedData(evt)    ((AfterTriggerShared) ((char *) (evt) + ((evt)->ate_flags & AFTER_TRIGGER_OFFSET)))
 
#define CHUNK_DATA_START(cptr)   ((char *) (cptr) + MAXALIGN(sizeof(AfterTriggerEventChunk)))
 
#define for_each_chunk(cptr, evtlist)    for (cptr = (evtlist).head; cptr != NULL; cptr = cptr->next)
 
#define for_each_event(eptr, cptr)
 
#define for_each_event_chunk(eptr, cptr, evtlist)    for_each_chunk(cptr, evtlist) for_each_event(eptr, cptr)
 
#define for_each_chunk_from(cptr)    for (; cptr != NULL; cptr = cptr->next)
 
#define for_each_event_from(eptr, cptr)
 
#define MIN_CHUNK_SIZE   1024
 
#define MAX_CHUNK_SIZE   (1024*1024)
 

Typedefs

typedef struct SetConstraintTriggerData SetConstraintTriggerData
 
typedef struct SetConstraintTriggerDataSetConstraintTrigger
 
typedef struct SetConstraintStateData SetConstraintStateData
 
typedef SetConstraintStateDataSetConstraintState
 
typedef uint32 TriggerFlags
 
typedef struct AfterTriggerSharedDataAfterTriggerShared
 
typedef struct AfterTriggerSharedData AfterTriggerSharedData
 
typedef struct AfterTriggerEventDataAfterTriggerEvent
 
typedef struct AfterTriggerEventData AfterTriggerEventData
 
typedef struct AfterTriggerEventDataNoOids AfterTriggerEventDataNoOids
 
typedef struct AfterTriggerEventDataOneCtid AfterTriggerEventDataOneCtid
 
typedef struct AfterTriggerEventDataZeroCtids AfterTriggerEventDataZeroCtids
 
typedef struct AfterTriggerEventChunk AfterTriggerEventChunk
 
typedef struct AfterTriggerEventList AfterTriggerEventList
 
typedef struct AfterTriggersQueryData AfterTriggersQueryData
 
typedef struct AfterTriggersTransData AfterTriggersTransData
 
typedef struct AfterTriggersTableData AfterTriggersTableData
 
typedef struct AfterTriggersData AfterTriggersData
 

Functions

static void renametrig_internal (Relation tgrel, Relation targetrel, HeapTuple trigtup, const char *newname, const char *expected_name)
 
static void renametrig_partition (Relation tgrel, Oid partitionId, Oid parentTriggerOid, const char *newname, const char *expected_name)
 
static void SetTriggerFlags (TriggerDesc *trigdesc, Trigger *trigger)
 
static bool GetTupleForTrigger (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **epqslot, TM_Result *tmresultp, TM_FailureData *tmfdp)
 
static bool TriggerEnabled (EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
static HeapTuple ExecCallTriggerFunc (TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
 
static void AfterTriggerSaveEvent (EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldslot, TupleTableSlot *newslot, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
static void AfterTriggerEnlargeQueryState (void)
 
static bool before_stmt_triggers_fired (Oid relid, CmdType cmdType)
 
ObjectAddress CreateTrigger (CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
 
ObjectAddress CreateTriggerFiringOn (CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
 
void TriggerSetParentTrigger (Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
 
void RemoveTriggerById (Oid trigOid)
 
Oid get_trigger_oid (Oid relid, const char *trigname, bool missing_ok)
 
static void RangeVarCallbackForRenameTrigger (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
ObjectAddress renametrig (RenameStmt *stmt)
 
void EnableDisableTrigger (Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
 
void RelationBuildTriggers (Relation relation)
 
TriggerDescCopyTriggerDesc (TriggerDesc *trigdesc)
 
void FreeTriggerDesc (TriggerDesc *trigdesc)
 
const char * FindTriggerIncompatibleWithInheritance (TriggerDesc *trigdesc)
 
void ExecBSInsertTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASInsertTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecARInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
bool ExecIRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecBSDeleteTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASDeleteTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRDeleteTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot, TM_Result *tmresult, TM_FailureData *tmfd)
 
void ExecARDeleteTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
bool ExecIRDeleteTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
 
void ExecBSUpdateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASUpdateTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRUpdateTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, TM_Result *tmresult, TM_FailureData *tmfd)
 
void ExecARUpdateTriggers (EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
bool ExecIRUpdateTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
 
void ExecBSTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
static void AfterTriggerExecute (EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, ResultRelInfo *src_relInfo, ResultRelInfo *dst_relInfo, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
 
static AfterTriggersTableDataGetAfterTriggersTableData (Oid relid, CmdType cmdType)
 
static TupleTableSlotGetAfterTriggersStoreSlot (AfterTriggersTableData *table, TupleDesc tupdesc)
 
static TuplestorestateGetAfterTriggersTransitionTable (int event, TupleTableSlot *oldslot, TupleTableSlot *newslot, TransitionCaptureState *transition_capture)
 
static void TransitionTableAddTuple (EState *estate, TransitionCaptureState *transition_capture, ResultRelInfo *relinfo, TupleTableSlot *slot, TupleTableSlot *original_insert_tuple, Tuplestorestate *tuplestore)
 
static void AfterTriggerFreeQuery (AfterTriggersQueryData *qs)
 
static SetConstraintState SetConstraintStateCreate (int numalloc)
 
static SetConstraintState SetConstraintStateCopy (SetConstraintState origstate)
 
static SetConstraintState SetConstraintStateAddItem (SetConstraintState state, Oid tgoid, bool tgisdeferred)
 
static void cancel_prior_stmt_triggers (Oid relid, CmdType cmdType, int tgevent)
 
static TuplestorestateGetCurrentFDWTuplestore (void)
 
static bool afterTriggerCheckState (AfterTriggerShared evtshared)
 
static BitmapsetafterTriggerCopyBitmap (Bitmapset *src)
 
static void afterTriggerAddEvent (AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
 
static void afterTriggerFreeEventList (AfterTriggerEventList *events)
 
static void afterTriggerRestoreEventList (AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
 
static void afterTriggerDeleteHeadEventChunk (AfterTriggersQueryData *qs)
 
static bool afterTriggerMarkEvents (AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
 
static bool afterTriggerInvokeEvents (AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
 
TransitionCaptureStateMakeTransitionCaptureState (TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
 
void AfterTriggerBeginXact (void)
 
void AfterTriggerBeginQuery (void)
 
void AfterTriggerEndQuery (EState *estate)
 
void AfterTriggerFireDeferred (void)
 
void AfterTriggerEndXact (bool isCommit)
 
void AfterTriggerBeginSubXact (void)
 
void AfterTriggerEndSubXact (bool isCommit)
 
void AfterTriggerSetState (ConstraintsSetStmt *stmt)
 
bool AfterTriggerPendingOnRel (Oid relid)
 
void assign_session_replication_role (int newval, void *extra)
 
Datum pg_trigger_depth (PG_FUNCTION_ARGS)
 

Variables

int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN
 
static int MyTriggerDepth = 0
 
static AfterTriggersData afterTriggers
 

Macro Definition Documentation

◆ AFTER_TRIGGER_1CTID

#define AFTER_TRIGGER_1CTID   0x10000000

Definition at line 3625 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3626 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3627 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3620 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3624 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3623 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3621 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3619 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3628 of file trigger.c.

◆ CHUNK_DATA_START

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

Definition at line 3707 of file trigger.c.

◆ for_each_chunk

#define for_each_chunk (   cptr,
  evtlist 
)     for (cptr = (evtlist).head; cptr != NULL; cptr = cptr->next)

Definition at line 3718 of file trigger.c.

◆ for_each_chunk_from

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

Definition at line 3729 of file trigger.c.

◆ for_each_event

#define for_each_event (   eptr,
  cptr 
)
Value:
for (eptr = (AfterTriggerEvent) CHUNK_DATA_START(cptr); \
(char *) eptr < (cptr)->freeptr; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3707
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3679
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3641

Definition at line 3720 of file trigger.c.

◆ for_each_event_chunk

#define for_each_event_chunk (   eptr,
  cptr,
  evtlist 
)     for_each_chunk(cptr, evtlist) for_each_event(eptr, cptr)

Definition at line 3725 of file trigger.c.

◆ for_each_event_from

#define for_each_event_from (   eptr,
  cptr 
)
Value:
for (; \
(char *) eptr < (cptr)->freeptr; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))

Definition at line 3731 of file trigger.c.

◆ GetTriggerSharedData

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

Definition at line 3688 of file trigger.c.

◆ MAX_CHUNK_SIZE

#define MAX_CHUNK_SIZE   (1024*1024)

◆ MIN_CHUNK_SIZE

#define MIN_CHUNK_SIZE   1024

◆ SizeofTriggerEvent

#define SizeofTriggerEvent (   evt)
Value:
(((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_CP_UPDATE ? \
(((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_2CTID ? \
(((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_1CTID ? \
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3628
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3625
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3626
#define AFTER_TRIGGER_CP_UPDATE
Definition: trigger.c:3627

Definition at line 3679 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3641 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3629 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3641 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3641 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3641 of file trigger.c.

◆ SetConstraintState

Definition at line 3572 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3551 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3617 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 4031 of file trigger.c.

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

References AFTER_TRIGGER_OFFSET, afterTriggers, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_relid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, CHUNK_DATA_START, AfterTriggersData::event_cxt, AfterTriggerEventList::head, MAX_CHUNK_SIZE, MemoryContextAlloc(), Min, MIN_CHUNK_SIZE, AfterTriggerEventChunk::next, SizeofTriggerEvent, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TopTransactionContext.

Referenced by afterTriggerMarkEvents(), and AfterTriggerSaveEvent().

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 5018 of file trigger.c.

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

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5301 of file trigger.c.

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

References afterTriggers, AfterTriggersData::events, AfterTriggersTransData::events, AfterTriggersData::firing_counter, AfterTriggersTransData::firing_counter, GetCurrentTransactionNestLevel(), AfterTriggersData::maxtransdepth, MemoryContextAlloc(), AfterTriggersData::query_depth, AfterTriggersTransData::query_depth, repalloc(), AfterTriggersTransData::state, TopTransactionContext, and AfterTriggersData::trans_stack.

Referenced by StartSubTransaction().

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

Definition at line 4986 of file trigger.c.

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

References afterTriggers, Assert(), AfterTriggersData::event_cxt, AfterTriggersData::events, AfterTriggersData::firing_counter, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, and AfterTriggersData::trans_stack.

Referenced by StartTransaction().

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

Definition at line 3954 of file trigger.c.

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

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

Referenced by afterTriggerMarkEvents().

◆ afterTriggerCopyBitmap()

static Bitmapset* afterTriggerCopyBitmap ( Bitmapset src)
static

Definition at line 3999 of file trigger.c.

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

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

Referenced by AfterTriggerSaveEvent().

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 4208 of file trigger.c.

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

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

Referenced by AfterTriggerEndQuery().

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 5038 of file trigger.c.

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

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

Definition at line 5349 of file trigger.c.

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

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

Referenced by AbortSubTransaction(), and CommitSubTransaction().

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

Definition at line 5253 of file trigger.c.

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

References afterTriggers, AfterTriggersData::event_cxt, AfterTriggersData::events, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, MemoryContextDelete(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and AfterTriggersData::trans_stack.

Referenced by AbortTransaction(), CommitTransaction(), and PrepareTransaction().

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

Definition at line 5537 of file trigger.c.

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

References afterTriggers, Assert(), AfterTriggersQueryData::events, AfterTriggersQueryData::fdw_tuplestore, AfterTriggerEventList::head, Max, AfterTriggersData::maxquerydepth, MemoryContextAlloc(), NIL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, repalloc(), AfterTriggersQueryData::tables, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TopTransactionContext.

Referenced by AfterTriggerSaveEvent(), before_stmt_triggers_fired(), and MakeTransitionCaptureState().

◆ AfterTriggerExecute()

static void AfterTriggerExecute ( EState estate,
AfterTriggerEvent  event,
ResultRelInfo relInfo,
ResultRelInfo src_relInfo,
ResultRelInfo dst_relInfo,
TriggerDesc trigdesc,
FmgrInfo finfo,
Instrumentation instr,
MemoryContext  per_tuple_context,
TupleTableSlot trig_tuple_slot1,
TupleTableSlot trig_tuple_slot2 
)
static

Definition at line 4270 of file trigger.c.

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

References AFTER_TRIGGER_2CTID, AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_TUP_BITS, AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_modifiedcols, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, TupleConversionMap::attrMap, AfterTriggersTableData::closed, elog, ERROR, ExecCallTriggerFunc(), ExecClearTuple(), ExecCopySlot(), ExecFetchSlotHeapTuple(), ExecGetChildToRootMap(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), execute_attr_map_slot(), GetCurrentFDWTuplestore(), GetTriggerSharedData, heap_freetuple(), InstrStartNode(), InstrStopNode(), ItemPointerIsValid(), MemoryContextReset(), AfterTriggersTableData::new_ins_tuplestore, AfterTriggersTableData::new_upd_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_del_tuplestore, AfterTriggersTableData::old_upd_tuplestore, ResultRelInfo::ri_RelationDesc, SnapshotAny, table_tuple_fetch_row_version(), TriggerData::tg_event, TriggerData::tg_newslot, TriggerData::tg_newtable, TriggerData::tg_newtuple, TriggerData::tg_oldtable, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, TriggerData::tg_updatedcols, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgtype, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, TriggerDesc::triggers, tuplestore_gettupleslot(), and TriggerData::type.

Referenced by afterTriggerInvokeEvents().

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

Definition at line 5197 of file trigger.c.

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

References afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, Assert(), AfterTriggersData::events, AfterTriggersData::firing_counter, GetTransactionSnapshot(), AfterTriggerEventList::head, PopActiveSnapshot(), PushActiveSnapshot(), and AfterTriggersData::query_depth.

Referenced by CommitTransaction(), and PrepareTransaction().

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4147 of file trigger.c.

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

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

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

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

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

◆ afterTriggerInvokeEvents()

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

Definition at line 4625 of file trigger.c.

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

References AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AFTER_TRIGGER_TUP_BITS, AfterTriggerExecute(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), AfterTriggerEventData::ate_dst_part, AfterTriggerEventData::ate_flags, AfterTriggerEventData::ate_src_part, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_relid, CHUNK_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 4541 of file trigger.c.

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

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

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

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

Definition at line 5974 of file trigger.c.

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

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

Referenced by CheckTableNotInUse().

◆ afterTriggerRestoreEventList()

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

Definition at line 4168 of file trigger.c.

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

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

Referenced by AfterTriggerEndSubXact().

◆ AfterTriggerSaveEvent()

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

Definition at line 6061 of file trigger.c.

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

References AFTER_TRIGGER_1CTID, AFTER_TRIGGER_2CTID, AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_INITDEFERRED, afterTriggerAddEvent(), afterTriggerCopyBitmap(), AfterTriggerEnlargeQueryState(), afterTriggers, Assert(), AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_dst_part, AfterTriggerEventData::ate_flags, AfterTriggerEventData::ate_src_part, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_modifiedcols, AfterTriggerSharedData::ats_relid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, TupleConversionMap::attrMap, cancel_prior_stmt_triggers(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, AfterTriggersQueryData::events, ExecCopySlot(), ExecGetChildToRootMap(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), execute_attr_map_slot(), GetAfterTriggersTransitionTable(), GetCurrentFDWTuplestore(), i, ItemPointerCopy(), ItemPointerSetInvalid(), list_member_oid(), AfterTriggersData::maxquerydepth, TriggerDesc::numtriggers, AfterTriggersData::query_depth, AfterTriggersData::query_stack, RelationData::rd_rel, RelationGetRelid, RI_FKey_fk_upd_check_required(), RI_FKey_pk_upd_check_required(), RI_FKey_trigger_type(), ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, RI_TRIGGER_FK, RI_TRIGGER_NONE, RI_TRIGGER_PK, TransitionCaptureState::tcs_original_insert_tuple, TransitionCaptureState::tcs_private, Trigger::tgconstrindid, Trigger::tgdeferrable, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgisclone, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgtype, TransitionTableAddTuple(), TriggerDesc::trig_delete_after_row, TriggerDesc::trig_insert_after_row, TriggerDesc::trig_update_after_row, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_TRUNCATE, TRIGGER_EVENT_UPDATE, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tid, TupIsNull, and tuplestore_puttupleslot().

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

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5659 of file trigger.c.

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

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, BTEqualStrategyNumber, RangeVar::catalogname, CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, AfterTriggersData::events, fetch_search_path(), AfterTriggersData::firing_counter, get_database_name(), GetCurrentTransactionNestLevel(), GETSTRUCT, GetTransactionSnapshot(), HeapTupleIsValid, i, IsSubTransaction(), sort-test::key, lappend_oid(), lfirst, lfirst_oid, list_free(), list_make1_oid, LookupExplicitNamespace(), MyDatabaseId, NIL, SetConstraintStateData::numstates, ObjectIdGetDatum(), PopActiveSnapshot(), PushActiveSnapshot(), RangeVar::relname, ScanKeyInit(), RangeVar::schemaname, SetConstraintStateAddItem(), SetConstraintStateCopy(), SetConstraintStateCreate(), AfterTriggersData::state, AfterTriggersTransData::state, stmt, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and AfterTriggersData::trans_stack.

Referenced by standard_ProcessUtility().

◆ assign_session_replication_role()

void assign_session_replication_role ( int  newval,
void *  extra 
)

Definition at line 6579 of file trigger.c.

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

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6458 of file trigger.c.

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

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

Referenced by ExecBSDeleteTriggers(), ExecBSInsertTriggers(), and ExecBSUpdateTriggers().

◆ cancel_prior_stmt_triggers()

static void cancel_prior_stmt_triggers ( Oid  relid,
CmdType  cmdType,
int  tgevent 
)
static

Definition at line 6504 of file trigger.c.

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

References AfterTriggersTableData::after_trig_done, AfterTriggersTableData::after_trig_events, AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, afterTriggers, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_relid, CHUNK_DATA_START, AfterTriggersQueryData::events, for_each_chunk_from, for_each_event_from, GetAfterTriggersTableData(), GetTriggerSharedData, AfterTriggerEventList::head, AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, TRIGGER_EVENT_OPMASK, TRIGGER_FIRED_AFTER, and TRIGGER_FIRED_FOR_STATEMENT.

Referenced by AfterTriggerSaveEvent().

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

Definition at line 2085 of file trigger.c.

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

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

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

◆ CreateTrigger()

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

Definition at line 158 of file trigger.c.

162 {
163  return
164  CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
165  constraintOid, indexOid, funcoid,
166  parentTriggerOid, whenClause, isInternal,
167  in_partition, TRIGGER_FIRES_ON_ORIGIN);
168 }
ObjectAddress CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition: trigger.c:175
#define TRIGGER_FIRES_ON_ORIGIN
Definition: trigger.h:149

References CreateTriggerFiringOn(), stmt, and TRIGGER_FIRES_ON_ORIGIN.

Referenced by CreateFKCheckTrigger(), createForeignKeyActionTriggers(), index_constraint_create(), and ProcessUtilitySlow().

◆ CreateTriggerFiringOn()

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

Definition at line 175 of file trigger.c.

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

References AccessShareLock, ACL_EXECUTE, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addNSItemToQuery(), addRangeTableEntryForRelation(), ALLOCSET_SMALL_SIZES, AllocSetContextCreate, allowSystemTableMods, generate_unaccent_rules::args, Assert(), assign_expr_collations(), attnameAttNum(), attnum, BoolGetDatum(), BTEqualStrategyNumber, buildint2vector(), byteain(), CacheInvalidateRelcacheByTuple(), CatalogTupleInsert(), CatalogTupleUpdate(), ObjectAddress::classId, CommandCounterIncrement(), copyObject, CreateConstraintEntry(), CStringGetDatum(), CStringGetTextDatum, CurrentMemoryContext, DatumGetPointer(), deleteDependencyRecordsFor(), DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, DirectFunctionCall1, elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errdetail_relkind_not_supported(), errhint(), errmsg(), ERROR, EXPR_KIND_TRIGGER_WHEN, find_all_inheritors(), free_parsestate(), CreateTrigStmt::funcname, get_func_rettype(), get_rel_name(), get_rel_relkind(), get_relkind_objtype(), GetNewOidWithIndex(), GETSTRUCT, GetUserId(), has_superclass(), heap_copytuple(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, Int16GetDatum(), InvalidAttrNumber, InvalidOid, InvokeObjectPostCreateHookArg, TriggerTransition::isNew, IsSystemRelation(), TriggerTransition::isTable, j, len, lfirst, lfirst_node, list_free(), list_length(), Var::location, LockRelationOid(), LookupFuncName(), make_parsestate(), makeAlias(), map_partition_varattnos(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), name, TriggerTransition::name, NAMEDATALEN, namein(), NameListToString(), NameStr, NIL, nodeToString(), NoLock, PartitionDescData::nparts, object_aclcheck(), OBJECT_FUNCTION, ObjectAddressSet, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, OidIsValid, PartitionDescData::oids, ParseState::p_rtable, ParseState::p_sourcetext, palloc(), parser_errposition(), pfree(), pg_class_aclcheck(), PointerGetDatum(), PRS2_NEW_VARNO, PRS2_OLD_VARNO, pull_var_clause(), RangeVarGetRelid, RelationData::rd_att, RelationData::rd_id, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnExpr(), RelationGetDescr, RelationGetNamespace, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, ShareRowExclusiveLock, snprintf, stmt, strVal, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), table_openrv(), transformWhereClause(), TupleDescAttr, values, Var::varattno, Var::varno, and CreateTrigStmt::whenClause.

Referenced by CloneRowTriggersToPartition(), and CreateTrigger().

◆ EnableDisableTrigger()

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

Definition at line 1721 of file trigger.c.

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

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, i, InvokeObjectPostAlterHook, NameStr, NoLock, PartitionDescData::nparts, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, RelationData::rd_rel, relation_open(), RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), superuser(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecEnableDisableTrigger().

◆ ExecARDeleteTriggers()

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

Definition at line 2774 of file trigger.c.

2780 {
2781  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2782 
2783  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2784  (transition_capture && transition_capture->tcs_delete_old_table))
2785  {
2786  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2787 
2788  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2789  if (fdw_trigtuple == NULL)
2790  GetTupleForTrigger(estate,
2791  NULL,
2792  relinfo,
2793  tupleid,
2795  slot,
2796  NULL,
2797  NULL,
2798  NULL);
2799  else
2800  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2801 
2802  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2804  true, slot, NULL, NIL, NULL,
2805  transition_capture,
2806  is_crosspart_update);
2807  }
2808 }
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1468
@ LockTupleExclusive
Definition: lockoptions.h:58
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldslot, TupleTableSlot *newslot, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition: trigger.c:6061
static bool GetTupleForTrigger(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **epqslot, TM_Result *tmresultp, TM_FailureData *tmfdp)
Definition: trigger.c:3286

References AfterTriggerSaveEvent(), Assert(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), GetTupleForTrigger(), HeapTupleIsValid, ItemPointerIsValid(), LockTupleExclusive, NIL, ResultRelInfo::ri_TrigDesc, TransitionCaptureState::tcs_delete_old_table, TriggerDesc::trig_delete_after_row, and TRIGGER_EVENT_DELETE.

Referenced by ExecDeleteEpilogue(), and ExecSimpleRelationDelete().

◆ ExecARInsertTriggers()

void ExecARInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot,
List recheckIndexes,
TransitionCaptureState transition_capture 
)

Definition at line 2534 of file trigger.c.

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

References AfterTriggerSaveEvent(), ResultRelInfo::ri_TrigDesc, TransitionCaptureState::tcs_insert_new_table, TriggerDesc::trig_insert_after_row, and TRIGGER_EVENT_INSERT.

Referenced by CopyFrom(), CopyMultiInsertBufferFlush(), ExecBatchInsert(), ExecInsert(), and ExecSimpleRelationInsert().

◆ ExecARUpdateTriggers()

void ExecARUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
ResultRelInfo src_partinfo,
ResultRelInfo dst_partinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot newslot,
List recheckIndexes,
TransitionCaptureState transition_capture,
bool  is_crosspart_update 
)

Definition at line 3097 of file trigger.c.

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

References AfterTriggerSaveEvent(), Assert(), ExecClearTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), GetTupleForTrigger(), ItemPointerIsValid(), LockTupleExclusive, ResultRelInfo::ri_TrigDesc, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, TriggerDesc::trig_update_after_row, and TRIGGER_EVENT_UPDATE.

Referenced by ExecCrossPartitionUpdateForeignKey(), ExecDeleteEpilogue(), ExecInsert(), ExecSimpleRelationUpdate(), and ExecUpdateEpilogue().

◆ ExecASDeleteTriggers()

void ExecASDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)

Definition at line 2663 of file trigger.c.

2665 {
2666  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2667 
2668  if (trigdesc && trigdesc->trig_delete_after_statement)
2669  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2671  false, NULL, NULL, NIL, NULL, transition_capture,
2672  false);
2673 }
bool trig_delete_after_statement
Definition: reltrigger.h:70

References AfterTriggerSaveEvent(), NIL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_delete_after_statement, and TRIGGER_EVENT_DELETE.

Referenced by fireASTriggers().

◆ ExecASInsertTriggers()

void ExecASInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)

Definition at line 2445 of file trigger.c.

2447 {
2448  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2449 
2450  if (trigdesc && trigdesc->trig_insert_after_statement)
2451  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2453  false, NULL, NULL, NIL, NULL, transition_capture,
2454  false);
2455 }
bool trig_insert_after_statement
Definition: reltrigger.h:60

References AfterTriggerSaveEvent(), NIL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_insert_after_statement, and TRIGGER_EVENT_INSERT.

Referenced by CopyFrom(), and fireASTriggers().

◆ ExecASTruncateTriggers()

void ExecASTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 3269 of file trigger.c.

3270 {
3271  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3272 
3273  if (trigdesc && trigdesc->trig_truncate_after_statement)
3274  AfterTriggerSaveEvent(estate, relinfo,
3275  NULL, NULL,
3277  false, NULL, NULL, NIL, NULL, NULL,
3278  false);
3279 }
bool trig_truncate_after_statement
Definition: reltrigger.h:73

References AfterTriggerSaveEvent(), NIL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_truncate_after_statement, and TRIGGER_EVENT_TRUNCATE.

Referenced by ExecuteTruncateGuts().

◆ ExecASUpdateTriggers()

void ExecASUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)

Definition at line 2916 of file trigger.c.

2918 {
2919  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2920 
2921  /* statement-level triggers operate on the parent table */
2922  Assert(relinfo->ri_RootResultRelInfo == NULL);
2923 
2924  if (trigdesc && trigdesc->trig_update_after_statement)
2925  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2927  false, NULL, NULL, NIL,
2928  ExecGetAllUpdatedCols(relinfo, estate),
2929  transition_capture,
2930  false);
2931 }
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:580
bool trig_update_after_statement
Definition: reltrigger.h:65

References AfterTriggerSaveEvent(), Assert(), ExecGetAllUpdatedCols(), NIL, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_update_after_statement, and TRIGGER_EVENT_UPDATE.

Referenced by fireASTriggers().

◆ ExecBRDeleteTriggers()

bool ExecBRDeleteTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot **  epqslot,
TM_Result tmresult,
TM_FailureData tmfd 
)

Definition at line 2683 of file trigger.c.

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

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

Referenced by ExecDeletePrologue(), and ExecSimpleRelationDelete().

◆ ExecBRInsertTriggers()

bool ExecBRInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot 
)

Definition at line 2458 of file trigger.c.

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

References ereport, errcode(), errdetail(), errmsg(), ERROR, ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecPartitionCheck(), get_namespace_name(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, RelationGetNamespace, RelationGetRelationName, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgisclone, Trigger::tgname, Trigger::tgtype, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by CopyFrom(), ExecInsert(), and ExecSimpleRelationInsert().

◆ ExecBRUpdateTriggers()

bool ExecBRUpdateTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot newslot,
TM_Result tmresult,
TM_FailureData tmfd 
)

Definition at line 2934 of file trigger.c.

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

References Assert(), ExecCallTriggerFunc(), ExecCopySlot(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), ExecGetUpdateNewTuple(), ExecMaterializeSlot(), ExecUpdateLockMode(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), HeapTupleIsValid, i, ItemPointerIsValid(), TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newslot, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, TriggerData::tg_updatedcols, Trigger::tgtype, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TriggerData::type, and unlikely.

Referenced by ExecSimpleRelationUpdate(), and ExecUpdatePrologue().

◆ ExecBSDeleteTriggers()

void ExecBSDeleteTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2612 of file trigger.c.

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

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

Referenced by fireBSTriggers().

◆ ExecBSInsertTriggers()

void ExecBSInsertTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2394 of file trigger.c.

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

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

Referenced by CopyFrom(), and fireBSTriggers().

◆ ExecBSTruncateTriggers()

void ExecBSTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 3222 of file trigger.c.

3223 {
3224  TriggerDesc *trigdesc;
3225  int i;
3226  TriggerData LocTriggerData = {0};
3227 
3228  trigdesc = relinfo->ri_TrigDesc;
3229 
3230  if (trigdesc == NULL)
3231  return;
3232  if (!trigdesc->trig_truncate_before_statement)
3233  return;
3234 
3235  LocTriggerData.type = T_TriggerData;
3236  LocTriggerData.tg_event = TRIGGER_EVENT_TRUNCATE |
3238  LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3239 
3240  for (i = 0; i < trigdesc->numtriggers; i++)
3241  {
3242  Trigger *trigger = &trigdesc->triggers[i];
3243  HeapTuple newtuple;
3244 
3245  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3246  TRIGGER_TYPE_STATEMENT,
3247  TRIGGER_TYPE_BEFORE,
3248  TRIGGER_TYPE_TRUNCATE))
3249  continue;
3250  if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3251  NULL, NULL, NULL))
3252  continue;
3253 
3254  LocTriggerData.tg_trigger = trigger;
3255  newtuple = ExecCallTriggerFunc(&LocTriggerData,
3256  i,
3257  relinfo->ri_TrigFunctions,
3258  relinfo->ri_TrigInstrument,
3259  GetPerTupleMemoryContext(estate));
3260 
3261  if (newtuple)
3262  ereport(ERROR,
3263  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3264  errmsg("BEFORE STATEMENT trigger cannot return a value")));
3265  }
3266 }
bool trig_truncate_before_statement
Definition: reltrigger.h:72

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

Referenced by ExecuteTruncateGuts().

◆ ExecBSUpdateTriggers()

void ExecBSUpdateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 2858 of file trigger.c.

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

References Assert(), before_stmt_triggers_fired(), CMD_UPDATE, ereport, errcode(), errmsg(), ERROR, ExecCallTriggerFunc(), ExecGetAllUpdatedCols(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_updatedcols, Trigger::tgtype, TriggerDesc::trig_update_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by fireBSTriggers().

◆ ExecCallTriggerFunc()

static HeapTuple ExecCallTriggerFunc ( TriggerData trigdata,
int  tgindx,
FmgrInfo finfo,
Instrumentation instr,
MemoryContext  per_tuple_context 
)
static

Definition at line 2302 of file trigger.c.

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

References AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_INITDEFERRED, Assert(), DatumGetPointer(), ereport, errcode(), errmsg(), ERROR, fmgr_info(), FmgrInfo::fn_oid, FunctionCallInvoke, InitFunctionCallInfoData, InstrStartNode(), InstrStopNode(), InvalidOid, LOCAL_FCINFO, MemoryContextSwitchTo(), MyTriggerDepth, PG_END_TRY, PG_FINALLY, PG_TRY, pgstat_end_function_usage(), pgstat_init_function_usage(), TriggerData::tg_event, TriggerData::tg_newtable, TriggerData::tg_oldtable, TriggerData::tg_trigger, Trigger::tgfoid, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, and TRIGGER_FIRED_BY_UPDATE.

Referenced by AfterTriggerExecute(), ExecBRDeleteTriggers(), ExecBRInsertTriggers(), ExecBRUpdateTriggers(), ExecBSDeleteTriggers(), ExecBSInsertTriggers(), ExecBSTruncateTriggers(), ExecBSUpdateTriggers(), ExecIRDeleteTriggers(), ExecIRInsertTriggers(), and ExecIRUpdateTriggers().

◆ ExecIRDeleteTriggers()

bool ExecIRDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
HeapTuple  trigtuple 
)

Definition at line 2811 of file trigger.c.

2813 {
2814  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2815  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2816  TriggerData LocTriggerData = {0};
2817  int i;
2818 
2819  LocTriggerData.type = T_TriggerData;
2820  LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
2823  LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2824 
2825  ExecForceStoreHeapTuple(trigtuple, slot, false);
2826 
2827  for (i = 0; i < trigdesc->numtriggers; i++)
2828  {
2829  HeapTuple rettuple;
2830  Trigger *trigger = &trigdesc->triggers[i];
2831 
2832  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2833  TRIGGER_TYPE_ROW,
2834  TRIGGER_TYPE_INSTEAD,
2835  TRIGGER_TYPE_DELETE))
2836  continue;
2837  if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2838  NULL, slot, NULL))
2839  continue;
2840 
2841  LocTriggerData.tg_trigslot = slot;
2842  LocTriggerData.tg_trigtuple = trigtuple;
2843  LocTriggerData.tg_trigger = trigger;
2844  rettuple = ExecCallTriggerFunc(&LocTriggerData,
2845  i,
2846  relinfo->ri_TrigFunctions,
2847  relinfo->ri_TrigInstrument,
2848  GetPerTupleMemoryContext(estate));
2849  if (rettuple == NULL)
2850  return false; /* Delete was suppressed */
2851  if (rettuple != trigtuple)
2852  heap_freetuple(rettuple);
2853  }
2854  return true;
2855 }
#define TRIGGER_EVENT_INSTEAD
Definition: trigger.h:102

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

Referenced by ExecDelete(), and ExecMergeMatched().

◆ ExecIRInsertTriggers()

bool ExecIRInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot 
)

Definition at line 2551 of file trigger.c.

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

References ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgtype, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_INSTEAD, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by CopyFrom(), and ExecInsert().

◆ ExecIRUpdateTriggers()

bool ExecIRUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
HeapTuple  trigtuple,
TupleTableSlot newslot 
)

Definition at line 3156 of file trigger.c.

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

References ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, TriggerData::tg_event, TriggerData::tg_newslot, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgtype, TRIGGER_EVENT_INSTEAD, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ FindTriggerIncompatibleWithInheritance()

const char* FindTriggerIncompatibleWithInheritance ( TriggerDesc trigdesc)

Definition at line 2272 of file trigger.c.

2273 {
2274  if (trigdesc != NULL)
2275  {
2276  int i;
2277 
2278  for (i = 0; i < trigdesc->numtriggers; ++i)
2279  {
2280  Trigger *trigger = &trigdesc->triggers[i];
2281 
2282  if (trigger->tgoldtable != NULL || trigger->tgnewtable != NULL)
2283  return trigger->tgname;
2284  }
2285  }
2286 
2287  return NULL;
2288 }

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

Referenced by ATExecAddInherit(), and ATExecAttachPartition().

◆ FreeTriggerDesc()

void FreeTriggerDesc ( TriggerDesc trigdesc)

Definition at line 2140 of file trigger.c.

2141 {
2142  Trigger *trigger;
2143  int i;
2144 
2145  if (trigdesc == NULL)
2146  return;
2147 
2148  trigger = trigdesc->triggers;
2149  for (i = 0; i < trigdesc->numtriggers; i++)
2150  {
2151  pfree(trigger->tgname);
2152  if (trigger->tgnattr > 0)
2153  pfree(trigger->tgattr);
2154  if (trigger->tgnargs > 0)
2155  {
2156  while (--(trigger->tgnargs) >= 0)
2157  pfree(trigger->tgargs[trigger->tgnargs]);
2158  pfree(trigger->tgargs);
2159  }
2160  if (trigger->tgqual)
2161  pfree(trigger->tgqual);
2162  if (trigger->tgoldtable)
2163  pfree(trigger->tgoldtable);
2164  if (trigger->tgnewtable)
2165  pfree(trigger->tgnewtable);
2166  trigger++;
2167  }
2168  pfree(trigdesc->triggers);
2169  pfree(trigdesc);
2170 }

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

Referenced by RelationBuildTriggers(), and RelationDestroyRelation().

◆ get_trigger_oid()

Oid get_trigger_oid ( Oid  relid,
const char *  trigname,
bool  missing_ok 
)

Definition at line 1366 of file trigger.c.

1367 {
1368  Relation tgrel;
1369  ScanKeyData skey[2];
1370  SysScanDesc tgscan;
1371  HeapTuple tup;
1372  Oid oid;
1373 
1374  /*
1375  * Find the trigger, verify permissions, set up object address
1376  */
1377  tgrel = table_open(TriggerRelationId, AccessShareLock);
1378 
1379  ScanKeyInit(&skey[0],
1380  Anum_pg_trigger_tgrelid,
1381  BTEqualStrategyNumber, F_OIDEQ,
1382  ObjectIdGetDatum(relid));
1383  ScanKeyInit(&skey[1],
1384  Anum_pg_trigger_tgname,
1385  BTEqualStrategyNumber, F_NAMEEQ,
1386  CStringGetDatum(trigname));
1387 
1388  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1389  NULL, 2, skey);
1390 
1391  tup = systable_getnext(tgscan);
1392 
1393  if (!HeapTupleIsValid(tup))
1394  {
1395  if (!missing_ok)
1396  ereport(ERROR,
1397  (errcode(ERRCODE_UNDEFINED_OBJECT),
1398  errmsg("trigger \"%s\" for table \"%s\" does not exist",
1399  trigname, get_rel_name(relid))));
1400  oid = InvalidOid;
1401  }
1402  else
1403  {
1404  oid = ((Form_pg_trigger) GETSTRUCT(tup))->oid;
1405  }
1406 
1407  systable_endscan(tgscan);
1408  table_close(tgrel, AccessShareLock);
1409  return oid;
1410 }

References AccessShareLock, BTEqualStrategyNumber, CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, get_rel_name(), GETSTRUCT, HeapTupleIsValid, InvalidOid, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by get_object_address_relobject().

◆ GetAfterTriggersStoreSlot()

static TupleTableSlot * GetAfterTriggersStoreSlot ( AfterTriggersTableData table,
TupleDesc  tupdesc 
)
static

Definition at line 4833 of file trigger.c.

4835 {
4836  /* Create it if not already done. */
4837  if (!table->storeslot)
4838  {
4839  MemoryContext oldcxt;
4840 
4841  /*
4842  * We need this slot only until AfterTriggerEndQuery, but making it
4843  * last till end-of-subxact is good enough. It'll be freed by
4844  * AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4845  * a different lifespan, so we'd better make a copy of that.
4846  */
4848  tupdesc = CreateTupleDescCopy(tupdesc);
4849  table->storeslot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
4850  MemoryContextSwitchTo(oldcxt);
4851  }
4852 
4853  return table->storeslot;
4854 }
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
MemoryContext CurTransactionContext
Definition: mcxt.c:143
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:133

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

Referenced by TransitionTableAddTuple().

◆ GetAfterTriggersTableData()

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

Definition at line 4796 of file trigger.c.

4797 {
4798  AfterTriggersTableData *table;
4800  MemoryContext oldcxt;
4801  ListCell *lc;
4802 
4803  /* Caller should have ensured query_depth is OK. */
4807 
4808  foreach(lc, qs->tables)
4809  {
4810  table = (AfterTriggersTableData *) lfirst(lc);
4811  if (table->relid == relid && table->cmdType == cmdType &&
4812  !table->closed)
4813  return table;
4814  }
4815 
4817 
4819  table->relid = relid;
4820  table->cmdType = cmdType;
4821  qs->tables = lappend(qs->tables, table);
4822 
4823  MemoryContextSwitchTo(oldcxt);
4824 
4825  return table;
4826 }
List * lappend(List *list, void *datum)
Definition: list.c:339
void * palloc0(Size size)
Definition: mcxt.c:1334

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

Referenced by before_stmt_triggers_fired(), cancel_prior_stmt_triggers(), and MakeTransitionCaptureState().

◆ GetAfterTriggersTransitionTable()

static Tuplestorestate * GetAfterTriggersTransitionTable ( int  event,
TupleTableSlot oldslot,
TupleTableSlot newslot,
TransitionCaptureState transition_capture 
)
static

Definition at line 5446 of file trigger.c.

5450 {
5451  Tuplestorestate *tuplestore = NULL;
5452  bool delete_old_table = transition_capture->tcs_delete_old_table;
5453  bool update_old_table = transition_capture->tcs_update_old_table;
5454  bool update_new_table = transition_capture->tcs_update_new_table;
5455  bool insert_new_table = transition_capture->tcs_insert_new_table;
5456 
5457  /*
5458  * For INSERT events NEW should be non-NULL, for DELETE events OLD should
5459  * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
5460  * non-NULL. But for UPDATE events fired for capturing transition tuples
5461  * during UPDATE partition-key row movement, OLD is NULL when the event is
5462  * for a row being inserted, whereas NEW is NULL when the event is for a
5463  * row being deleted.
5464  */
5465  Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
5466  TupIsNull(oldslot)));
5467  Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
5468  TupIsNull(newslot)));
5469 
5470  if (!TupIsNull(oldslot))
5471  {
5472  Assert(TupIsNull(newslot));
5473  if (event == TRIGGER_EVENT_DELETE && delete_old_table)
5474  tuplestore = transition_capture->tcs_private->old_del_tuplestore;
5475  else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
5476  tuplestore = transition_capture->tcs_private->old_upd_tuplestore;
5477  }
5478  else if (!TupIsNull(newslot))
5479  {
5480  Assert(TupIsNull(oldslot));
5481  if (event == TRIGGER_EVENT_INSERT && insert_new_table)
5482  tuplestore = transition_capture->tcs_private->new_ins_tuplestore;
5483  else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
5484  tuplestore = transition_capture->tcs_private->new_upd_tuplestore;
5485  }
5486 
5487  return tuplestore;
5488 }

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

Referenced by AfterTriggerSaveEvent().

◆ GetCurrentFDWTuplestore()

static Tuplestorestate* GetCurrentFDWTuplestore ( void  )
static

Definition at line 3918 of file trigger.c.

3919 {
3920  Tuplestorestate *ret;
3921 
3923  if (ret == NULL)
3924  {
3925  MemoryContext oldcxt;
3926  ResourceOwner saveResourceOwner;
3927 
3928  /*
3929  * Make the tuplestore valid until end of subtransaction. We really
3930  * only need it until AfterTriggerEndQuery().
3931  */
3933  saveResourceOwner = CurrentResourceOwner;
3935 
3936  ret = tuplestore_begin_heap(false, false, work_mem);
3937 
3938  CurrentResourceOwner = saveResourceOwner;
3939  MemoryContextSwitchTo(oldcxt);
3940 
3942  }
3943 
3944  return ret;
3945 }
int work_mem
Definition: globals.c:128
ResourceOwner CurrentResourceOwner
Definition: resowner.c:165
ResourceOwner CurTransactionResourceOwner
Definition: resowner.c:166
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ GetTupleForTrigger()

static bool GetTupleForTrigger ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tid,
LockTupleMode  lockmode,
TupleTableSlot oldslot,
TupleTableSlot **  epqslot,
TM_Result tmresultp,
TM_FailureData tmfdp 
)
static

Definition at line 3286 of file trigger.c.

3295 {
3296  Relation relation = relinfo->ri_RelationDesc;
3297 
3298  if (epqslot != NULL)
3299  {
3300  TM_Result test;
3301  TM_FailureData tmfd;
3302  int lockflags = 0;
3303 
3304  *epqslot = NULL;
3305 
3306  /* caller must pass an epqstate if EvalPlanQual is possible */
3307  Assert(epqstate != NULL);
3308 
3309  /*
3310  * lock tuple for update
3311  */
3313  lockflags |= TUPLE_LOCK_FLAG_FIND_LAST_VERSION;
3314  test = table_tuple_lock(relation, tid, estate->es_snapshot, oldslot,
3315  estate->es_output_cid,
3316  lockmode, LockWaitBlock,
3317  lockflags,
3318  &tmfd);
3319 
3320  /* Let the caller know about the status of this operation */
3321  if (tmresultp)
3322  *tmresultp = test;
3323  if (tmfdp)
3324  *tmfdp = tmfd;
3325 
3326  switch (test)
3327  {
3328  case TM_SelfModified:
3329 
3330  /*
3331  * The target tuple was already updated or deleted by the
3332  * current command, or by a later command in the current
3333  * transaction. We ignore the tuple in the former case, and
3334  * throw error in the latter case, for the same reasons
3335  * enumerated in ExecUpdate and ExecDelete in
3336  * nodeModifyTable.c.
3337  */
3338  if (tmfd.cmax != estate->es_output_cid)
3339  ereport(ERROR,
3340  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3341  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
3342  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3343 
3344  /* treat it as deleted; do not process */
3345  return false;
3346 
3347  case TM_Ok:
3348  if (tmfd.traversed)
3349  {
3350  /*
3351  * Recheck the tuple using EPQ. For MERGE, we leave this
3352  * to the caller (it must do additional rechecking, and
3353  * might end up executing a different action entirely).
3354  */
3355  if (estate->es_plannedstmt->commandType == CMD_MERGE)
3356  {
3357  if (tmresultp)
3358  *tmresultp = TM_Updated;
3359  return false;
3360  }
3361 
3362  *epqslot = EvalPlanQual(epqstate,
3363  relation,
3364  relinfo->ri_RangeTableIndex,
3365  oldslot);
3366 
3367  /*
3368  * If PlanQual failed for updated tuple - we must not
3369  * process this tuple!
3370  */
3371  if (TupIsNull(*epqslot))
3372  {
3373  *epqslot = NULL;
3374  return false;
3375  }
3376  }
3377  break;
3378 
3379  case TM_Updated:
3381  ereport(ERROR,
3383  errmsg("could not serialize access due to concurrent update")));
3384  elog(ERROR, "unexpected table_tuple_lock status: %u", test);
3385  break;
3386 
3387  case TM_Deleted:
3389  ereport(ERROR,
3391  errmsg("could not serialize access due to concurrent delete")));
3392  /* tuple was deleted */
3393  return false;
3394 
3395  case TM_Invisible:
3396  elog(ERROR, "attempted to lock invisible tuple");
3397  break;
3398 
3399  default:
3400  elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
3401  return false; /* keep compiler quiet */
3402  }
3403  }
3404  else
3405  {
3406  /*
3407  * We expect the tuple to be present, thus very simple error handling
3408  * suffices.
3409  */
3410  if (!table_tuple_fetch_row_version(relation, tid, SnapshotAny,
3411  oldslot))
3412  elog(ERROR, "failed to fetch tuple for trigger");
3413  }
3414 
3415  return true;
3416 }
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition: execMain.c:2468
@ LockWaitBlock
Definition: lockoptions.h:39
@ CMD_MERGE
Definition: nodes.h:259
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition: pgbench.c:76
static void test(void)
PlannedStmt * es_plannedstmt
Definition: execnodes.h:631
CommandId es_output_cid
Definition: execnodes.h:637
Snapshot es_snapshot
Definition: execnodes.h:622
CmdType commandType
Definition: plannodes.h:52
Index ri_RangeTableIndex
Definition: execnodes.h:453
bool traversed
Definition: tableam.h:145
CommandId cmax
Definition: tableam.h:144
TM_Result
Definition: tableam.h:72
@ TM_Ok
Definition: tableam.h:77
@ TM_Deleted
Definition: tableam.h:92
@ TM_Updated
Definition: tableam.h:89
@ TM_SelfModified
Definition: tableam.h:83
@ TM_Invisible
Definition: tableam.h:80
static TM_Result table_tuple_lock(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
Definition: tableam.h:1570
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:260
#define IsolationUsesXactSnapshot()
Definition: xact.h:51

References Assert(), TM_FailureData::cmax, CMD_MERGE, PlannedStmt::commandType, elog, ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errhint(), errmsg(), ERROR, EState::es_output_cid, EState::es_plannedstmt, EState::es_snapshot, EvalPlanQual(), IsolationUsesXactSnapshot, LockWaitBlock, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, SnapshotAny, table_tuple_fetch_row_version(), table_tuple_lock(), test(), TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TM_FailureData::traversed, TupIsNull, and TUPLE_LOCK_FLAG_FIND_LAST_VERSION.

Referenced by ExecARDeleteTriggers(), ExecARUpdateTriggers(), ExecBRDeleteTriggers(), and ExecBRUpdateTriggers().

◆ MakeTransitionCaptureState()

TransitionCaptureState* MakeTransitionCaptureState ( TriggerDesc trigdesc,
Oid  relid,
CmdType  cmdType 
)

Definition at line 4882 of file trigger.c.

4883 {
4885  bool need_old_upd,
4886  need_new_upd,
4887  need_old_del,
4888  need_new_ins;
4889  AfterTriggersTableData *table;
4890  MemoryContext oldcxt;
4891  ResourceOwner saveResourceOwner;
4892 
4893  if (trigdesc == NULL)
4894  return NULL;
4895 
4896  /* Detect which table(s) we need. */
4897  switch (cmdType)
4898  {
4899  case CMD_INSERT:
4900  need_old_upd = need_old_del = need_new_upd = false;
4901  need_new_ins = trigdesc->trig_insert_new_table;
4902  break;
4903  case CMD_UPDATE:
4904  need_old_upd = trigdesc->trig_update_old_table;
4905  need_new_upd = trigdesc->trig_update_new_table;
4906  need_old_del = need_new_ins = false;
4907  break;
4908  case CMD_DELETE:
4909  need_old_del = trigdesc->trig_delete_old_table;
4910  need_old_upd = need_new_upd = need_new_ins = false;
4911  break;
4912  case CMD_MERGE:
4913  need_old_upd = trigdesc->trig_update_old_table;
4914  need_new_upd = trigdesc->trig_update_new_table;
4915  need_old_del = trigdesc->trig_delete_old_table;
4916  need_new_ins = trigdesc->trig_insert_new_table;
4917  break;
4918  default:
4919  elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
4920  /* keep compiler quiet */
4921  need_old_upd = need_new_upd = need_old_del = need_new_ins = false;
4922  break;
4923  }
4924  if (!need_old_upd && !need_new_upd && !need_new_ins && !need_old_del)
4925  return NULL;
4926 
4927  /* Check state, like AfterTriggerSaveEvent. */
4928  if (afterTriggers.query_depth < 0)
4929  elog(ERROR, "MakeTransitionCaptureState() called outside of query");
4930 
4931  /* Be sure we have enough space to record events at this query depth. */
4934 
4935  /*
4936  * Find or create an AfterTriggersTableData struct to hold the
4937  * tuplestore(s). If there's a matching struct but it's marked closed,
4938  * ignore it; we need a newer one.
4939  *
4940  * Note: the AfterTriggersTableData list, as well as the tuplestores, are
4941  * allocated in the current (sub)transaction's CurTransactionContext, and
4942  * the tuplestores are managed by the (sub)transaction's resource owner.
4943  * This is sufficient lifespan because we do not allow triggers using
4944  * transition tables to be deferrable; they will be fired during
4945  * AfterTriggerEndQuery, after which it's okay to delete the data.
4946  */
4947  table = GetAfterTriggersTableData(relid, cmdType);
4948 
4949  /* Now create required tuplestore(s), if we don't have them already. */
4951  saveResourceOwner = CurrentResourceOwner;
4953 
4954  if (need_old_upd && table->old_upd_tuplestore == NULL)
4955  table->old_upd_tuplestore = tuplestore_begin_heap(false, false, work_mem);
4956  if (need_new_upd && table->new_upd_tuplestore == NULL)
4957  table->new_upd_tuplestore = tuplestore_begin_heap(false, false, work_mem);
4958  if (need_old_del && table->old_del_tuplestore == NULL)
4959  table->old_del_tuplestore = tuplestore_begin_heap(false, false, work_mem);
4960  if (need_new_ins && table->new_ins_tuplestore == NULL)
4961  table->new_ins_tuplestore = tuplestore_begin_heap(false, false, work_mem);
4962 
4963  CurrentResourceOwner = saveResourceOwner;
4964  MemoryContextSwitchTo(oldcxt);
4965 
4966  /* Now build the TransitionCaptureState struct, in caller's context */
4968  state->tcs_delete_old_table = trigdesc->trig_delete_old_table;
4969  state->tcs_update_old_table = trigdesc->trig_update_old_table;
4970  state->tcs_update_new_table = trigdesc->trig_update_new_table;
4971  state->tcs_insert_new_table = trigdesc->trig_insert_new_table;
4972  state->tcs_private = table;
4973 
4974  return state;
4975 }
bool trig_update_new_table
Definition: reltrigger.h:77
bool trig_insert_new_table
Definition: reltrigger.h:75
bool trig_delete_old_table
Definition: reltrigger.h:78
bool trig_update_old_table
Definition: reltrigger.h:76

References AfterTriggerEnlargeQueryState(), afterTriggers, CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, CurrentResourceOwner, CurTransactionContext, CurTransactionResourceOwner, elog, ERROR, GetAfterTriggersTableData(), AfterTriggersData::maxquerydepth, MemoryContextSwitchTo(), AfterTriggersTableData::new_ins_tuplestore, AfterTriggersTableData::new_upd_tuplestore, AfterTriggersTableData::old_del_tuplestore, AfterTriggersTableData::old_upd_tuplestore, palloc0(), AfterTriggersData::query_depth, TriggerDesc::trig_delete_old_table, TriggerDesc::trig_insert_new_table, TriggerDesc::trig_update_new_table, TriggerDesc::trig_update_old_table, tuplestore_begin_heap(), and work_mem.

Referenced by CopyFrom(), and ExecSetupTransitionCaptureState().

◆ pg_trigger_depth()

Datum pg_trigger_depth ( PG_FUNCTION_ARGS  )

Definition at line 6593 of file trigger.c.

6594 {
6596 }
#define PG_RETURN_INT32(x)
Definition: fmgr.h:354

References MyTriggerDepth, and PG_RETURN_INT32.

◆ RangeVarCallbackForRenameTrigger()

static void RangeVarCallbackForRenameTrigger ( const RangeVar rv,
Oid  relid,
Oid  oldrelid,
void *  arg 
)
static

Definition at line 1416 of file trigger.c.

1418 {
1419  HeapTuple tuple;
1420  Form_pg_class form;
1421 
1422  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1423  if (!HeapTupleIsValid(tuple))
1424  return; /* concurrently dropped */
1425  form = (Form_pg_class) GETSTRUCT(tuple);
1426 
1427  /* only tables and views can have triggers */
1428  if (form->relkind != RELKIND_RELATION && form->relkind != RELKIND_VIEW &&
1429  form->relkind != RELKIND_FOREIGN_TABLE &&
1430  form->relkind != RELKIND_PARTITIONED_TABLE)
1431  ereport(ERROR,
1432  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1433  errmsg("relation \"%s\" cannot have triggers",
1434  rv->relname),
1435  errdetail_relkind_not_supported(form->relkind)));
1436 
1437  /* you must own the table to rename one of its triggers */
1438  if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
1440  if (!allowSystemTableMods && IsSystemClass(relid, form))
1441  ereport(ERROR,
1442  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1443  errmsg("permission denied: \"%s\" is a system catalog",
1444  rv->relname)));
1445 
1446  ReleaseSysCache(tuple);
1447 }
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4130
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:85
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, ereport, errcode(), errdetail_relkind_not_supported(), errmsg(), ERROR, get_rel_relkind(), get_relkind_objtype(), GETSTRUCT, GetUserId(), HeapTupleIsValid, IsSystemClass(), object_ownercheck(), ObjectIdGetDatum(), ReleaseSysCache(), RangeVar::relname, and SearchSysCache1().

Referenced by renametrig().

◆ RelationBuildTriggers()

void RelationBuildTriggers ( Relation  relation)

Definition at line 1856 of file trigger.c.

1857 {
1858  TriggerDesc *trigdesc;
1859  int numtrigs;
1860  int maxtrigs;
1861  Trigger *triggers;
1862  Relation tgrel;
1863  ScanKeyData skey;
1864  SysScanDesc tgscan;
1865  HeapTuple htup;
1866  MemoryContext oldContext;
1867  int i;
1868 
1869  /*
1870  * Allocate a working array to hold the triggers (the array is extended if
1871  * necessary)
1872  */
1873  maxtrigs = 16;
1874  triggers = (Trigger *) palloc(maxtrigs * sizeof(Trigger));
1875  numtrigs = 0;
1876 
1877  /*
1878  * Note: since we scan the triggers using TriggerRelidNameIndexId, we will
1879  * be reading the triggers in name order, except possibly during
1880  * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn
1881  * ensures that triggers will be fired in name order.
1882  */
1883  ScanKeyInit(&skey,
1884  Anum_pg_trigger_tgrelid,
1885  BTEqualStrategyNumber, F_OIDEQ,
1886  ObjectIdGetDatum(RelationGetRelid(relation)));
1887 
1888  tgrel = table_open(TriggerRelationId, AccessShareLock);
1889  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1890  NULL, 1, &skey);
1891 
1892  while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
1893  {
1894  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
1895  Trigger *build;
1896  Datum datum;
1897  bool isnull;
1898 
1899  if (numtrigs >= maxtrigs)
1900  {
1901  maxtrigs *= 2;
1902  triggers = (Trigger *) repalloc(triggers, maxtrigs * sizeof(Trigger));
1903  }
1904  build = &(triggers[numtrigs]);
1905 
1906  build->tgoid = pg_trigger->oid;
1908  NameGetDatum(&pg_trigger->tgname)));
1909  build->tgfoid = pg_trigger->tgfoid;
1910  build->tgtype = pg_trigger->tgtype;
1911  build->tgenabled = pg_trigger->tgenabled;
1912  build->tgisinternal = pg_trigger->tgisinternal;
1913  build->tgisclone = OidIsValid(pg_trigger->tgparentid);
1914  build->tgconstrrelid = pg_trigger->tgconstrrelid;
1915  build->tgconstrindid = pg_trigger->tgconstrindid;
1916  build->tgconstraint = pg_trigger->tgconstraint;
1917  build->tgdeferrable = pg_trigger->tgdeferrable;
1918  build->tginitdeferred = pg_trigger->tginitdeferred;
1919  build->tgnargs = pg_trigger->tgnargs;
1920  /* tgattr is first var-width field, so OK to access directly */
1921  build->tgnattr = pg_trigger->tgattr.dim1;
1922  if (build->tgnattr > 0)
1923  {
1924  build->tgattr = (int16 *) palloc(build->tgnattr * sizeof(int16));
1925  memcpy(build->tgattr, &(pg_trigger->tgattr.values),
1926  build->tgnattr * sizeof(int16));
1927  }
1928  else
1929  build->tgattr = NULL;
1930  if (build->tgnargs > 0)
1931  {
1932  bytea *val;
1933  char *p;
1934 
1936  Anum_pg_trigger_tgargs,
1937  tgrel->rd_att, &isnull));
1938  if (isnull)
1939  elog(ERROR, "tgargs is null in trigger for relation \"%s\"",
1940  RelationGetRelationName(relation));
1941  p = (char *) VARDATA_ANY(val);
1942  build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
1943  for (i = 0; i < build->tgnargs; i++)
1944  {
1945  build->tgargs[i] = pstrdup(p);
1946  p += strlen(p) + 1;
1947  }
1948  }
1949  else
1950  build->tgargs = NULL;
1951 
1952  datum = fastgetattr(htup, Anum_pg_trigger_tgoldtable,
1953  tgrel->rd_att, &isnull);
1954  if (!isnull)
1955  build->tgoldtable =
1957  else
1958  build->tgoldtable = NULL;
1959 
1960  datum = fastgetattr(htup, Anum_pg_trigger_tgnewtable,
1961  tgrel->rd_att, &isnull);
1962  if (!isnull)
1963  build->tgnewtable =
1965  else
1966  build->tgnewtable = NULL;
1967 
1968  datum = fastgetattr(htup, Anum_pg_trigger_tgqual,
1969  tgrel->rd_att, &isnull);
1970  if (!isnull)
1971  build->tgqual = TextDatumGetCString(datum);
1972  else
1973  build->tgqual = NULL;
1974 
1975  numtrigs++;
1976  }
1977 
1978  systable_endscan(tgscan);
1979  table_close(tgrel, AccessShareLock);
1980 
1981  /* There might not be any triggers */
1982  if (numtrigs == 0)
1983  {
1984  pfree(triggers);
1985  return;
1986  }
1987 
1988  /* Build trigdesc */
1989  trigdesc = (TriggerDesc *) palloc0(sizeof(TriggerDesc));
1990  trigdesc->triggers = triggers;
1991  trigdesc->numtriggers = numtrigs;
1992  for (i = 0; i < numtrigs; i++)
1993  SetTriggerFlags(trigdesc, &(triggers[i]));
1994 
1995  /* Copy completed trigdesc into cache storage */
1997  relation->trigdesc = CopyTriggerDesc(trigdesc);
1998  MemoryContextSwitchTo(oldContext);
1999 
2000  /* Release working memory */
2001  FreeTriggerDesc(trigdesc);
2002 }
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:749
long val
Definition: informix.c:664
MemoryContext CacheMemoryContext
Definition: mcxt.c:140
Datum nameout(PG_FUNCTION_ARGS)
Definition: name.c:71
static char * DatumGetCString(Datum X)
Definition: postgres.h:335
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:373
TriggerDesc * trigdesc
Definition: rel.h:117
char tgenabled
Definition: reltrigger.h:30
Oid tgconstraint
Definition: reltrigger.h:35
Oid tgconstrrelid
Definition: reltrigger.h:33
bool tgisinternal
Definition: reltrigger.h:31
Definition: c.h:674
void FreeTriggerDesc(TriggerDesc *trigdesc)
Definition: trigger.c:2140
TriggerDesc * CopyTriggerDesc(TriggerDesc *trigdesc)
Definition: trigger.c:2085
static void SetTriggerFlags(TriggerDesc *trigdesc, Trigger *trigger)
Definition: trigger.c:2008
#define VARDATA_ANY(PTR)
Definition: varatt.h:324

References AccessShareLock, BTEqualStrategyNumber, CacheMemoryContext, CopyTriggerDesc(), DatumGetByteaPP, DatumGetCString(), DirectFunctionCall1, elog, ERROR, fastgetattr(), FreeTriggerDesc(), GETSTRUCT, HeapTupleIsValid, i, MemoryContextSwitchTo(), NameGetDatum(), nameout(), TriggerDesc::numtriggers, ObjectIdGetDatum(), OidIsValid, palloc(), palloc0(), pfree(), pstrdup(), RelationData::rd_att, RelationGetRelationName, RelationGetRelid, repalloc(), ScanKeyInit(), SetTriggerFlags(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TextDatumGetCString, Trigger::tgargs, Trigger::tgattr, Trigger::tgconstraint, Trigger::tgconstrindid, Trigger::tgconstrrelid, Trigger::tgdeferrable, Trigger::tgenabled, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgisclone, Trigger::tgisinternal, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgqual, Trigger::tgtype, RelationData::trigdesc, TriggerDesc::triggers, val, and VARDATA_ANY.

Referenced by RelationBuildDesc(), and RelationCacheInitializePhase3().

◆ RemoveTriggerById()

void RemoveTriggerById ( Oid  trigOid)

Definition at line 1287 of file trigger.c.

1288 {
1289  Relation tgrel;
1290  SysScanDesc tgscan;
1291  ScanKeyData skey[1];
1292  HeapTuple tup;
1293  Oid relid;
1294  Relation rel;
1295 
1296  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1297 
1298  /*
1299  * Find the trigger to delete.
1300  */
1301  ScanKeyInit(&skey[0],
1302  Anum_pg_trigger_oid,
1303  BTEqualStrategyNumber, F_OIDEQ,
1304  ObjectIdGetDatum(trigOid));
1305 
1306  tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
1307  NULL, 1, skey);
1308 
1309  tup = systable_getnext(tgscan);
1310  if (!HeapTupleIsValid(tup))
1311  elog(ERROR, "could not find tuple for trigger %u", trigOid);
1312 
1313  /*
1314  * Open and exclusive-lock the relation the trigger belongs to.
1315  */
1316  relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
1317 
1318  rel = table_open(relid, AccessExclusiveLock);
1319 
1320  if (rel->rd_rel->relkind != RELKIND_RELATION &&
1321  rel->rd_rel->relkind != RELKIND_VIEW &&
1322  rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
1323  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1324  ereport(ERROR,
1325  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1326  errmsg("relation \"%s\" cannot have triggers",
1328  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
1329 
1331  ereport(ERROR,
1332  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1333  errmsg("permission denied: \"%s\" is a system catalog",
1334  RelationGetRelationName(rel))));
1335 
1336  /*
1337  * Delete the pg_trigger tuple.
1338  */
1339  CatalogTupleDelete(tgrel, &tup->t_self);
1340 
1341  systable_endscan(tgscan);
1342  table_close(tgrel, RowExclusiveLock);
1343 
1344  /*
1345  * We do not bother to try to determine whether any other triggers remain,
1346  * which would be needed in order to decide whether it's safe to clear the
1347  * relation's relhastriggers. (In any case, there might be a concurrent
1348  * process adding new triggers.) Instead, just force a relcache inval to
1349  * make other backends (and this one too!) rebuild their relcache entries.
1350  * There's no great harm in leaving relhastriggers true even if there are
1351  * no triggers left.
1352  */
1354 
1355  /* Keep lock on trigger's rel until end of xact */
1356  table_close(rel, NoLock);
1357 }
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
#define AccessExclusiveLock
Definition: lockdefs.h:43

References AccessExclusiveLock, allowSystemTableMods, BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleDelete(), elog, ereport, errcode(), errdetail_relkind_not_supported(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, IsSystemRelation(), NoLock, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelationName, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by doDeletion().

◆ renametrig()

ObjectAddress renametrig ( RenameStmt stmt)

Definition at line 1463 of file trigger.c.

1464 {
1465  Oid tgoid;
1466  Relation targetrel;
1467  Relation tgrel;
1468  HeapTuple tuple;
1469  SysScanDesc tgscan;
1470  ScanKeyData key[2];
1471  Oid relid;
1472  ObjectAddress address;
1473 
1474  /*
1475  * Look up name, check permissions, and acquire lock (which we will NOT
1476  * release until end of transaction).
1477  */
1479  0,
1481  NULL);
1482 
1483  /* Have lock already, so just need to build relcache entry. */
1484  targetrel = relation_open(relid, NoLock);
1485 
1486  /*
1487  * On partitioned tables, this operation recurses to partitions. Lock all
1488  * tables upfront.
1489  */
1490  if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1491  (void) find_all_inheritors(relid, AccessExclusiveLock, NULL);
1492 
1493  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1494 
1495  /*
1496  * Search for the trigger to modify.
1497  */
1498  ScanKeyInit(&key[0],
1499  Anum_pg_trigger_tgrelid,
1500  BTEqualStrategyNumber, F_OIDEQ,
1501  ObjectIdGetDatum(relid));
1502  ScanKeyInit(&key[1],
1503  Anum_pg_trigger_tgname,
1504  BTEqualStrategyNumber, F_NAMEEQ,
1505  PointerGetDatum(stmt->subname));
1506  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1507  NULL, 2, key);
1508  if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1509  {
1510  Form_pg_trigger trigform;
1511 
1512  trigform = (Form_pg_trigger) GETSTRUCT(tuple);
1513  tgoid = trigform->oid;
1514 
1515  /*
1516  * If the trigger descends from a trigger on a parent partitioned
1517  * table, reject the rename. We don't allow a trigger in a partition
1518  * to differ in name from that of its parent: that would lead to an
1519  * inconsistency that pg_dump would not reproduce.
1520  */
1521  if (OidIsValid(trigform->tgparentid))
1522  ereport(ERROR,
1523  errmsg("cannot rename trigger \"%s\" on table \"%s\"",
1524  stmt->subname, RelationGetRelationName(targetrel)),
1525  errhint("Rename the trigger on the partitioned table \"%s\" instead.",
1526  get_rel_name(get_partition_parent(relid, false))));
1527 
1528 
1529  /* Rename the trigger on this relation ... */
1530  renametrig_internal(tgrel, targetrel, tuple, stmt->newname,
1531  stmt->subname);
1532 
1533  /* ... and if it is partitioned, recurse to its partitions */
1534  if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1535  {
1536  PartitionDesc partdesc = RelationGetPartitionDesc(targetrel, true);
1537 
1538  for (int i = 0; i < partdesc->nparts; i++)
1539  {
1540  Oid partitionId = partdesc->oids[i];
1541 
1542  renametrig_partition(tgrel, partitionId, trigform->oid,
1543  stmt->newname, stmt->subname);
1544  }
1545  }
1546  }
1547  else
1548  {
1549  ereport(ERROR,
1550  (errcode(ERRCODE_UNDEFINED_OBJECT),
1551  errmsg("trigger \"%s\" for table \"%s\" does not exist",
1552  stmt->subname, RelationGetRelationName(targetrel))));
1553  }
1554 
1555  ObjectAddressSet(address, TriggerRelationId, tgoid);
1556 
1557  systable_endscan(tgscan);
1558 
1559  table_close(tgrel, RowExclusiveLock);
1560 
1561  /*
1562  * Close rel, but keep exclusive lock!
1563  */
1564  relation_close(targetrel, NoLock);
1565 
1566  return address;
1567 }
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:426
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:53
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
static void renametrig_internal(Relation tgrel, Relation targetrel, HeapTuple trigtup, const char *newname, const char *expected_name)
Definition: trigger.c:1577
static void RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: trigger.c:1416
static void renametrig_partition(Relation tgrel, Oid partitionId, Oid parentTriggerOid, const char *newname, const char *expected_name)
Definition: trigger.c:1648

References AccessExclusiveLock, BTEqualStrategyNumber, ereport, errcode(), errhint(), errmsg(), ERROR, find_all_inheritors(), get_partition_parent(), get_rel_name(), GETSTRUCT, HeapTupleIsValid, i, sort-test::key, NoLock, PartitionDescData::nparts, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, PointerGetDatum(), RangeVarCallbackForRenameTrigger(), RangeVarGetRelidExtended(), RelationData::rd_rel, relation_close(), relation_open(), RelationGetPartitionDesc(), RelationGetRelationName, renametrig_internal(), renametrig_partition(), RowExclusiveLock, ScanKeyInit(), stmt, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ExecRenameStmt().

◆ renametrig_internal()

static void renametrig_internal ( Relation  tgrel,
Relation  targetrel,
HeapTuple  trigtup,
const char *  newname,
const char *  expected_name 
)
static

Definition at line 1577 of file trigger.c.

1579 {
1580  HeapTuple tuple;
1581  Form_pg_trigger tgform;
1582  ScanKeyData key[2];
1583  SysScanDesc tgscan;
1584 
1585  /* If the trigger already has the new name, nothing to do. */
1586  tgform = (Form_pg_trigger) GETSTRUCT(trigtup);
1587  if (strcmp(NameStr(tgform->tgname), newname) == 0)
1588  return;
1589 
1590  /*
1591  * Before actually trying the rename, search for triggers with the same
1592  * name. The update would fail with an ugly message in that case, and it
1593  * is better to throw a nicer error.
1594  */
1595  ScanKeyInit(&key[0],
1596  Anum_pg_trigger_tgrelid,
1597  BTEqualStrategyNumber, F_OIDEQ,
1598  ObjectIdGetDatum(RelationGetRelid(targetrel)));
1599  ScanKeyInit(&key[1],
1600  Anum_pg_trigger_tgname,
1601  BTEqualStrategyNumber, F_NAMEEQ,
1602  PointerGetDatum(newname));
1603  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1604  NULL, 2, key);
1605  if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1606  ereport(ERROR,
1608  errmsg("trigger \"%s\" for relation \"%s\" already exists",
1609  newname, RelationGetRelationName(targetrel))));
1610  systable_endscan(tgscan);
1611 
1612  /*
1613  * The target name is free; update the existing pg_trigger tuple with it.
1614  */
1615  tuple = heap_copytuple(trigtup); /* need a modifiable copy */
1616  tgform = (Form_pg_trigger) GETSTRUCT(tuple);
1617 
1618  /*
1619  * If the trigger has a name different from what we expected, let the user
1620  * know. (We can proceed anyway, since we must have reached here following
1621  * a tgparentid link.)
1622  */
1623  if (strcmp(NameStr(tgform->tgname), expected_name) != 0)
1624  ereport(NOTICE,
1625  errmsg("renamed trigger \"%s\" on relation \"%s\"",
1626  NameStr(tgform->tgname),
1627  RelationGetRelationName(targetrel)));
1628 
1629  namestrcpy(&tgform->tgname, newname);
1630 
1631  CatalogTupleUpdate(tgrel, &tuple->t_self, tuple);
1632 
1633  InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
1634 
1635  /*
1636  * Invalidate relation's relcache entry so that other backends (and this
1637  * one too!) are sent SI message to make them rebuild relcache entries.
1638  * (Ideally this should happen automatically...)
1639  */
1640  CacheInvalidateRelcache(targetrel);
1641 }
#define NOTICE
Definition: elog.h:35
void namestrcpy(Name name, const char *str)
Definition: name.c:233

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, GETSTRUCT, heap_copytuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, sort-test::key, NameStr, namestrcpy(), NOTICE, ObjectIdGetDatum(), PointerGetDatum(), RelationGetRelationName, RelationGetRelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.

Referenced by renametrig(), and renametrig_partition().

◆ renametrig_partition()

static void renametrig_partition ( Relation  tgrel,
Oid  partitionId,
Oid  parentTriggerOid,
const char *  newname,
const char *  expected_name 
)
static

Definition at line 1648 of file trigger.c.

1650 {
1651  SysScanDesc tgscan;
1652  ScanKeyData key;
1653  HeapTuple tuple;
1654 
1655  /*
1656  * Given a relation and the OID of a trigger on parent relation, find the
1657  * corresponding trigger in the child and rename that trigger to the given
1658  * name.
1659  */
1660  ScanKeyInit(&key,
1661  Anum_pg_trigger_tgrelid,
1662  BTEqualStrategyNumber, F_OIDEQ,
1663  ObjectIdGetDatum(partitionId));
1664  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1665  NULL, 1, &key);
1666  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1667  {
1668  Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tuple);
1669  Relation partitionRel;
1670 
1671  if (tgform->tgparentid != parentTriggerOid)
1672  continue; /* not our trigger */
1673 
1674  partitionRel = table_open(partitionId, NoLock);
1675 
1676  /* Rename the trigger on this partition */
1677  renametrig_internal(tgrel, partitionRel, tuple, newname, expected_name);
1678 
1679  /* And if this relation is partitioned, recurse to its partitions */
1680  if (partitionRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1681  {
1682  PartitionDesc partdesc = RelationGetPartitionDesc(partitionRel,
1683  true);
1684 
1685  for (int i = 0; i < partdesc->nparts; i++)
1686  {
1687  Oid partoid = partdesc->oids[i];
1688 
1689  renametrig_partition(tgrel, partoid, tgform->oid, newname,
1690  NameStr(tgform->tgname));
1691  }
1692  }
1693  table_close(partitionRel, NoLock);
1694 
1695  /* There should be at most one matching tuple */
1696  break;
1697  }
1698  systable_endscan(tgscan);
1699 }

References BTEqualStrategyNumber, GETSTRUCT, HeapTupleIsValid, i, sort-test::key, NameStr, NoLock, PartitionDescData::nparts, ObjectIdGetDatum(), PartitionDescData::oids, RelationData::rd_rel, RelationGetPartitionDesc(), renametrig_internal(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by renametrig().

◆ SetConstraintStateAddItem()

static SetConstraintState SetConstraintStateAddItem ( SetConstraintState  state,
Oid  tgoid,
bool  tgisdeferred 
)
static

Definition at line 5629 of file trigger.c.

5631 {
5632  if (state->numstates >= state->numalloc)
5633  {
5634  int newalloc = state->numalloc * 2;
5635 
5636  newalloc = Max(newalloc, 8); /* in case original has size 0 */
5638  repalloc(state,
5639  offsetof(SetConstraintStateData, trigstates) +
5640  newalloc * sizeof(SetConstraintTriggerData));
5641  state->numalloc = newalloc;
5642  Assert(state->numstates < state->numalloc);
5643  }
5644 
5645  state->trigstates[state->numstates].sct_tgoid = tgoid;
5646  state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5647  state->numstates++;
5648 
5649  return state;
5650 }
struct SetConstraintTriggerData SetConstraintTriggerData
SetConstraintStateData * SetConstraintState
Definition: trigger.c:3572

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCopy()

static SetConstraintState SetConstraintStateCopy ( SetConstraintState  origstate)
static

Definition at line 5609 of file trigger.c.

5610 {
5612 
5614 
5615  state->all_isset = origstate->all_isset;
5616  state->all_isdeferred = origstate->all_isdeferred;
5617  state->numstates = origstate->numstates;
5618  memcpy(state->trigstates, origstate->trigstates,
5619  origstate->numstates * sizeof(SetConstraintTriggerData));
5620 
5621  return state;
5622 }
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3569

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

Referenced by AfterTriggerSetState().

◆ SetConstraintStateCreate()

static SetConstraintState SetConstraintStateCreate ( int  numalloc)
static

Definition at line 5584 of file trigger.c.

5585 {
5587 
5588  /* Behave sanely with numalloc == 0 */
5589  if (numalloc <= 0)
5590  numalloc = 1;
5591 
5592  /*
5593  * We assume that zeroing will correctly initialize the state values.
5594  */
5597  offsetof(SetConstraintStateData, trigstates) +
5598  numalloc * sizeof(SetConstraintTriggerData));
5599 
5600  state->numalloc = numalloc;
5601 
5602  return state;
5603 }
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1202

References MemoryContextAllocZero(), and TopTransactionContext.

Referenced by AfterTriggerSetState(), and SetConstraintStateCopy().

◆ SetTriggerFlags()

static void SetTriggerFlags ( TriggerDesc trigdesc,
Trigger trigger 
)
static

Definition at line 2008 of file trigger.c.

2009 {
2010  int16 tgtype = trigger->tgtype;
2011 
2012  trigdesc->trig_insert_before_row |=
2013  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2014  TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT);
2015  trigdesc->trig_insert_after_row |=
2016  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2017  TRIGGER_TYPE_AFTER, TRIGGER_TYPE_INSERT);
2018  trigdesc->trig_insert_instead_row |=
2019  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2020  TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_INSERT);
2021  trigdesc->trig_insert_before_statement |=
2022  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2023  TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT);
2024  trigdesc->trig_insert_after_statement |=
2025  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2026  TRIGGER_TYPE_AFTER, TRIGGER_TYPE_INSERT);
2027  trigdesc->trig_update_before_row |=
2028  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2029  TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_UPDATE);
2030  trigdesc->trig_update_after_row |=
2031  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2032  TRIGGER_TYPE_AFTER, TRIGGER_TYPE_UPDATE);
2033  trigdesc->trig_update_instead_row |=
2034  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2035  TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_UPDATE);
2036  trigdesc->trig_update_before_statement |=
2037  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2038  TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_UPDATE);
2039  trigdesc->trig_update_after_statement |=
2040  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2041  TRIGGER_TYPE_AFTER, TRIGGER_TYPE_UPDATE);
2042  trigdesc->trig_delete_before_row |=
2043  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2044  TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE);
2045  trigdesc->trig_delete_after_row |=
2046  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2047  TRIGGER_TYPE_AFTER, TRIGGER_TYPE_DELETE);
2048  trigdesc->trig_delete_instead_row |=
2049  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_ROW,
2050  TRIGGER_TYPE_INSTEAD, TRIGGER_TYPE_DELETE);
2051  trigdesc->trig_delete_before_statement |=
2052  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2053  TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_DELETE);
2054  trigdesc->trig_delete_after_statement |=
2055  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2056  TRIGGER_TYPE_AFTER, TRIGGER_TYPE_DELETE);
2057  /* there are no row-level truncate triggers */
2058  trigdesc->trig_truncate_before_statement |=
2059  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2060  TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_TRUNCATE);
2061  trigdesc->trig_truncate_after_statement |=
2062  TRIGGER_TYPE_MATCHES(tgtype, TRIGGER_TYPE_STATEMENT,
2063  TRIGGER_TYPE_AFTER, TRIGGER_TYPE_TRUNCATE);
2064 
2065  trigdesc->trig_insert_new_table |=
2066  (TRIGGER_FOR_INSERT(tgtype) &&
2067  TRIGGER_USES_TRANSITION_TABLE(trigger->tgnewtable));
2068  trigdesc->trig_update_old_table |=
2069  (TRIGGER_FOR_UPDATE(tgtype) &&
2070  TRIGGER_USES_TRANSITION_TABLE(trigger->tgoldtable));
2071  trigdesc->trig_update_new_table |=
2072  (TRIGGER_FOR_UPDATE(tgtype) &&
2073  TRIGGER_USES_TRANSITION_TABLE(trigger->tgnewtable));
2074  trigdesc->trig_delete_old_table |=
2075  (TRIGGER_FOR_DELETE(tgtype) &&
2076  TRIGGER_USES_TRANSITION_TABLE(trigger->tgoldtable));
2077 }
bool trig_delete_before_row
Definition: reltrigger.h:66
bool trig_update_instead_row
Definition: reltrigger.h:63
bool trig_delete_instead_row
Definition: reltrigger.h:68
bool trig_insert_instead_row
Definition: reltrigger.h:58
bool trig_update_before_row
Definition: reltrigger.h:61
bool trig_insert_before_row
Definition: reltrigger.h:56

References Trigger::tgnewtable, Trigger::tgoldtable, Trigger::tgtype, TriggerDesc::trig_delete_after_row, TriggerDesc::trig_delete_after_statement, TriggerDesc::trig_delete_before_row, TriggerDesc::trig_delete_before_statement, TriggerDesc::trig_delete_instead_row, TriggerDesc::trig_delete_old_table, TriggerDesc::trig_insert_after_row, TriggerDesc::trig_insert_after_statement, TriggerDesc::trig_insert_before_row, TriggerDesc::trig_insert_before_statement, TriggerDesc::trig_insert_instead_row, TriggerDesc::trig_insert_new_table, TriggerDesc::trig_truncate_after_statement, TriggerDesc::trig_truncate_before_statement, TriggerDesc::trig_update_after_row, TriggerDesc::trig_update_after_statement, TriggerDesc::trig_update_before_row, TriggerDesc::trig_update_before_statement, TriggerDesc::trig_update_instead_row, TriggerDesc::trig_update_new_table, and TriggerDesc::trig_update_old_table.

Referenced by RelationBuildTriggers().

◆ TransitionTableAddTuple()

static void TransitionTableAddTuple ( EState estate,
TransitionCaptureState transition_capture,
ResultRelInfo relinfo,
TupleTableSlot slot,
TupleTableSlot original_insert_tuple,
Tuplestorestate tuplestore 
)
static

Definition at line 5497 of file trigger.c.

5503 {
5504  TupleConversionMap *map;
5505 
5506  /*
5507  * Nothing needs to be done if we don't have a tuplestore.
5508  */
5509  if (tuplestore == NULL)
5510  return;
5511 
5512  if (original_insert_tuple)
5513  tuplestore_puttupleslot(tuplestore, original_insert_tuple);
5514  else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
5515  {
5516  AfterTriggersTableData *table = transition_capture->tcs_private;
5517  TupleTableSlot *storeslot;
5518 
5519  storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5520  execute_attr_map_slot(map->attrMap, slot, storeslot);
5521  tuplestore_puttupleslot(tuplestore, storeslot);
5522  }
5523  else
5524  tuplestore_puttupleslot(tuplestore, slot);
5525 }
TupleDesc outdesc
Definition: tupconvert.h:27
static TupleTableSlot * GetAfterTriggersStoreSlot(AfterTriggersTableData *table, TupleDesc tupdesc)
Definition: trigger.c:4833

References TupleConversionMap::attrMap, ExecGetChildToRootMap(), execute_attr_map_slot(), GetAfterTriggersStoreSlot(), TupleConversionMap::outdesc, TransitionCaptureState::tcs_private, and tuplestore_puttupleslot().

Referenced by AfterTriggerSaveEvent().

◆ TriggerEnabled()

static bool TriggerEnabled ( EState estate,
ResultRelInfo relinfo,
Trigger trigger,
TriggerEvent  event,
Bitmapset modifiedCols,
TupleTableSlot oldslot,
TupleTableSlot newslot 
)
static

Definition at line 3422 of file trigger.c.

3426 {
3427  /* Check replication-role-dependent enable state */
3429  {
3430  if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN ||
3431  trigger->tgenabled == TRIGGER_DISABLED)
3432  return false;
3433  }
3434  else /* ORIGIN or LOCAL role */
3435  {
3436  if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA ||
3437  trigger->tgenabled == TRIGGER_DISABLED)
3438  return false;
3439  }
3440 
3441  /*
3442  * Check for column-specific trigger (only possible for UPDATE, and in
3443  * fact we *must* ignore tgattr for other event types)
3444  */
3445  if (trigger->tgnattr > 0 && TRIGGER_FIRED_BY_UPDATE(event))
3446  {
3447  int i;
3448  bool modified;
3449 
3450  modified = false;
3451  for (i = 0; i < trigger->tgnattr; i++)
3452  {
3454  modifiedCols))
3455  {
3456  modified = true;
3457  break;
3458  }
3459  }
3460  if (!modified)
3461  return false;
3462  }
3463 
3464  /* Check for WHEN clause */
3465  if (trigger->tgqual)
3466  {
3467  ExprState **predicate;
3468  ExprContext *econtext;
3469  MemoryContext oldContext;
3470  int i;
3471 
3472  Assert(estate != NULL);
3473 
3474  /*
3475  * trigger is an element of relinfo->ri_TrigDesc->triggers[]; find the
3476  * matching element of relinfo->ri_TrigWhenExprs[]
3477  */
3478  i = trigger - relinfo->ri_TrigDesc->triggers;
3479  predicate = &relinfo->ri_TrigWhenExprs[i];
3480 
3481  /*
3482  * If first time through for this WHEN expression, build expression
3483  * nodetrees for it. Keep them in the per-query memory context so
3484  * they'll survive throughout the query.
3485  */
3486  if (*predicate == NULL)
3487  {
3488  Node *tgqual;
3489 
3490  oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
3491  tgqual = stringToNode(trigger->tgqual);
3492  /* Change references to OLD and NEW to INNER_VAR and OUTER_VAR */
3495  /* ExecPrepareQual wants implicit-AND form */
3496  tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
3497  *predicate = ExecPrepareQual((List *) tgqual, estate);
3498  MemoryContextSwitchTo(oldContext);
3499  }
3500 
3501  /*
3502  * We will use the EState's per-tuple context for evaluating WHEN
3503  * expressions (creating it if it's not already there).
3504  */
3505  econtext = GetPerTupleExprContext(estate);
3506 
3507  /*
3508  * Finally evaluate the expression, making the old and/or new tuples
3509  * available as INNER_VAR/OUTER_VAR respectively.
3510  */
3511  econtext->ecxt_innertuple = oldslot;
3512  econtext->ecxt_outertuple = newslot;
3513  if (!ExecQual(*predicate, econtext))
3514  return false;
3515  }
3516 
3517  return true;
3518 }
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
ExprState * ExecPrepareQual(List *qual, EState *estate)
Definition: execExpr.c:760
#define GetPerTupleExprContext(estate)
Definition: executor.h:550
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:413
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:721
#define OUTER_VAR
Definition: primnodes.h:223
#define INNER_VAR
Definition: primnodes.h:222
void * stringToNode(const char *str)
Definition: read.c:90
void ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
Definition: rewriteManip.c:674
MemoryContext es_query_cxt
Definition: execnodes.h:665
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:257
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:259
ExprState ** ri_TrigWhenExprs
Definition: execnodes.h:492
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
#define SESSION_REPLICATION_ROLE_REPLICA
Definition: trigger.h:141
#define TRIGGER_DISABLED
Definition: trigger.h:152
#define TRIGGER_FIRES_ON_REPLICA
Definition: trigger.h:151

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

Referenced by AfterTriggerSaveEvent(), ExecBRDeleteTriggers(), ExecBRInsertTriggers(), ExecBRUpdateTriggers(), ExecBSDeleteTriggers(), ExecBSInsertTriggers(), ExecBSTruncateTriggers(), ExecBSUpdateTriggers(), ExecIRDeleteTriggers(), ExecIRInsertTriggers(), and ExecIRUpdateTriggers().

◆ TriggerSetParentTrigger()

void TriggerSetParentTrigger ( Relation  trigRel,
Oid  childTrigId,
Oid  parentTrigId,
Oid  childTableId 
)

Definition at line 1216 of file trigger.c.

1220 {
1221  SysScanDesc tgscan;
1222  ScanKeyData skey[1];
1223  Form_pg_trigger trigForm;
1224  HeapTuple tuple,
1225  newtup;
1226  ObjectAddress depender;
1227  ObjectAddress referenced;
1228 
1229  /*
1230  * Find the trigger to delete.
1231  */
1232  ScanKeyInit(&skey[0],
1233  Anum_pg_trigger_oid,
1234  BTEqualStrategyNumber, F_OIDEQ,
1235  ObjectIdGetDatum(childTrigId));
1236 
1237  tgscan = systable_beginscan(trigRel, TriggerOidIndexId, true,
1238  NULL, 1, skey);
1239 
1240  tuple = systable_getnext(tgscan);
1241  if (!HeapTupleIsValid(tuple))
1242  elog(ERROR, "could not find tuple for trigger %u", childTrigId);
1243  newtup = heap_copytuple(tuple);
1244  trigForm = (Form_pg_trigger) GETSTRUCT(newtup);
1245  if (OidIsValid(parentTrigId))
1246  {
1247  /* don't allow setting parent for a constraint that already has one */
1248  if (OidIsValid(trigForm->tgparentid))
1249  elog(ERROR, "trigger %u already has a parent trigger",
1250  childTrigId);
1251 
1252  trigForm->tgparentid = parentTrigId;
1253 
1254  CatalogTupleUpdate(trigRel, &tuple->t_self, newtup);
1255 
1256  ObjectAddressSet(depender, TriggerRelationId, childTrigId);
1257 
1258  ObjectAddressSet(referenced, TriggerRelationId, parentTrigId);
1259  recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
1260 
1261  ObjectAddressSet(referenced, RelationRelationId, childTableId);
1262  recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
1263  }
1264  else
1265  {
1266  trigForm->tgparentid = InvalidOid;
1267 
1268  CatalogTupleUpdate(trigRel, &tuple->t_self, newtup);
1269 
1270  deleteDependencyRecordsForClass(TriggerRelationId, childTrigId,
1271  TriggerRelationId,
1273  deleteDependencyRecordsForClass(TriggerRelationId, childTrigId,
1274  RelationRelationId,
1276  }
1277 
1278  heap_freetuple(newtup);
1279  systable_endscan(tgscan);
1280 }
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:350

References BTEqualStrategyNumber, CatalogTupleUpdate(), deleteDependencyRecordsForClass(), DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, elog, ERROR, GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvalidOid, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, recordDependencyOn(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.

Referenced by DetachPartitionFinalize(), and tryAttachPartitionForeignKey().

Variable Documentation

◆ afterTriggers

◆ MyTriggerDepth

int MyTriggerDepth = 0
static

Definition at line 66 of file trigger.c.

Referenced by ExecCallTriggerFunc(), and pg_trigger_depth().

◆ SessionReplicationRole

int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN