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

Go to the source code of this file.

Data Structures

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

Macros

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

Typedefs

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

Functions

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

Variables

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

Macro Definition Documentation

◆ AFTER_TRIGGER_1CTID

#define AFTER_TRIGGER_1CTID   0x10000000

Definition at line 3625 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3626 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3627 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3620 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3624 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3623 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3621 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3619 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3628 of file trigger.c.

◆ CHUNK_DATA_START

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

Definition at line 3707 of file trigger.c.

◆ for_each_chunk

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

Definition at line 3718 of file trigger.c.

◆ for_each_chunk_from

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

Definition at line 3729 of file trigger.c.

◆ for_each_event

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

Definition at line 3720 of file trigger.c.

◆ for_each_event_chunk

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

Definition at line 3725 of file trigger.c.

◆ for_each_event_from

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

Definition at line 3731 of file trigger.c.

◆ GetTriggerSharedData

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

Definition at line 3688 of file trigger.c.

◆ MAX_CHUNK_SIZE

#define MAX_CHUNK_SIZE   (1024*1024)

◆ MIN_CHUNK_SIZE

#define MIN_CHUNK_SIZE   1024

◆ SizeofTriggerEvent

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

Definition at line 3679 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3641 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3629 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3641 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3641 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3641 of file trigger.c.

◆ SetConstraintState

Definition at line 3572 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3551 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3617 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 4031 of file trigger.c.

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

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

Referenced by afterTriggerMarkEvents(), and AfterTriggerSaveEvent().

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 5020 of file trigger.c.

5021 {
5022  /* Increase the query stack depth */
5024 }

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5303 of file trigger.c.

5304 {
5305  int my_level = GetCurrentTransactionNestLevel();
5306 
5307  /*
5308  * Allocate more space in the trans_stack if needed. (Note: because the
5309  * minimum nest level of a subtransaction is 2, we waste the first couple
5310  * entries of the array; not worth the notational effort to avoid it.)
5311  */
5312  while (my_level >= afterTriggers.maxtransdepth)
5313  {
5314  if (afterTriggers.maxtransdepth == 0)
5315  {
5316  /* Arbitrarily initialize for max of 8 subtransaction levels */
5319  8 * sizeof(AfterTriggersTransData));
5321  }
5322  else
5323  {
5324  /* repalloc will keep the stack in the same context */
5325  int new_alloc = afterTriggers.maxtransdepth * 2;
5326 
5329  new_alloc * sizeof(AfterTriggersTransData));
5330  afterTriggers.maxtransdepth = new_alloc;
5331  }
5332  }
5333 
5334  /*
5335  * Push the current information into the stack. The SET CONSTRAINTS state
5336  * is not saved until/unless changed. Likewise, we don't make a
5337  * per-subtransaction event context until needed.
5338  */
5339  afterTriggers.trans_stack[my_level].state = NULL;
5343 }
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1541
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:927

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

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

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

Referenced by StartTransaction().

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

Definition at line 3954 of file trigger.c.

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

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

Referenced by afterTriggerMarkEvents().

◆ afterTriggerCopyBitmap()

static Bitmapset* afterTriggerCopyBitmap ( Bitmapset src)
static

Definition at line 3999 of file trigger.c.

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

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

Referenced by AfterTriggerSaveEvent().

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 4208 of file trigger.c.

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

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

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

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

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

Referenced by AbortSubTransaction(), and CommitSubTransaction().

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

Definition at line 5255 of file trigger.c.

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

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

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

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

Definition at line 5539 of file trigger.c.

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

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

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

◆ AfterTriggerExecute()

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

Definition at line 4270 of file trigger.c.

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

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

Referenced by afterTriggerInvokeEvents().

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

Definition at line 5199 of file trigger.c.

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

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

Referenced by CommitTransaction(), and PrepareTransaction().

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4147 of file trigger.c.

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

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 5131 of file trigger.c.

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

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

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

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

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

◆ afterTriggerMarkEvents()

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

Definition at line 4545 of file trigger.c.

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

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

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

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

Definition at line 5976 of file trigger.c.

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

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

Referenced by CheckTableNotInUse().

◆ afterTriggerRestoreEventList()

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

Definition at line 4168 of file trigger.c.

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

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

Referenced by AfterTriggerEndSubXact().

◆ AfterTriggerSaveEvent()

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

Definition at line 6063 of file trigger.c.

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

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

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

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5661 of file trigger.c.

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

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

6582 {
6583  /*
6584  * Must flush the plan cache when changing replication role; but don't
6585  * flush unnecessarily.
6586  */
6588  ResetPlanCache();
6589 }
#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 6460 of file trigger.c.

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

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

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

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

Referenced by AfterTriggerSaveEvent().

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

Definition at line 2085 of file trigger.c.

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

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

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

◆ CreateTrigger()

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

Definition at line 158 of file trigger.c.

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

References CreateTriggerFiringOn(), stmt, and TRIGGER_FIRES_ON_ORIGIN.

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

◆ CreateTriggerFiringOn()

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

Definition at line 175 of file trigger.c.

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

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

Referenced by CloneRowTriggersToPartition(), and CreateTrigger().

◆ EnableDisableTrigger()

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

Definition at line 1721 of file trigger.c.

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

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

Referenced by ATExecEnableDisableTrigger().

◆ ExecARDeleteTriggers()

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

Definition at line 2774 of file trigger.c.

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

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

Referenced by ExecDeleteEpilogue(), and ExecSimpleRelationDelete().

◆ ExecARInsertTriggers()

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

Definition at line 2534 of file trigger.c.

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

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

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

◆ ExecARUpdateTriggers()

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

Definition at line 3097 of file trigger.c.

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

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

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

◆ ExecASDeleteTriggers()

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

Definition at line 2663 of file trigger.c.

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

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

Referenced by fireASTriggers().

◆ ExecASInsertTriggers()

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

Definition at line 2445 of file trigger.c.

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

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

Referenced by CopyFrom(), and fireASTriggers().

◆ ExecASTruncateTriggers()

void ExecASTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 3269 of file trigger.c.

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

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

Referenced by ExecuteTruncateGuts().

◆ ExecASUpdateTriggers()

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

Definition at line 2916 of file trigger.c.

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

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

Referenced by fireASTriggers().

◆ ExecBRDeleteTriggers()

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

Definition at line 2683 of file trigger.c.

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

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

Referenced by ExecDeletePrologue(), and ExecSimpleRelationDelete().

◆ ExecBRInsertTriggers()

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

Definition at line 2458 of file trigger.c.

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

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

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

◆ ExecBRUpdateTriggers()

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

Definition at line 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:311
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2351
LockTupleMode
Definition: lockoptions.h:50
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:472

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:6460

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

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

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

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

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

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

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

◆ GetAfterTriggersTransitionTable()

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

Definition at line 5448 of file trigger.c.

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

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:129
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:328

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:2470
@ LockWaitBlock
Definition: lockoptions.h:39
@ CMD_MERGE
Definition: nodes.h:269
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition: pgbench.c:76
static void test(void)
PlannedStmt * es_plannedstmt
Definition: execnodes.h:633
CommandId es_output_cid
Definition: execnodes.h:639
Snapshot es_snapshot
Definition: execnodes.h:624
CmdType commandType
Definition: plannodes.h:52
Index ri_RangeTableIndex
Definition: execnodes.h:453
bool traversed
Definition: tableam.h:153
CommandId cmax
Definition: tableam.h:152
TM_Result
Definition: tableam.h:80
@ TM_Ok
Definition: tableam.h:85
@ TM_Deleted
Definition: tableam.h:100
@ TM_Updated
Definition: tableam.h:97
@ TM_SelfModified
Definition: tableam.h:91
@ TM_Invisible
Definition: tableam.h:88
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:1580
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:268
#define IsolationUsesXactSnapshot()
Definition: xact.h:51

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

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

◆ MakeTransitionCaptureState()

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

Definition at line 4884 of file trigger.c.

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

6596 {
6598 }
#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 1415 of file trigger.c.

1417 {
1418  HeapTuple tuple;
1419  Form_pg_class form;
1420 
1421  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1422  if (!HeapTupleIsValid(tuple))
1423  return; /* concurrently dropped */
1424  form = (Form_pg_class) GETSTRUCT(tuple);
1425 
1426  /* only tables and views can have triggers */
1427  if (form->relkind != RELKIND_RELATION && form->relkind != RELKIND_VIEW &&
1428  form->relkind != RELKIND_FOREIGN_TABLE &&
1429  form->relkind != RELKIND_PARTITIONED_TABLE)
1430  ereport(ERROR,
1431  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1432  errmsg("relation \"%s\" cannot have triggers",
1433  rv->relname),
1434  errdetail_relkind_not_supported(form->relkind)));
1435 
1436  /* you must own the table to rename one of its triggers */
1437  if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
1439  if (!allowSystemTableMods && IsSystemClass(relid, form))
1440  ereport(ERROR,
1441  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1442  errmsg("permission denied: \"%s\" is a system catalog",
1443  rv->relname)));
1444 
1445  ReleaseSysCache(tuple);
1446 }
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4142
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:670
MemoryContext CacheMemoryContext
Definition: mcxt.c:152
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:687
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 1286 of file trigger.c.

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

1463 {
1464  Oid tgoid;
1465  Relation targetrel;
1466  Relation tgrel;
1467  HeapTuple tuple;
1468  SysScanDesc tgscan;
1469  ScanKeyData key[2];
1470  Oid relid;
1471  ObjectAddress address;
1472 
1473  /*
1474  * Look up name, check permissions, and acquire lock (which we will NOT
1475  * release until end of transaction).
1476  */
1478  0,
1480  NULL);
1481 
1482  /* Have lock already, so just need to build relcache entry. */
1483  targetrel = relation_open(relid, NoLock);
1484 
1485  /*
1486  * On partitioned tables, this operation recurses to partitions. Lock all
1487  * tables upfront.
1488  */
1489  if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1490  (void) find_all_inheritors(relid, AccessExclusiveLock, NULL);
1491 
1492  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1493 
1494  /*
1495  * Search for the trigger to modify.
1496  */
1497  ScanKeyInit(&key[0],
1498  Anum_pg_trigger_tgrelid,
1499  BTEqualStrategyNumber, F_OIDEQ,
1500  ObjectIdGetDatum(relid));
1501  ScanKeyInit(&key[1],
1502  Anum_pg_trigger_tgname,
1503  BTEqualStrategyNumber, F_NAMEEQ,
1504  PointerGetDatum(stmt->subname));
1505  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1506  NULL, 2, key);
1507  if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1508  {
1509  Form_pg_trigger trigform;
1510 
1511  trigform = (Form_pg_trigger) GETSTRUCT(tuple);
1512  tgoid = trigform->oid;
1513 
1514  /*
1515  * If the trigger descends from a trigger on a parent partitioned
1516  * table, reject the rename. We don't allow a trigger in a partition
1517  * to differ in name from that of its parent: that would lead to an
1518  * inconsistency that pg_dump would not reproduce.
1519  */
1520  if (OidIsValid(trigform->tgparentid))
1521  ereport(ERROR,
1522  errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
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:1415
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 5631 of file trigger.c.

5633 {
5634  if (state->numstates >= state->numalloc)
5635  {
5636  int newalloc = state->numalloc * 2;
5637 
5638  newalloc = Max(newalloc, 8); /* in case original has size 0 */
5640  repalloc(state,
5641  offsetof(SetConstraintStateData, trigstates) +
5642  newalloc * sizeof(SetConstraintTriggerData));
5643  state->numalloc = newalloc;
5644  Assert(state->numstates < state->numalloc);
5645  }
5646 
5647  state->trigstates[state->numstates].sct_tgoid = tgoid;
5648  state->trigstates[state->numstates].sct_tgisdeferred = tgisdeferred;
5649  state->numstates++;
5650 
5651  return state;
5652 }
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 5611 of file trigger.c.

5612 {
5614 
5616 
5617  state->all_isset = origstate->all_isset;
5618  state->all_isdeferred = origstate->all_isdeferred;
5619  state->numstates = origstate->numstates;
5620  memcpy(state->trigstates, origstate->trigstates,
5621  origstate->numstates * sizeof(SetConstraintTriggerData));
5622 
5623  return state;
5624 }
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 5586 of file trigger.c.

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

References MemoryContextAllocZero(), and TopTransactionContext.

Referenced by AfterTriggerSetState(), and SetConstraintStateCopy().

◆ SetTriggerFlags()

static void SetTriggerFlags ( TriggerDesc trigdesc,
Trigger trigger 
)
static

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

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

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:767
#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:737
#define OUTER_VAR
Definition: primnodes.h:237
#define INNER_VAR
Definition: primnodes.h:236
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:667
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 1215 of file trigger.c.

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

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