PostgreSQL Source Code  git master
trigger.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/partition.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/bitmapset.h"
#include "nodes/makefuncs.h"
#include "optimizer/optimizer.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.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  AfterTriggerEventDataOneCtid
 
struct  AfterTriggerEventDataZeroCtids
 
struct  AfterTriggerEventChunk
 
struct  AfterTriggerEventList
 
struct  AfterTriggersData
 
struct  AfterTriggersQueryData
 
struct  AfterTriggersTransData
 
struct  AfterTriggersTableData
 

Macros

#define GetAllUpdatedColumns(relinfo, estate)
 
#define AFTER_TRIGGER_OFFSET   0x0FFFFFFF /* must be low-order bits */
 
#define AFTER_TRIGGER_DONE   0x10000000
 
#define AFTER_TRIGGER_IN_PROGRESS   0x20000000
 
#define AFTER_TRIGGER_FDW_REUSE   0x00000000
 
#define AFTER_TRIGGER_FDW_FETCH   0x80000000
 
#define AFTER_TRIGGER_1CTID   0x40000000
 
#define AFTER_TRIGGER_2CTID   0xC0000000
 
#define AFTER_TRIGGER_TUP_BITS   0xC0000000
 
#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 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 SetTriggerFlags (TriggerDesc *trigdesc, Trigger *trigger)
 
static bool GetTupleForTrigger (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **newSlot)
 
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, int event, bool row_trigger, TupleTableSlot *oldtup, TupleTableSlot *newtup, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture)
 
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)
 
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, char fires_when, bool skip_system, 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)
 
void ExecARDeleteTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
 
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)
 
void ExecARUpdateTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
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, 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 void AfterTriggerFreeQuery (AfterTriggersQueryData *qs)
 
static SetConstraintState SetConstraintStateCreate (int numalloc)
 
static SetConstraintState SetConstraintStateCopy (SetConstraintState state)
 
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 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)
 
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   0x40000000

Definition at line 3263 of file trigger.c.

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

Definition at line 3264 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x10000000

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x80000000

Definition at line 3262 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3261 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x20000000

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3257 of file trigger.c.

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3265 of file trigger.c.

Referenced by AfterTriggerExecute().

◆ CHUNK_DATA_START

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

◆ for_each_chunk

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

Definition at line 3338 of file trigger.c.

Referenced by afterTriggerInvokeEvents().

◆ for_each_chunk_from

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

Definition at line 3349 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

◆ 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:3327
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3301
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3279

Definition at line 3340 of file trigger.c.

Referenced by afterTriggerInvokeEvents().

◆ for_each_event_chunk

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

◆ for_each_event_from

#define for_each_event_from (   eptr,
  cptr 
)
Value:
for (; \
(char *) eptr < (cptr)->freeptr; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3301
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3279

Definition at line 3351 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

◆ GetAllUpdatedColumns

#define GetAllUpdatedColumns (   relinfo,
  estate 
)
Value:
(bms_union(exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->updatedCols, \
exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->extraUpdatedCols))
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:542
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:225

Definition at line 78 of file trigger.c.

Referenced by ExecARUpdateTriggers(), ExecASUpdateTriggers(), ExecBRUpdateTriggers(), and ExecBSUpdateTriggers().

◆ GetTriggerSharedData

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

◆ MAX_CHUNK_SIZE

#define MAX_CHUNK_SIZE   (1024*1024)

Referenced by afterTriggerAddEvent().

◆ MIN_CHUNK_SIZE

#define MIN_CHUNK_SIZE   1024

Referenced by afterTriggerAddEvent().

◆ SizeofTriggerEvent

#define SizeofTriggerEvent (   evt)
Value:
(((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:3265
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3263
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3264

Definition at line 3301 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3279 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3267 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3432 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3434 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3433 of file trigger.c.

◆ SetConstraintState

Definition at line 3215 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3194 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3255 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 3591 of file trigger.c.

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

Referenced by afterTriggerMarkEvents(), and AfterTriggerSaveEvent().

3593 {
3594  Size eventsize = SizeofTriggerEvent(event);
3595  Size needed = eventsize + sizeof(AfterTriggerSharedData);
3596  AfterTriggerEventChunk *chunk;
3597  AfterTriggerShared newshared;
3598  AfterTriggerEvent newevent;
3599 
3600  /*
3601  * If empty list or not enough room in the tail chunk, make a new chunk.
3602  * We assume here that a new shared record will always be needed.
3603  */
3604  chunk = events->tail;
3605  if (chunk == NULL ||
3606  chunk->endfree - chunk->freeptr < needed)
3607  {
3608  Size chunksize;
3609 
3610  /* Create event context if we didn't already */
3611  if (afterTriggers.event_cxt == NULL)
3614  "AfterTriggerEvents",
3616 
3617  /*
3618  * Chunk size starts at 1KB and is allowed to increase up to 1MB.
3619  * These numbers are fairly arbitrary, though there is a hard limit at
3620  * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
3621  * shared records using the available space in ate_flags. Another
3622  * constraint is that if the chunk size gets too huge, the search loop
3623  * below would get slow given a (not too common) usage pattern with
3624  * many distinct event types in a chunk. Therefore, we double the
3625  * preceding chunk size only if there weren't too many shared records
3626  * in the preceding chunk; otherwise we halve it. This gives us some
3627  * ability to adapt to the actual usage pattern of the current query
3628  * while still having large chunk sizes in typical usage. All chunk
3629  * sizes used should be MAXALIGN multiples, to ensure that the shared
3630  * records will be aligned safely.
3631  */
3632 #define MIN_CHUNK_SIZE 1024
3633 #define MAX_CHUNK_SIZE (1024*1024)
3634 
3635 #if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
3636 #error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
3637 #endif
3638 
3639  if (chunk == NULL)
3640  chunksize = MIN_CHUNK_SIZE;
3641  else
3642  {
3643  /* preceding chunk size... */
3644  chunksize = chunk->endptr - (char *) chunk;
3645  /* check number of shared records in preceding chunk */
3646  if ((chunk->endptr - chunk->endfree) <=
3647  (100 * sizeof(AfterTriggerSharedData)))
3648  chunksize *= 2; /* okay, double it */
3649  else
3650  chunksize /= 2; /* too many shared records */
3651  chunksize = Min(chunksize, MAX_CHUNK_SIZE);
3652  }
3653  chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize);
3654  chunk->next = NULL;
3655  chunk->freeptr = CHUNK_DATA_START(chunk);
3656  chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
3657  Assert(chunk->endfree - chunk->freeptr >= needed);
3658 
3659  if (events->head == NULL)
3660  events->head = chunk;
3661  else
3662  events->tail->next = chunk;
3663  events->tail = chunk;
3664  /* events->tailfree is now out of sync, but we'll fix it below */
3665  }
3666 
3667  /*
3668  * Try to locate a matching shared-data record already in the chunk. If
3669  * none, make a new one.
3670  */
3671  for (newshared = ((AfterTriggerShared) chunk->endptr) - 1;
3672  (char *) newshared >= chunk->endfree;
3673  newshared--)
3674  {
3675  if (newshared->ats_tgoid == evtshared->ats_tgoid &&
3676  newshared->ats_relid == evtshared->ats_relid &&
3677  newshared->ats_event == evtshared->ats_event &&
3678  newshared->ats_table == evtshared->ats_table &&
3679  newshared->ats_firing_id == 0)
3680  break;
3681  }
3682  if ((char *) newshared < chunk->endfree)
3683  {
3684  *newshared = *evtshared;
3685  newshared->ats_firing_id = 0; /* just to be sure */
3686  chunk->endfree = (char *) newshared;
3687  }
3688 
3689  /* Insert the data */
3690  newevent = (AfterTriggerEvent) chunk->freeptr;
3691  memcpy(newevent, event, eventsize);
3692  /* ... and link the new event to its shared record */
3693  newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
3694  newevent->ate_flags |= (char *) newshared - (char *) newevent;
3695 
3696  chunk->freeptr += eventsize;
3697  events->tailfree = chunk->freeptr;
3698 }
TriggerEvent ats_event
Definition: trigger.c:3271
#define AllocSetContextCreate
Definition: memutils.h:170
MemoryContext TopTransactionContext
Definition: mcxt.c:49
#define MIN_CHUNK_SIZE
TriggerFlags ate_flags
Definition: trigger.c:3283
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3327
struct AfterTriggerSharedData AfterTriggerSharedData
#define Min(x, y)
Definition: c.h:920
AfterTriggerEventChunk * tail
Definition: trigger.c:3333
struct AfterTriggerEventChunk * next
Definition: trigger.c:3320
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3301
#define AFTER_TRIGGER_OFFSET
Definition: trigger.c:3257
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3275
#define Assert(condition)
Definition: c.h:738
size_t Size
Definition: c.h:466
CommandId ats_firing_id
Definition: trigger.c:3274
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3279
#define MAX_CHUNK_SIZE
AfterTriggerEventChunk * head
Definition: trigger.c:3332
MemoryContext event_cxt
Definition: trigger.c:3441
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4432 of file trigger.c.

References AfterTriggersData::query_depth.

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

4433 {
4434  /* Increase the query stack depth */
4436 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 4700 of file trigger.c.

References 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().

4701 {
4702  int my_level = GetCurrentTransactionNestLevel();
4703 
4704  /*
4705  * Allocate more space in the trans_stack if needed. (Note: because the
4706  * minimum nest level of a subtransaction is 2, we waste the first couple
4707  * entries of the array; not worth the notational effort to avoid it.)
4708  */
4709  while (my_level >= afterTriggers.maxtransdepth)
4710  {
4711  if (afterTriggers.maxtransdepth == 0)
4712  {
4713  /* Arbitrarily initialize for max of 8 subtransaction levels */
4716  8 * sizeof(AfterTriggersTransData));
4718  }
4719  else
4720  {
4721  /* repalloc will keep the stack in the same context */
4722  int new_alloc = afterTriggers.maxtransdepth * 2;
4723 
4726  new_alloc * sizeof(AfterTriggersTransData));
4727  afterTriggers.maxtransdepth = new_alloc;
4728  }
4729  }
4730 
4731  /*
4732  * Push the current information into the stack. The SET CONSTRAINTS state
4733  * is not saved until/unless changed. Likewise, we don't make a
4734  * per-subtransaction event context until needed.
4735  */
4736  afterTriggers.trans_stack[my_level].state = NULL;
4740 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3449
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggerEventList events
Definition: trigger.c:3464
CommandId firing_counter
Definition: trigger.c:3466
CommandId firing_counter
Definition: trigger.c:3438
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:841
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
AfterTriggerEventList events
Definition: trigger.c:3440
static AfterTriggersData afterTriggers
Definition: trigger.c:3483
SetConstraintState state
Definition: trigger.c:3463

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

Definition at line 4400 of file trigger.c.

References 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().

4401 {
4402  /*
4403  * Initialize after-trigger state structure to empty
4404  */
4405  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4407 
4408  /*
4409  * Verify that there is no leftover state remaining. If these assertions
4410  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4411  * up properly.
4412  */
4413  Assert(afterTriggers.state == NULL);
4414  Assert(afterTriggers.query_stack == NULL);
4416  Assert(afterTriggers.event_cxt == NULL);
4417  Assert(afterTriggers.events.head == NULL);
4418  Assert(afterTriggers.trans_stack == NULL);
4420 }
uint32 CommandId
Definition: c.h:527
AfterTriggersTransData * trans_stack
Definition: trigger.c:3449
AfterTriggersQueryData * query_stack
Definition: trigger.c:3444
SetConstraintState state
Definition: trigger.c:3439
CommandId firing_counter
Definition: trigger.c:3438
#define Assert(condition)
Definition: c.h:738
AfterTriggerEventChunk * head
Definition: trigger.c:3332
MemoryContext event_cxt
Definition: trigger.c:3441
AfterTriggerEventList events
Definition: trigger.c:3440
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

Definition at line 3545 of file trigger.c.

References AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_INITDEFERRED, SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_tgoid, i, SetConstraintStateData::numstates, SetConstraintTriggerData::sct_tgisdeferred, SetConstraintTriggerData::sct_tgoid, AfterTriggersData::state, and SetConstraintStateData::trigstates.

Referenced by afterTriggerMarkEvents().

3546 {
3547  Oid tgoid = evtshared->ats_tgoid;
3549  int i;
3550 
3551  /*
3552  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3553  * constraints declared NOT DEFERRABLE), the state is always false.
3554  */
3555  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3556  return false;
3557 
3558  /*
3559  * If constraint state exists, SET CONSTRAINTS might have been executed
3560  * either for this trigger or for all triggers.
3561  */
3562  if (state != NULL)
3563  {
3564  /* Check for SET CONSTRAINTS for this specific trigger. */
3565  for (i = 0; i < state->numstates; i++)
3566  {
3567  if (state->trigstates[i].sct_tgoid == tgoid)
3568  return state->trigstates[i].sct_tgisdeferred;
3569  }
3570 
3571  /* Check for SET CONSTRAINTS ALL. */
3572  if (state->all_isset)
3573  return state->all_isdeferred;
3574  }
3575 
3576  /*
3577  * Otherwise return the default state for the trigger.
3578  */
3579  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3580 }
TriggerEvent ats_event
Definition: trigger.c:3271
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:115
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:114
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3212
unsigned int Oid
Definition: postgres_ext.h:31
SetConstraintState state
Definition: trigger.c:3439
Definition: regguts.h:298
int i
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 3768 of file trigger.c.

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().

3769 {
3770  AfterTriggerEventChunk *target = qs->events.head;
3771  ListCell *lc;
3772 
3773  Assert(target && target->next);
3774 
3775  /*
3776  * First, update any pointers in the per-table data, so that they won't be
3777  * dangling. Resetting obsoleted pointers to NULL will make
3778  * cancel_prior_stmt_triggers start from the list head, which is fine.
3779  */
3780  foreach(lc, qs->tables)
3781  {
3783 
3784  if (table->after_trig_done &&
3785  table->after_trig_events.tail == target)
3786  {
3787  table->after_trig_events.head = NULL;
3788  table->after_trig_events.tail = NULL;
3789  table->after_trig_events.tailfree = NULL;
3790  }
3791  }
3792 
3793  /* Now we can flush the head chunk */
3794  qs->events.head = target->next;
3795  pfree(target);
3796 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3333
struct AfterTriggerEventChunk * next
Definition: trigger.c:3320
void pfree(void *pointer)
Definition: mcxt.c:1056
AfterTriggerEventList after_trig_events
Definition: trigger.c:3477
#define Assert(condition)
Definition: c.h:738
#define lfirst(lc)
Definition: pg_list.h:190
AfterTriggerEventChunk * head
Definition: trigger.c:3332
AfterTriggerEventList events
Definition: trigger.c:3455

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 4452 of file trigger.c.

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

Referenced by apply_handle_delete(), apply_handle_insert(), apply_handle_update(), CopyFrom(), ExecuteTruncateGuts(), and standard_ExecutorFinish().

4453 {
4455 
4456  /* Must be inside a query, too */
4458 
4459  /*
4460  * If we never even got as far as initializing the event stack, there
4461  * certainly won't be any events, so exit quickly.
4462  */
4464  {
4466  return;
4467  }
4468 
4469  /*
4470  * Process all immediate-mode triggers queued by the query, and move the
4471  * deferred ones to the main list of deferred events.
4472  *
4473  * Notice that we decide which ones will be fired, and put the deferred
4474  * ones on the main list, before anything is actually fired. This ensures
4475  * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
4476  * IMMEDIATE: all events we have decided to defer will be available for it
4477  * to fire.
4478  *
4479  * We loop in case a trigger queues more events at the same query level.
4480  * Ordinary trigger functions, including all PL/pgSQL trigger functions,
4481  * will instead fire any triggers in a dedicated query level. Foreign key
4482  * enforcement triggers do add to the current query level, thanks to their
4483  * passing fire_triggers = false to SPI_execute_snapshot(). Other
4484  * C-language triggers might do likewise.
4485  *
4486  * If we find no firable events, we don't have to increment
4487  * firing_counter.
4488  */
4490 
4491  for (;;)
4492  {
4494  {
4495  CommandId firing_id = afterTriggers.firing_counter++;
4496  AfterTriggerEventChunk *oldtail = qs->events.tail;
4497 
4498  if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
4499  break; /* all fired */
4500 
4501  /*
4502  * Firing a trigger could result in query_stack being repalloc'd,
4503  * so we must recalculate qs after each afterTriggerInvokeEvents
4504  * call. Furthermore, it's unsafe to pass delete_ok = true here,
4505  * because that could cause afterTriggerInvokeEvents to try to
4506  * access qs->events after the stack has been repalloc'd.
4507  */
4509 
4510  /*
4511  * We'll need to scan the events list again. To reduce the cost
4512  * of doing so, get rid of completely-fired chunks. We know that
4513  * all events were marked IN_PROGRESS or DONE at the conclusion of
4514  * afterTriggerMarkEvents, so any still-interesting events must
4515  * have been added after that, and so must be in the chunk that
4516  * was then the tail chunk, or in later chunks. So, zap all
4517  * chunks before oldtail. This is approximately the same set of
4518  * events we would have gotten rid of by passing delete_ok = true.
4519  */
4520  Assert(oldtail != NULL);
4521  while (qs->events.head != oldtail)
4523  }
4524  else
4525  break;
4526  }
4527 
4528  /* Release query-level-local storage, including tuplestores if any */
4530 
4532 }
uint32 CommandId
Definition: c.h:527
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition: trigger.c:3768
AfterTriggersQueryData * query_stack
Definition: trigger.c:3444
AfterTriggerEventChunk * tail
Definition: trigger.c:3333
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4108
CommandId firing_counter
Definition: trigger.c:3438
#define Assert(condition)
Definition: c.h:738
AfterTriggerEventChunk * head
Definition: trigger.c:3332
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4036
AfterTriggerEventList events
Definition: trigger.c:3440
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4543
AfterTriggerEventList events
Definition: trigger.c:3455
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

Definition at line 4748 of file trigger.c.

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

Referenced by AbortSubTransaction(), and CommitSubTransaction().

4749 {
4750  int my_level = GetCurrentTransactionNestLevel();
4752  AfterTriggerEvent event;
4753  AfterTriggerEventChunk *chunk;
4754  CommandId subxact_firing_id;
4755 
4756  /*
4757  * Pop the prior state if needed.
4758  */
4759  if (isCommit)
4760  {
4761  Assert(my_level < afterTriggers.maxtransdepth);
4762  /* If we saved a prior state, we don't need it anymore */
4763  state = afterTriggers.trans_stack[my_level].state;
4764  if (state != NULL)
4765  pfree(state);
4766  /* this avoids double pfree if error later: */
4767  afterTriggers.trans_stack[my_level].state = NULL;
4770  }
4771  else
4772  {
4773  /*
4774  * Aborting. It is possible subxact start failed before calling
4775  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
4776  * trans_stack levels that aren't there.
4777  */
4778  if (my_level >= afterTriggers.maxtransdepth)
4779  return;
4780 
4781  /*
4782  * Release query-level storage for queries being aborted, and restore
4783  * query_depth to its pre-subxact value. This assumes that a
4784  * subtransaction will not add events to query levels started in a
4785  * earlier transaction state.
4786  */
4788  {
4792  }
4795 
4796  /*
4797  * Restore the global deferred-event list to its former length,
4798  * discarding any events queued by the subxact.
4799  */
4801  &afterTriggers.trans_stack[my_level].events);
4802 
4803  /*
4804  * Restore the trigger state. If the saved state is NULL, then this
4805  * subxact didn't save it, so it doesn't need restoring.
4806  */
4807  state = afterTriggers.trans_stack[my_level].state;
4808  if (state != NULL)
4809  {
4811  afterTriggers.state = state;
4812  }
4813  /* this avoids double pfree if error later: */
4814  afterTriggers.trans_stack[my_level].state = NULL;
4815 
4816  /*
4817  * Scan for any remaining deferred events that were marked DONE or IN
4818  * PROGRESS by this subxact or a child, and un-mark them. We can
4819  * recognize such events because they have a firing ID greater than or
4820  * equal to the firing_counter value we saved at subtransaction start.
4821  * (This essentially assumes that the current subxact includes all
4822  * subxacts started after it.)
4823  */
4824  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
4826  {
4827  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4828 
4829  if (event->ate_flags &
4831  {
4832  if (evtshared->ats_firing_id >= subxact_firing_id)
4833  event->ate_flags &=
4835  }
4836  }
4837  }
4838 }
uint32 CommandId
Definition: c.h:527
AfterTriggersTransData * trans_stack
Definition: trigger.c:3449
TriggerFlags ate_flags
Definition: trigger.c:3283
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3258
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3345
AfterTriggerEventList events
Definition: trigger.c:3464
AfterTriggersQueryData * query_stack
Definition: trigger.c:3444
CommandId firing_counter
Definition: trigger.c:3466
#define GetTriggerSharedData(evt)
Definition: trigger.c:3308
void pfree(void *pointer)
Definition: mcxt.c:1056
SetConstraintState state
Definition: trigger.c:3439
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:841
#define Assert(condition)
Definition: c.h:738
Definition: regguts.h:298
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:3728
CommandId ats_firing_id
Definition: trigger.c:3274
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3259
AfterTriggerEventList events
Definition: trigger.c:3440
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4543
static AfterTriggersData afterTriggers
Definition: trigger.c:3483
SetConstraintState state
Definition: trigger.c:3463

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

Definition at line 4652 of file trigger.c.

References 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().

4653 {
4654  /*
4655  * Forget the pending-events list.
4656  *
4657  * Since all the info is in TopTransactionContext or children thereof, we
4658  * don't really need to do anything to reclaim memory. However, the
4659  * pending-events list could be large, and so it's useful to discard it as
4660  * soon as possible --- especially if we are aborting because we ran out
4661  * of memory for the list!
4662  */
4664  {
4666  afterTriggers.event_cxt = NULL;
4667  afterTriggers.events.head = NULL;
4668  afterTriggers.events.tail = NULL;
4669  afterTriggers.events.tailfree = NULL;
4670  }
4671 
4672  /*
4673  * Forget any subtransaction state as well. Since this can't be very
4674  * large, we let the eventual reset of TopTransactionContext free the
4675  * memory instead of doing it here.
4676  */
4677  afterTriggers.trans_stack = NULL;
4679 
4680 
4681  /*
4682  * Forget the query stack and constraint-related state information. As
4683  * with the subtransaction state information, we don't bother freeing the
4684  * memory here.
4685  */
4686  afterTriggers.query_stack = NULL;
4688  afterTriggers.state = NULL;
4689 
4690  /* No more afterTriggers manipulation until next transaction starts. */
4692 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
AfterTriggersTransData * trans_stack
Definition: trigger.c:3449
AfterTriggersQueryData * query_stack
Definition: trigger.c:3444
AfterTriggerEventChunk * tail
Definition: trigger.c:3333
SetConstraintState state
Definition: trigger.c:3439
AfterTriggerEventChunk * head
Definition: trigger.c:3332
MemoryContext event_cxt
Definition: trigger.c:3441
AfterTriggerEventList events
Definition: trigger.c:3440
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

Definition at line 4850 of file trigger.c.

References 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().

4851 {
4852  int init_depth = afterTriggers.maxquerydepth;
4853 
4855 
4856  if (afterTriggers.maxquerydepth == 0)
4857  {
4858  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
4859 
4862  new_alloc * sizeof(AfterTriggersQueryData));
4863  afterTriggers.maxquerydepth = new_alloc;
4864  }
4865  else
4866  {
4867  /* repalloc will keep the stack in the same context */
4868  int old_alloc = afterTriggers.maxquerydepth;
4869  int new_alloc = Max(afterTriggers.query_depth + 1,
4870  old_alloc * 2);
4871 
4874  new_alloc * sizeof(AfterTriggersQueryData));
4875  afterTriggers.maxquerydepth = new_alloc;
4876  }
4877 
4878  /* Initialize new array entries to empty */
4879  while (init_depth < afterTriggers.maxquerydepth)
4880  {
4882 
4883  qs->events.head = NULL;
4884  qs->events.tail = NULL;
4885  qs->events.tailfree = NULL;
4886  qs->fdw_tuplestore = NULL;
4887  qs->tables = NIL;
4888 
4889  ++init_depth;
4890  }
4891 }
#define NIL
Definition: pg_list.h:65
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggersQueryData * query_stack
Definition: trigger.c:3444
AfterTriggerEventChunk * tail
Definition: trigger.c:3333
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3456
#define Max(x, y)
Definition: c.h:914
#define Assert(condition)
Definition: c.h:738
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069
AfterTriggerEventChunk * head
Definition: trigger.c:3332
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
AfterTriggerEventList events
Definition: trigger.c:3455
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ AfterTriggerExecute()

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

Definition at line 3822 of file trigger.c.

References AFTER_TRIGGER_2CTID, 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, AfterTriggersTableData::closed, elog, ERROR, ExecCallTriggerFunc(), ExecClearTuple(), ExecFetchSlotHeapTuple(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), GetCurrentFDWTuplestore(), GetTriggerSharedData, heap_freetuple(), InstrStartNode(), InstrStopNode(), ItemPointerIsValid, MemoryContextReset(), AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, ResultRelInfo::ri_RelationDesc, SnapshotAny, T_TriggerData, 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, TriggerDesc::triggers, tuplestore_gettupleslot(), and TriggerData::type.

Referenced by afterTriggerInvokeEvents().

3830 {
3831  Relation rel = relInfo->ri_RelationDesc;
3832  AfterTriggerShared evtshared = GetTriggerSharedData(event);
3833  Oid tgoid = evtshared->ats_tgoid;
3834  TriggerData LocTriggerData = {0};
3835  HeapTuple rettuple;
3836  int tgindx;
3837  bool should_free_trig = false;
3838  bool should_free_new = false;
3839 
3840  /*
3841  * Locate trigger in trigdesc.
3842  */
3843  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
3844  {
3845  if (trigdesc->triggers[tgindx].tgoid == tgoid)
3846  {
3847  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
3848  break;
3849  }
3850  }
3851  if (LocTriggerData.tg_trigger == NULL)
3852  elog(ERROR, "could not find trigger %u", tgoid);
3853 
3854  /*
3855  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
3856  * to include time spent re-fetching tuples in the trigger cost.
3857  */
3858  if (instr)
3859  InstrStartNode(instr + tgindx);
3860 
3861  /*
3862  * Fetch the required tuple(s).
3863  */
3864  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
3865  {
3867  {
3868  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
3869 
3870  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
3871  trig_tuple_slot1))
3872  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
3873 
3874  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
3876  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
3877  trig_tuple_slot2))
3878  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
3879  }
3880  /* fall through */
3882 
3883  /*
3884  * Store tuple in the slot so that tg_trigtuple does not reference
3885  * tuplestore memory. (It is formally possible for the trigger
3886  * function to queue trigger events that add to the same
3887  * tuplestore, which can push other tuples out of memory.) The
3888  * distinction is academic, because we start with a minimal tuple
3889  * that is stored as a heap tuple, constructed in different memory
3890  * context, in the slot anyway.
3891  */
3892  LocTriggerData.tg_trigslot = trig_tuple_slot1;
3893  LocTriggerData.tg_trigtuple =
3894  ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
3895 
3896  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
3898  {
3899  LocTriggerData.tg_newslot = trig_tuple_slot2;
3900  LocTriggerData.tg_newtuple =
3901  ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new);
3902  }
3903  else
3904  {
3905  LocTriggerData.tg_newtuple = NULL;
3906  }
3907  break;
3908 
3909  default:
3910  if (ItemPointerIsValid(&(event->ate_ctid1)))
3911  {
3912  LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
3913 
3914  if (!table_tuple_fetch_row_version(rel, &(event->ate_ctid1),
3915  SnapshotAny,
3916  LocTriggerData.tg_trigslot))
3917  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
3918  LocTriggerData.tg_trigtuple =
3919  ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, &should_free_trig);
3920  }
3921  else
3922  {
3923  LocTriggerData.tg_trigtuple = NULL;
3924  }
3925 
3926  /* don't touch ctid2 if not there */
3927  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
3929  ItemPointerIsValid(&(event->ate_ctid2)))
3930  {
3931  LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
3932 
3933  if (!table_tuple_fetch_row_version(rel, &(event->ate_ctid2),
3934  SnapshotAny,
3935  LocTriggerData.tg_newslot))
3936  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
3937  LocTriggerData.tg_newtuple =
3938  ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, &should_free_new);
3939  }
3940  else
3941  {
3942  LocTriggerData.tg_newtuple = NULL;
3943  }
3944  }
3945 
3946  /*
3947  * Set up the tuplestore information to let the trigger have access to
3948  * transition tables. When we first make a transition table available to
3949  * a trigger, mark it "closed" so that it cannot change anymore. If any
3950  * additional events of the same type get queued in the current trigger
3951  * query level, they'll go into new transition tables.
3952  */
3953  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
3954  if (evtshared->ats_table)
3955  {
3956  if (LocTriggerData.tg_trigger->tgoldtable)
3957  {
3958  LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
3959  evtshared->ats_table->closed = true;
3960  }
3961 
3962  if (LocTriggerData.tg_trigger->tgnewtable)
3963  {
3964  LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
3965  evtshared->ats_table->closed = true;
3966  }
3967  }
3968 
3969  /*
3970  * Setup the remaining trigger information
3971  */
3972  LocTriggerData.type = T_TriggerData;
3973  LocTriggerData.tg_event =
3975  LocTriggerData.tg_relation = rel;
3976  if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
3977  LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
3978 
3979  MemoryContextReset(per_tuple_context);
3980 
3981  /*
3982  * Call the trigger and throw away any possibly returned updated tuple.
3983  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
3984  */
3985  rettuple = ExecCallTriggerFunc(&LocTriggerData,
3986  tgindx,
3987  finfo,
3988  NULL,
3989  per_tuple_context);
3990  if (rettuple != NULL &&
3991  rettuple != LocTriggerData.tg_trigtuple &&
3992  rettuple != LocTriggerData.tg_newtuple)
3993  heap_freetuple(rettuple);
3994 
3995  /*
3996  * Release resources
3997  */
3998  if (should_free_trig)
3999  heap_freetuple(LocTriggerData.tg_trigtuple);
4000  if (should_free_new)
4001  heap_freetuple(LocTriggerData.tg_newtuple);
4002 
4003  /* don't clear slots' contents if foreign table */
4004  if (trig_tuple_slot1 == NULL)
4005  {
4006  if (LocTriggerData.tg_trigslot)
4007  ExecClearTuple(LocTriggerData.tg_trigslot);
4008  if (LocTriggerData.tg_newslot)
4009  ExecClearTuple(LocTriggerData.tg_newslot);
4010  }
4011 
4012  /*
4013  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4014  * one "tuple returned" (really the number of firings).
4015  */
4016  if (instr)
4017  InstrStopNode(instr + tgindx, 1);
4018 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define TRIGGER_EVENT_ROW
Definition: trigger.h:105
TriggerEvent ats_event
Definition: trigger.c:3271
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:74
TupleTableSlot * tg_trigslot
Definition: trigger.h:38
Relation ri_RelationDesc
Definition: execnodes.h:413
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3261
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3509
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
ItemPointerData ate_ctid2
Definition: trigger.c:3285
TriggerFlags ate_flags
Definition: trigger.c:3283
const Bitmapset * tg_updatedcols
Definition: trigger.h:42
Oid tgoid
Definition: reltrigger.h:25
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3265
Tuplestorestate * old_tuplestore
Definition: trigger.c:3478
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1104
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:103
TupleTableSlot * ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1126
HeapTuple tg_trigtuple
Definition: trigger.h:35
#define GetTriggerSharedData(evt)
Definition: trigger.c:3308
Bitmapset * ats_modifiedcols
Definition: trigger.c:3276
#define ERROR
Definition: elog.h:43
void InstrStartNode(Instrumentation *instr)
Definition: instrument.c:61
int16 tgtype
Definition: reltrigger.h:29
Trigger * triggers
Definition: reltrigger.h:49
TupleTableSlot * tg_newslot
Definition: trigger.h:39
Tuplestorestate * new_tuplestore
Definition: trigger.c:3479
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3264
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1614
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1053
int numtriggers
Definition: reltrigger.h:50
char * tgnewtable
Definition: reltrigger.h:44
Trigger * tg_trigger
Definition: trigger.h:37
HeapTuple tg_newtuple
Definition: trigger.h:36
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3275
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
TriggerEvent tg_event
Definition: trigger.h:33
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3262
#define SnapshotAny
Definition: snapmgr.h:69
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:101
Tuplestorestate * tg_oldtable
Definition: trigger.h:40
NodeTag type
Definition: trigger.h:32
#define elog(elevel,...)
Definition: elog.h:214
Tuplestorestate * tg_newtable
Definition: trigger.h:41
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition: trigger.c:2038
ItemPointerData ate_ctid1
Definition: trigger.c:3284
char * tgoldtable
Definition: reltrigger.h:43
Relation tg_relation
Definition: trigger.h:34

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

Definition at line 4596 of file trigger.c.

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

Referenced by CommitTransaction(), and PrepareTransaction().

4597 {
4598  AfterTriggerEventList *events;
4599  bool snap_pushed = false;
4600 
4601  /* Must not be inside a query */
4603 
4604  /*
4605  * If there are any triggers to fire, make sure we have set a snapshot for
4606  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4607  * can't assume ActiveSnapshot is valid on entry.)
4608  */
4609  events = &afterTriggers.events;
4610  if (events->head != NULL)
4611  {
4613  snap_pushed = true;
4614  }
4615 
4616  /*
4617  * Run all the remaining triggers. Loop until they are all gone, in case
4618  * some trigger queues more for us to do.
4619  */
4620  while (afterTriggerMarkEvents(events, NULL, false))
4621  {
4622  CommandId firing_id = afterTriggers.firing_counter++;
4623 
4624  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4625  break; /* all fired */
4626  }
4627 
4628  /*
4629  * We don't bother freeing the event list, since it will go away anyway
4630  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4631  */
4632 
4633  if (snap_pushed)
4635 }
uint32 CommandId
Definition: c.h:527
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4108
void PopActiveSnapshot(void)
Definition: snapmgr.c:814
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:306
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:735
CommandId firing_counter
Definition: trigger.c:3438
#define Assert(condition)
Definition: c.h:738
AfterTriggerEventChunk * head
Definition: trigger.c:3332
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4036
AfterTriggerEventList events
Definition: trigger.c:3440
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 3707 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

3708 {
3709  AfterTriggerEventChunk *chunk;
3710 
3711  while ((chunk = events->head) != NULL)
3712  {
3713  events->head = chunk->next;
3714  pfree(chunk);
3715  }
3716  events->tail = NULL;
3717  events->tailfree = NULL;
3718 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3333
struct AfterTriggerEventChunk * next
Definition: trigger.c:3320
void pfree(void *pointer)
Definition: mcxt.c:1056
AfterTriggerEventChunk * head
Definition: trigger.c:3332

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 4543 of file trigger.c.

References afterTriggerFreeEventList(), AfterTriggersQueryData::events, AfterTriggersQueryData::fdw_tuplestore, lfirst, list_free_deep(), AfterTriggersTableData::new_tuplestore, NIL, AfterTriggersTableData::old_tuplestore, AfterTriggersQueryData::tables, and tuplestore_end().

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

4544 {
4545  Tuplestorestate *ts;
4546  List *tables;
4547  ListCell *lc;
4548 
4549  /* Drop the trigger events */
4551 
4552  /* Drop FDW tuplestore if any */
4553  ts = qs->fdw_tuplestore;
4554  qs->fdw_tuplestore = NULL;
4555  if (ts)
4556  tuplestore_end(ts);
4557 
4558  /* Release per-table subsidiary storage */
4559  tables = qs->tables;
4560  foreach(lc, tables)
4561  {
4563 
4564  ts = table->old_tuplestore;
4565  table->old_tuplestore = NULL;
4566  if (ts)
4567  tuplestore_end(ts);
4568  ts = table->new_tuplestore;
4569  table->new_tuplestore = NULL;
4570  if (ts)
4571  tuplestore_end(ts);
4572  }
4573 
4574  /*
4575  * Now free the AfterTriggersTableData structs and list cells. Reset list
4576  * pointer first; if list_free_deep somehow gets an error, better to leak
4577  * that storage than have an infinite loop.
4578  */
4579  qs->tables = NIL;
4580  list_free_deep(tables);
4581 }
#define NIL
Definition: pg_list.h:65
Tuplestorestate * old_tuplestore
Definition: trigger.c:3478
void list_free_deep(List *list)
Definition: list.c:1391
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3707
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3456
Tuplestorestate * new_tuplestore
Definition: trigger.c:3479
#define lfirst(lc)
Definition: pg_list.h:190
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3455
Definition: pg_list.h:50

◆ afterTriggerInvokeEvents()

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

Definition at line 4108 of file trigger.c.

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

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

4112 {
4113  bool all_fired = true;
4114  AfterTriggerEventChunk *chunk;
4115  MemoryContext per_tuple_context;
4116  bool local_estate = false;
4117  ResultRelInfo *rInfo = NULL;
4118  Relation rel = NULL;
4119  TriggerDesc *trigdesc = NULL;
4120  FmgrInfo *finfo = NULL;
4121  Instrumentation *instr = NULL;
4122  TupleTableSlot *slot1 = NULL,
4123  *slot2 = NULL;
4124 
4125  /* Make a local EState if need be */
4126  if (estate == NULL)
4127  {
4128  estate = CreateExecutorState();
4129  local_estate = true;
4130  }
4131 
4132  /* Make a per-tuple memory context for trigger function calls */
4133  per_tuple_context =
4135  "AfterTriggerTupleContext",
4137 
4138  for_each_chunk(chunk, *events)
4139  {
4140  AfterTriggerEvent event;
4141  bool all_fired_in_chunk = true;
4142 
4143  for_each_event(event, chunk)
4144  {
4145  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4146 
4147  /*
4148  * Is it one for me to fire?
4149  */
4150  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4151  evtshared->ats_firing_id == firing_id)
4152  {
4153  /*
4154  * So let's fire it... but first, find the correct relation if
4155  * this is not the same relation as before.
4156  */
4157  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4158  {
4159  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4160  rel = rInfo->ri_RelationDesc;
4161  trigdesc = rInfo->ri_TrigDesc;
4162  finfo = rInfo->ri_TrigFunctions;
4163  instr = rInfo->ri_TrigInstrument;
4164  if (slot1 != NULL)
4165  {
4168  slot1 = slot2 = NULL;
4169  }
4170  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4171  {
4172  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4174  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4176  }
4177  if (trigdesc == NULL) /* should not happen */
4178  elog(ERROR, "relation %u has no triggers",
4179  evtshared->ats_relid);
4180  }
4181 
4182  /*
4183  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4184  * still set, so recursive examinations of the event list
4185  * won't try to re-fire it.
4186  */
4187  AfterTriggerExecute(estate, event, rInfo, trigdesc, finfo, instr,
4188  per_tuple_context, slot1, slot2);
4189 
4190  /*
4191  * Mark the event as done.
4192  */
4193  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4194  event->ate_flags |= AFTER_TRIGGER_DONE;
4195  }
4196  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4197  {
4198  /* something remains to be done */
4199  all_fired = all_fired_in_chunk = false;
4200  }
4201  }
4202 
4203  /* Clear the chunk if delete_ok and nothing left of interest */
4204  if (delete_ok && all_fired_in_chunk)
4205  {
4206  chunk->freeptr = CHUNK_DATA_START(chunk);
4207  chunk->endfree = chunk->endptr;
4208 
4209  /*
4210  * If it's last chunk, must sync event list's tailfree too. Note
4211  * that delete_ok must NOT be passed as true if there could be
4212  * additional AfterTriggerEventList values pointing at this event
4213  * list, since we'd fail to fix their copies of tailfree.
4214  */
4215  if (chunk == events->tail)
4216  events->tailfree = chunk->freeptr;
4217  }
4218  }
4219  if (slot1 != NULL)
4220  {
4223  }
4224 
4225  /* Release working resources */
4226  MemoryContextDelete(per_tuple_context);
4227 
4228  if (local_estate)
4229  {
4230  ExecCleanUpTriggerState(estate);
4231  ExecResetTupleTable(estate->es_tupleTable, false);
4232  FreeExecutorState(estate);
4233  }
4234 
4235  return all_fired;
4236 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:413
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define AllocSetContextCreate
Definition: memutils.h:170
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1208
TriggerFlags ate_flags
Definition: trigger.c:3283
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3258
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3327
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:434
AfterTriggerEventChunk * tail
Definition: trigger.c:3333
Form_pg_class rd_rel
Definition: rel.h:89
#define GetTriggerSharedData(evt)
Definition: trigger.c:3308
#define for_each_event(eptr, cptr)
Definition: trigger.c:3340
void FreeExecutorState(EState *estate)
Definition: execUtils.c:190
#define ERROR
Definition: elog.h:43
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:425
EState * CreateExecutorState(void)
Definition: execUtils.c:88
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1371
List * es_tupleTable
Definition: execnodes.h:557
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1161
TupleDesc rd_att
Definition: rel.h:90
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3338
CommandId ats_firing_id
Definition: trigger.c:3274
void ExecCleanUpTriggerState(EState *estate)
Definition: execMain.c:1454
#define elog(elevel,...)
Definition: elog.h:214
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3259
#define RelationGetRelid(relation)
Definition: rel.h:436
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:428
static void AfterTriggerExecute(EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
Definition: trigger.c:3822

◆ afterTriggerMarkEvents()

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

Definition at line 4036 of file trigger.c.

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, afterTriggerAddEvent(), afterTriggerCheckState(), AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_firing_id, AfterTriggersData::firing_counter, for_each_event_chunk, and GetTriggerSharedData.

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

4039 {
4040  bool found = false;
4041  AfterTriggerEvent event;
4042  AfterTriggerEventChunk *chunk;
4043 
4044  for_each_event_chunk(event, chunk, *events)
4045  {
4046  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4047  bool defer_it = false;
4048 
4049  if (!(event->ate_flags &
4051  {
4052  /*
4053  * This trigger hasn't been called or scheduled yet. Check if we
4054  * should call it now.
4055  */
4056  if (immediate_only && afterTriggerCheckState(evtshared))
4057  {
4058  defer_it = true;
4059  }
4060  else
4061  {
4062  /*
4063  * Mark it as to be fired in this firing cycle.
4064  */
4066  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4067  found = true;
4068  }
4069  }
4070 
4071  /*
4072  * If it's deferred, move it to move_list, if requested.
4073  */
4074  if (defer_it && move_list != NULL)
4075  {
4076  /* add it to move_list */
4077  afterTriggerAddEvent(move_list, event, evtshared);
4078  /* mark original copy "done" so we don't do it again */
4079  event->ate_flags |= AFTER_TRIGGER_DONE;
4080  }
4081  }
4082 
4083  return found;
4084 }
TriggerFlags ate_flags
Definition: trigger.c:3283
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3258
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3345
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3545
#define GetTriggerSharedData(evt)
Definition: trigger.c:3308
CommandId firing_counter
Definition: trigger.c:3438
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3591
CommandId ats_firing_id
Definition: trigger.c:3274
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3259
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

Definition at line 5287 of file trigger.c.

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

Referenced by CheckTableNotInUse().

5288 {
5289  AfterTriggerEvent event;
5290  AfterTriggerEventChunk *chunk;
5291  int depth;
5292 
5293  /* Scan queued events */
5295  {
5296  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5297 
5298  /*
5299  * We can ignore completed events. (Even if a DONE flag is rolled
5300  * back by subxact abort, it's OK because the effects of the TRUNCATE
5301  * or whatever must get rolled back too.)
5302  */
5303  if (event->ate_flags & AFTER_TRIGGER_DONE)
5304  continue;
5305 
5306  if (evtshared->ats_relid == relid)
5307  return true;
5308  }
5309 
5310  /*
5311  * Also scan events queued by incomplete queries. This could only matter
5312  * if TRUNCATE/etc is executed by a function or trigger within an updating
5313  * query on the same relation, which is pretty perverse, but let's check.
5314  */
5315  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5316  {
5318  {
5319  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5320 
5321  if (event->ate_flags & AFTER_TRIGGER_DONE)
5322  continue;
5323 
5324  if (evtshared->ats_relid == relid)
5325  return true;
5326  }
5327  }
5328 
5329  return false;
5330 }
TriggerFlags ate_flags
Definition: trigger.c:3283
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3258
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3345
AfterTriggersQueryData * query_stack
Definition: trigger.c:3444
#define GetTriggerSharedData(evt)
Definition: trigger.c:3308
AfterTriggerEventList events
Definition: trigger.c:3440
AfterTriggerEventList events
Definition: trigger.c:3455
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ afterTriggerRestoreEventList()

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

Definition at line 3728 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

3730 {
3731  AfterTriggerEventChunk *chunk;
3732  AfterTriggerEventChunk *next_chunk;
3733 
3734  if (old_events->tail == NULL)
3735  {
3736  /* restoring to a completely empty state, so free everything */
3737  afterTriggerFreeEventList(events);
3738  }
3739  else
3740  {
3741  *events = *old_events;
3742  /* free any chunks after the last one we want to keep */
3743  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
3744  {
3745  next_chunk = chunk->next;
3746  pfree(chunk);
3747  }
3748  /* and clean up the tail chunk to be the right length */
3749  events->tail->next = NULL;
3750  events->tail->freeptr = events->tailfree;
3751 
3752  /*
3753  * We don't make any effort to remove now-unused shared data records.
3754  * They might still be useful, anyway.
3755  */
3756  }
3757 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3333
struct AfterTriggerEventChunk * next
Definition: trigger.c:3320
void pfree(void *pointer)
Definition: mcxt.c:1056
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3707

◆ AfterTriggerSaveEvent()

static void AfterTriggerSaveEvent ( EState estate,
ResultRelInfo relinfo,
int  event,
bool  row_trigger,
TupleTableSlot oldtup,
TupleTableSlot newtup,
List recheckIndexes,
Bitmapset modifiedCols,
TransitionCaptureState transition_capture 
)
static

Definition at line 5357 of file trigger.c.

References AFTER_TRIGGER_1CTID, AFTER_TRIGGER_2CTID, AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_INITDEFERRED, afterTriggerAddEvent(), AfterTriggerEnlargeQueryState(), Assert, AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_flags, 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, EState::es_tupleTable, AfterTriggersQueryData::events, ExecAllocTableSlot(), execute_attr_map_slot(), GetCurrentFDWTuplestore(), i, ItemPointerCopy, ItemPointerSetInvalid, list_member_oid(), AfterTriggersData::maxquerydepth, AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, TupleConversionMap::outdesc, 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, AfterTriggersTableData::storeslot, TransitionCaptureState::tcs_delete_old_table, TransitionCaptureState::tcs_insert_new_table, TransitionCaptureState::tcs_map, TransitionCaptureState::tcs_original_insert_tuple, TransitionCaptureState::tcs_private, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, Trigger::tgconstrindid, Trigger::tgdeferrable, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgtype, 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, TTSOpsVirtual, TupIsNull, and tuplestore_puttupleslot().

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

5362 {
5363  Relation rel = relinfo->ri_RelationDesc;
5364  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
5365  AfterTriggerEventData new_event;
5366  AfterTriggerSharedData new_shared;
5367  char relkind = rel->rd_rel->relkind;
5368  int tgtype_event;
5369  int tgtype_level;
5370  int i;
5371  Tuplestorestate *fdw_tuplestore = NULL;
5372 
5373  /*
5374  * Check state. We use a normal test not Assert because it is possible to
5375  * reach here in the wrong state given misconfigured RI triggers, in
5376  * particular deferring a cascade action trigger.
5377  */
5378  if (afterTriggers.query_depth < 0)
5379  elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
5380 
5381  /* Be sure we have enough space to record events at this query depth. */
5384 
5385  /*
5386  * If the directly named relation has any triggers with transition tables,
5387  * then we need to capture transition tuples.
5388  */
5389  if (row_trigger && transition_capture != NULL)
5390  {
5391  TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
5392  TupleConversionMap *map = transition_capture->tcs_map;
5393  bool delete_old_table = transition_capture->tcs_delete_old_table;
5394  bool update_old_table = transition_capture->tcs_update_old_table;
5395  bool update_new_table = transition_capture->tcs_update_new_table;
5396  bool insert_new_table = transition_capture->tcs_insert_new_table;
5397 
5398  /*
5399  * For INSERT events NEW should be non-NULL, for DELETE events OLD
5400  * should be non-NULL, whereas for UPDATE events normally both OLD and
5401  * NEW are non-NULL. But for UPDATE events fired for capturing
5402  * transition tuples during UPDATE partition-key row movement, OLD is
5403  * NULL when the event is for a row being inserted, whereas NEW is
5404  * NULL when the event is for a row being deleted.
5405  */
5406  Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
5407  TupIsNull(oldslot)));
5408  Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
5409  TupIsNull(newslot)));
5410 
5411  if (!TupIsNull(oldslot) &&
5412  ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
5413  (event == TRIGGER_EVENT_UPDATE && update_old_table)))
5414  {
5415  Tuplestorestate *old_tuplestore;
5416 
5417  old_tuplestore = transition_capture->tcs_private->old_tuplestore;
5418 
5419  if (map != NULL)
5420  {
5421  TupleTableSlot *storeslot;
5422 
5423  storeslot = transition_capture->tcs_private->storeslot;
5424  if (!storeslot)
5425  {
5426  storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
5427  map->outdesc,
5428  &TTSOpsVirtual);
5429  transition_capture->tcs_private->storeslot = storeslot;
5430  }
5431 
5432  execute_attr_map_slot(map->attrMap, oldslot, storeslot);
5433  tuplestore_puttupleslot(old_tuplestore, storeslot);
5434  }
5435  else
5436  tuplestore_puttupleslot(old_tuplestore, oldslot);
5437  }
5438  if (!TupIsNull(newslot) &&
5439  ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
5440  (event == TRIGGER_EVENT_UPDATE && update_new_table)))
5441  {
5442  Tuplestorestate *new_tuplestore;
5443 
5444  new_tuplestore = transition_capture->tcs_private->new_tuplestore;
5445 
5446  if (original_insert_tuple != NULL)
5447  tuplestore_puttupleslot(new_tuplestore,
5448  original_insert_tuple);
5449  else if (map != NULL)
5450  {
5451  TupleTableSlot *storeslot;
5452 
5453  storeslot = transition_capture->tcs_private->storeslot;
5454 
5455  if (!storeslot)
5456  {
5457  storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
5458  map->outdesc,
5459  &TTSOpsVirtual);
5460  transition_capture->tcs_private->storeslot = storeslot;
5461  }
5462 
5463  execute_attr_map_slot(map->attrMap, newslot, storeslot);
5464  tuplestore_puttupleslot(new_tuplestore, storeslot);
5465  }
5466  else
5467  tuplestore_puttupleslot(new_tuplestore, newslot);
5468  }
5469 
5470  /*
5471  * If transition tables are the only reason we're here, return. As
5472  * mentioned above, we can also be here during update tuple routing in
5473  * presence of transition tables, in which case this function is
5474  * called separately for oldtup and newtup, so we expect exactly one
5475  * of them to be NULL.
5476  */
5477  if (trigdesc == NULL ||
5478  (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
5479  (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
5480  (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
5481  (event == TRIGGER_EVENT_UPDATE && (TupIsNull(oldslot) ^ TupIsNull(newslot))))
5482  return;
5483  }
5484 
5485  /*
5486  * Validate the event code and collect the associated tuple CTIDs.
5487  *
5488  * The event code will be used both as a bitmask and an array offset, so
5489  * validation is important to make sure we don't walk off the edge of our
5490  * arrays.
5491  *
5492  * Also, if we're considering statement-level triggers, check whether we
5493  * already queued a set of them for this event, and cancel the prior set
5494  * if so. This preserves the behavior that statement-level triggers fire
5495  * just once per statement and fire after row-level triggers.
5496  */
5497  switch (event)
5498  {
5499  case TRIGGER_EVENT_INSERT:
5500  tgtype_event = TRIGGER_TYPE_INSERT;
5501  if (row_trigger)
5502  {
5503  Assert(oldslot == NULL);
5504  Assert(newslot != NULL);
5505  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
5506  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5507  }
5508  else
5509  {
5510  Assert(oldslot == NULL);
5511  Assert(newslot == NULL);
5512  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5513  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5515  CMD_INSERT, event);
5516  }
5517  break;
5518  case TRIGGER_EVENT_DELETE:
5519  tgtype_event = TRIGGER_TYPE_DELETE;
5520  if (row_trigger)
5521  {
5522  Assert(oldslot != NULL);
5523  Assert(newslot == NULL);
5524  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
5525  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5526  }
5527  else
5528  {
5529  Assert(oldslot == NULL);
5530  Assert(newslot == NULL);
5531  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5532  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5534  CMD_DELETE, event);
5535  }
5536  break;
5537  case TRIGGER_EVENT_UPDATE:
5538  tgtype_event = TRIGGER_TYPE_UPDATE;
5539  if (row_trigger)
5540  {
5541  Assert(oldslot != NULL);
5542  Assert(newslot != NULL);
5543  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
5544  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
5545  }
5546  else
5547  {
5548  Assert(oldslot == NULL);
5549  Assert(newslot == NULL);
5550  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5551  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5553  CMD_UPDATE, event);
5554  }
5555  break;
5557  tgtype_event = TRIGGER_TYPE_TRUNCATE;
5558  Assert(oldslot == NULL);
5559  Assert(newslot == NULL);
5560  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5561  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5562  break;
5563  default:
5564  elog(ERROR, "invalid after-trigger event code: %d", event);
5565  tgtype_event = 0; /* keep compiler quiet */
5566  break;
5567  }
5568 
5569  if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
5570  new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ?
5572  /* else, we'll initialize ate_flags for each trigger */
5573 
5574  tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
5575 
5576  for (i = 0; i < trigdesc->numtriggers; i++)
5577  {
5578  Trigger *trigger = &trigdesc->triggers[i];
5579 
5580  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
5581  tgtype_level,
5582  TRIGGER_TYPE_AFTER,
5583  tgtype_event))
5584  continue;
5585  if (!TriggerEnabled(estate, relinfo, trigger, event,
5586  modifiedCols, oldslot, newslot))
5587  continue;
5588 
5589  if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
5590  {
5591  if (fdw_tuplestore == NULL)
5592  {
5593  fdw_tuplestore = GetCurrentFDWTuplestore();
5594  new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH;
5595  }
5596  else
5597  /* subsequent event for the same tuple */
5598  new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE;
5599  }
5600 
5601  /*
5602  * If the trigger is a foreign key enforcement trigger, there are
5603  * certain cases where we can skip queueing the event because we can
5604  * tell by inspection that the FK constraint will still pass.
5605  */
5606  if (TRIGGER_FIRED_BY_UPDATE(event) || TRIGGER_FIRED_BY_DELETE(event))
5607  {
5608  switch (RI_FKey_trigger_type(trigger->tgfoid))
5609  {
5610  case RI_TRIGGER_PK:
5611  /* Update or delete on trigger's PK table */
5612  if (!RI_FKey_pk_upd_check_required(trigger, rel,
5613  oldslot, newslot))
5614  {
5615  /* skip queuing this event */
5616  continue;
5617  }
5618  break;
5619 
5620  case RI_TRIGGER_FK:
5621  /* Update on trigger's FK table */
5622  if (!RI_FKey_fk_upd_check_required(trigger, rel,
5623  oldslot, newslot))
5624  {
5625  /* skip queuing this event */
5626  continue;
5627  }
5628  break;
5629 
5630  case RI_TRIGGER_NONE:
5631  /* Not an FK trigger */
5632  break;
5633  }
5634  }
5635 
5636  /*
5637  * If the trigger is a deferred unique constraint check trigger, only
5638  * queue it if the unique constraint was potentially violated, which
5639  * we know from index insertion time.
5640  */
5641  if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
5642  {
5643  if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
5644  continue; /* Uniqueness definitely not violated */
5645  }
5646 
5647  /*
5648  * Fill in event structure and add it to the current query's queue.
5649  * Note we set ats_table to NULL whenever this trigger doesn't use
5650  * transition tables, to improve sharability of the shared event data.
5651  */
5652  new_shared.ats_event =
5653  (event & TRIGGER_EVENT_OPMASK) |
5654  (row_trigger ? TRIGGER_EVENT_ROW : 0) |
5655  (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
5656  (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
5657  new_shared.ats_tgoid = trigger->tgoid;
5658  new_shared.ats_relid = RelationGetRelid(rel);
5659  new_shared.ats_firing_id = 0;
5660  if ((trigger->tgoldtable || trigger->tgnewtable) &&
5661  transition_capture != NULL)
5662  new_shared.ats_table = transition_capture->tcs_private;
5663  else
5664  new_shared.ats_table = NULL;
5665  new_shared.ats_modifiedcols = modifiedCols;
5666 
5668  &new_event, &new_shared);
5669  }
5670 
5671  /*
5672  * Finally, spool any foreign tuple(s). The tuplestore squashes them to
5673  * minimal tuples, so this loses any system columns. The executor lost
5674  * those columns before us, for an unrelated reason, so this is fine.
5675  */
5676  if (fdw_tuplestore)
5677  {
5678  if (oldslot != NULL)
5679  tuplestore_puttupleslot(fdw_tuplestore, oldslot);
5680  if (newslot != NULL)
5681  tuplestore_puttupleslot(fdw_tuplestore, newslot);
5682  }
5683 }
#define TRIGGER_EVENT_ROW
Definition: trigger.h:105
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:708
TriggerEvent ats_event
Definition: trigger.c:3271
Relation ri_RelationDesc
Definition: execnodes.h:413
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:2863
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3261
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:115
TupleDesc outdesc
Definition: tupconvert.h:26
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: trigger.c:3065
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3509
ItemPointerData ate_ctid2
Definition: trigger.c:3285
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:100
Oid tgfoid
Definition: reltrigger.h:28
TriggerFlags ate_flags
Definition: trigger.c:3283
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:5736
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:114
Oid tgoid
Definition: reltrigger.h:25
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
AfterTriggersQueryData * query_stack
Definition: trigger.c:3444
Tuplestorestate * old_tuplestore
Definition: trigger.c:3478
Form_pg_class rd_rel
Definition: rel.h:89
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:103
Bitmapset * ats_modifiedcols
Definition: trigger.c:3276
struct AfterTriggersTableData * tcs_private
Definition: trigger.h:88
#define ERROR
Definition: elog.h:43
TupleConversionMap * tcs_map
Definition: trigger.h:74
int16 tgtype
Definition: reltrigger.h:29
bool tgdeferrable
Definition: reltrigger.h:36
bool tginitdeferred
Definition: reltrigger.h:37
bool trig_delete_after_row
Definition: reltrigger.h:67
Trigger * triggers
Definition: reltrigger.h:49
TupleTableSlot * storeslot
Definition: trigger.c:3480
#define TupIsNull(slot)
Definition: tuptable.h:292
bool trig_insert_after_row
Definition: reltrigger.h:57
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3263
Tuplestorestate * new_tuplestore
Definition: trigger.c:3479
AttrMap * attrMap
Definition: tupconvert.h:27
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3264
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:425
TupleTableSlot * ExecAllocTableSlot(List **tupleTable, TupleDesc desc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1141
bool trig_update_after_row
Definition: reltrigger.h:62
int numtriggers
Definition: reltrigger.h:50
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:120
List * es_tupleTable
Definition: execnodes.h:557
char * tgnewtable
Definition: reltrigger.h:44
bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: ri_triggers.c:1162
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3275
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4850
#define RI_TRIGGER_FK
Definition: trigger.h:272
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:675
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:177
#define Assert(condition)
Definition: c.h:738
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3591
#define TRIGGER_EVENT_TRUNCATE
Definition: trigger.h:102
Oid tgconstrindid
Definition: reltrigger.h:34
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3262
CommandId ats_firing_id
Definition: trigger.c:3274
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:101
bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: ri_triggers.c:1194
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:99
#define RI_TRIGGER_PK
Definition: trigger.h:271
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
#define elog(elevel,...)
Definition: elog.h:214
int i
ItemPointerData ate_ctid1
Definition: trigger.c:3284
#define RI_TRIGGER_NONE
Definition: trigger.h:273
char * tgoldtable
Definition: reltrigger.h:43
AfterTriggerEventList events
Definition: trigger.c:3455
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:123
static AfterTriggersData afterTriggers
Definition: trigger.c:3483
#define RelationGetRelid(relation)
Definition: rel.h:436
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:161
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:83

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 4972 of file trigger.c.

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, BTEqualStrategyNumber, RangeVar::catalogname, ConstraintNameNspIndexId, ConstraintParentIndexId, ConstraintsSetStmt::constraints, CStringGetDatum, ConstraintsSetStmt::deferred, 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, SetConstraintTriggerData::sct_tgisdeferred, SetConstraintTriggerData::sct_tgoid, SetConstraintStateAddItem(), SetConstraintStateCopy(), SetConstraintStateCreate(), AfterTriggersData::state, AfterTriggersTransData::state, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), AfterTriggersData::trans_stack, TriggerConstraintIndexId, and SetConstraintStateData::trigstates.

Referenced by standard_ProcessUtility().

4973 {
4974  int my_level = GetCurrentTransactionNestLevel();
4975 
4976  /* If we haven't already done so, initialize our state. */
4977  if (afterTriggers.state == NULL)
4979 
4980  /*
4981  * If in a subtransaction, and we didn't save the current state already,
4982  * save it so it can be restored if the subtransaction aborts.
4983  */
4984  if (my_level > 1 &&
4985  afterTriggers.trans_stack[my_level].state == NULL)
4986  {
4987  afterTriggers.trans_stack[my_level].state =
4989  }
4990 
4991  /*
4992  * Handle SET CONSTRAINTS ALL ...
4993  */
4994  if (stmt->constraints == NIL)
4995  {
4996  /*
4997  * Forget any previous SET CONSTRAINTS commands in this transaction.
4998  */
5000 
5001  /*
5002  * Set the per-transaction ALL state to known.
5003  */
5004  afterTriggers.state->all_isset = true;
5006  }
5007  else
5008  {
5009  Relation conrel;
5010  Relation tgrel;
5011  List *conoidlist = NIL;
5012  List *tgoidlist = NIL;
5013  ListCell *lc;
5014 
5015  /*
5016  * Handle SET CONSTRAINTS constraint-name [, ...]
5017  *
5018  * First, identify all the named constraints and make a list of their
5019  * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5020  * the same name within a schema, the specifications are not
5021  * necessarily unique. Our strategy is to target all matching
5022  * constraints within the first search-path schema that has any
5023  * matches, but disregard matches in schemas beyond the first match.
5024  * (This is a bit odd but it's the historical behavior.)
5025  *
5026  * A constraint in a partitioned table may have corresponding
5027  * constraints in the partitions. Grab those too.
5028  */
5029  conrel = table_open(ConstraintRelationId, AccessShareLock);
5030 
5031  foreach(lc, stmt->constraints)
5032  {
5033  RangeVar *constraint = lfirst(lc);
5034  bool found;
5035  List *namespacelist;
5036  ListCell *nslc;
5037 
5038  if (constraint->catalogname)
5039  {
5040  if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5041  ereport(ERROR,
5042  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5043  errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5044  constraint->catalogname, constraint->schemaname,
5045  constraint->relname)));
5046  }
5047 
5048  /*
5049  * If we're given the schema name with the constraint, look only
5050  * in that schema. If given a bare constraint name, use the
5051  * search path to find the first matching constraint.
5052  */
5053  if (constraint->schemaname)
5054  {
5055  Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5056  false);
5057 
5058  namespacelist = list_make1_oid(namespaceId);
5059  }
5060  else
5061  {
5062  namespacelist = fetch_search_path(true);
5063  }
5064 
5065  found = false;
5066  foreach(nslc, namespacelist)
5067  {
5068  Oid namespaceId = lfirst_oid(nslc);
5069  SysScanDesc conscan;
5070  ScanKeyData skey[2];
5071  HeapTuple tup;
5072 
5073  ScanKeyInit(&skey[0],
5074  Anum_pg_constraint_conname,
5075  BTEqualStrategyNumber, F_NAMEEQ,
5076  CStringGetDatum(constraint->relname));
5077  ScanKeyInit(&skey[1],
5078  Anum_pg_constraint_connamespace,
5079  BTEqualStrategyNumber, F_OIDEQ,
5080  ObjectIdGetDatum(namespaceId));
5081 
5082  conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5083  true, NULL, 2, skey);
5084 
5085  while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5086  {
5088 
5089  if (con->condeferrable)
5090  conoidlist = lappend_oid(conoidlist, con->oid);
5091  else if (stmt->deferred)
5092  ereport(ERROR,
5093  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5094  errmsg("constraint \"%s\" is not deferrable",
5095  constraint->relname)));
5096  found = true;
5097  }
5098 
5099  systable_endscan(conscan);
5100 
5101  /*
5102  * Once we've found a matching constraint we do not search
5103  * later parts of the search path.
5104  */
5105  if (found)
5106  break;
5107  }
5108 
5109  list_free(namespacelist);
5110 
5111  /*
5112  * Not found ?
5113  */
5114  if (!found)
5115  ereport(ERROR,
5116  (errcode(ERRCODE_UNDEFINED_OBJECT),
5117  errmsg("constraint \"%s\" does not exist",
5118  constraint->relname)));
5119  }
5120 
5121  /*
5122  * Scan for any possible descendants of the constraints. We append
5123  * whatever we find to the same list that we're scanning; this has the
5124  * effect that we create new scans for those, too, so if there are
5125  * further descendents, we'll also catch them.
5126  */
5127  foreach(lc, conoidlist)
5128  {
5129  Oid parent = lfirst_oid(lc);
5130  ScanKeyData key;
5131  SysScanDesc scan;
5132  HeapTuple tuple;
5133 
5134  ScanKeyInit(&key,
5135  Anum_pg_constraint_conparentid,
5136  BTEqualStrategyNumber, F_OIDEQ,
5137  ObjectIdGetDatum(parent));
5138 
5139  scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5140 
5141  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5142  {
5144 
5145  conoidlist = lappend_oid(conoidlist, con->oid);
5146  }
5147 
5148  systable_endscan(scan);
5149  }
5150 
5151  table_close(conrel, AccessShareLock);
5152 
5153  /*
5154  * Now, locate the trigger(s) implementing each of these constraints,
5155  * and make a list of their OIDs.
5156  */
5157  tgrel = table_open(TriggerRelationId, AccessShareLock);
5158 
5159  foreach(lc, conoidlist)
5160  {
5161  Oid conoid = lfirst_oid(lc);
5162  ScanKeyData skey;
5163  SysScanDesc tgscan;
5164  HeapTuple htup;
5165 
5166  ScanKeyInit(&skey,
5167  Anum_pg_trigger_tgconstraint,
5168  BTEqualStrategyNumber, F_OIDEQ,
5169  ObjectIdGetDatum(conoid));
5170 
5171  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5172  NULL, 1, &skey);
5173 
5174  while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5175  {
5176  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5177 
5178  /*
5179  * Silently skip triggers that are marked as non-deferrable in
5180  * pg_trigger. This is not an error condition, since a
5181  * deferrable RI constraint may have some non-deferrable
5182  * actions.
5183  */
5184  if (pg_trigger->tgdeferrable)
5185  tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid);
5186  }
5187 
5188  systable_endscan(tgscan);
5189  }
5190 
5191  table_close(tgrel, AccessShareLock);
5192 
5193  /*
5194  * Now we can set the trigger states of individual triggers for this
5195  * xact.
5196  */
5197  foreach(lc, tgoidlist)
5198  {
5199  Oid tgoid = lfirst_oid(lc);
5201  bool found = false;
5202  int i;
5203 
5204  for (i = 0; i < state->numstates; i++)
5205  {
5206  if (state->trigstates[i].sct_tgoid == tgoid)
5207  {
5208  state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5209  found = true;
5210  break;
5211  }
5212  }
5213  if (!found)
5214  {
5216  SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5217  }
5218  }
5219  }
5220 
5221  /*
5222  * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5223  * checks against that constraint must be made when the SET CONSTRAINTS
5224  * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5225  * apply retroactively. We've updated the constraints state, so scan the
5226  * list of previously deferred events to fire any that have now become
5227  * immediate.
5228  *
5229  * Obviously, if this was SET ... DEFERRED then it can't have converted
5230  * any unfired events to immediate, so we need do nothing in that case.
5231  */
5232  if (!stmt->deferred)
5233  {
5235  bool snapshot_set = false;
5236 
5237  while (afterTriggerMarkEvents(events, NULL, true))
5238  {
5239  CommandId firing_id = afterTriggers.firing_counter++;
5240 
5241  /*
5242  * Make sure a snapshot has been established in case trigger
5243  * functions need one. Note that we avoid setting a snapshot if
5244  * we don't find at least one trigger that has to be fired now.
5245  * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
5246  * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
5247  * at the start of a transaction it's not possible for any trigger
5248  * events to be queued yet.)
5249  */
5250  if (!snapshot_set)
5251  {
5253  snapshot_set = true;
5254  }
5255 
5256  /*
5257  * We can delete fired events if we are at top transaction level,
5258  * but we'd better not if inside a subtransaction, since the
5259  * subtransaction could later get rolled back.
5260  */
5261  if (afterTriggerInvokeEvents(events, firing_id, NULL,
5262  !IsSubTransaction()))
5263  break; /* all fired */
5264  }
5265 
5266  if (snapshot_set)
5268  }
5269 }
#define NIL
Definition: pg_list.h:65
uint32 CommandId
Definition: c.h:527
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2893
AfterTriggersTransData * trans_stack
Definition: trigger.c:3449
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:529
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3212
#define AccessShareLock
Definition: lockdefs.h:36
int errcode(int sqlerrcode)
Definition: elog.c:610
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition: trigger.c:4942
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4108
void PopActiveSnapshot(void)
Definition: snapmgr.c:814
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:358
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:306
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:356
char * schemaname
Definition: primnodes.h:67
char * relname
Definition: primnodes.h:68
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:448
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
SetConstraintState state
Definition: trigger.c:3439
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2155
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:735
#define CStringGetDatum(X)
Definition: postgres.h:578
#define TriggerConstraintIndexId
Definition: indexing.h:251
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:4897
#define list_make1_oid(x1)
Definition: pg_list.h:249
Oid MyDatabaseId
Definition: globals.c:85
CommandId firing_counter
Definition: trigger.c:3438
#define ereport(elevel,...)
Definition: elog.h:144
#define ConstraintNameNspIndexId
Definition: indexing.h:126
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:841
FormData_pg_constraint * Form_pg_constraint
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
Definition: regguts.h:298
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:72
bool IsSubTransaction(void)
Definition: xact.c:4708
int errmsg(const char *fmt,...)
Definition: elog.c:824
void list_free(List *list)
Definition: list.c:1377
int i
#define ConstraintParentIndexId
Definition: indexing.h:134
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4036
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
AfterTriggerEventList events
Definition: trigger.c:3440
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
Definition: pg_list.h:50
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4383
static AfterTriggersData afterTriggers
Definition: trigger.c:3483
char * catalogname
Definition: primnodes.h:66
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define lfirst_oid(lc)
Definition: pg_list.h:192
SetConstraintState state
Definition: trigger.c:3463
static SetConstraintState SetConstraintStateCopy(SetConstraintState state)
Definition: trigger.c:4922

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 5690 of file trigger.c.

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

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

5691 {
5692  bool result;
5693  AfterTriggersTableData *table;
5694 
5695  /* Check state, like AfterTriggerSaveEvent. */
5696  if (afterTriggers.query_depth < 0)
5697  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
5698 
5699  /* Be sure we have enough space to record events at this query depth. */
5702 
5703  /*
5704  * We keep this state in the AfterTriggersTableData that also holds
5705  * transition tables for the relation + operation. In this way, if we are
5706  * forced to make a new set of transition tables because more tuples get
5707  * entered after we've already fired triggers, we will allow a new set of
5708  * statement triggers to get queued.
5709  */
5710  table = GetAfterTriggersTableData(relid, cmdType);
5711  result = table->before_trig_done;
5712  table->before_trig_done = true;
5713  return result;
5714 }
#define ERROR
Definition: elog.h:43
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4252
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4850
#define elog(elevel,...)
Definition: elog.h:214
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ cancel_prior_stmt_triggers()

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

Definition at line 5736 of file trigger.c.

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

Referenced by AfterTriggerSaveEvent().

5737 {
5738  AfterTriggersTableData *table;
5740 
5741  /*
5742  * We keep this state in the AfterTriggersTableData that also holds
5743  * transition tables for the relation + operation. In this way, if we are
5744  * forced to make a new set of transition tables because more tuples get
5745  * entered after we've already fired triggers, we will allow a new set of
5746  * statement triggers to get queued without canceling the old ones.
5747  */
5748  table = GetAfterTriggersTableData(relid, cmdType);
5749 
5750  if (table->after_trig_done)
5751  {
5752  /*
5753  * We want to start scanning from the tail location that existed just
5754  * before we inserted any statement triggers. But the events list
5755  * might've been entirely empty then, in which case scan from the
5756  * current head.
5757  */
5758  AfterTriggerEvent event;
5759  AfterTriggerEventChunk *chunk;
5760 
5761  if (table->after_trig_events.tail)
5762  {
5763  chunk = table->after_trig_events.tail;
5764  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
5765  }
5766  else
5767  {
5768  chunk = qs->events.head;
5769  event = NULL;
5770  }
5771 
5772  for_each_chunk_from(chunk)
5773  {
5774  if (event == NULL)
5775  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
5776  for_each_event_from(event, chunk)
5777  {
5778  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5779 
5780  /*
5781  * Exit loop when we reach events that aren't AS triggers for
5782  * the target relation.
5783  */
5784  if (evtshared->ats_relid != relid)
5785  goto done;
5786  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
5787  goto done;
5788  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
5789  goto done;
5790  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
5791  goto done;
5792  /* OK, mark it DONE */
5793  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
5794  event->ate_flags |= AFTER_TRIGGER_DONE;
5795  }
5796  /* signal we must reinitialize event ptr for next chunk */
5797  event = NULL;
5798  }
5799  }
5800 done:
5801 
5802  /* In any case, save current insertion point for next time */
5803  table->after_trig_done = true;
5804  table->after_trig_events = qs->events;
5805 }
TriggerEvent ats_event
Definition: trigger.c:3271
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3258
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3327
AfterTriggersQueryData * query_stack
Definition: trigger.c:3444
AfterTriggerEventChunk * tail
Definition: trigger.c:3333
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:138
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:103
#define TRIGGER_FIRED_FOR_STATEMENT(event)
Definition: trigger.h:132
#define GetTriggerSharedData(evt)
Definition: trigger.c:3308
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4252
AfterTriggerEventList after_trig_events
Definition: trigger.c:3477
#define for_each_chunk_from(cptr)
Definition: trigger.c:3349
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3351
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3279
AfterTriggerEventChunk * head
Definition: trigger.c:3332
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3259
AfterTriggerEventList events
Definition: trigger.c:3455
static AfterTriggersData afterTriggers
Definition: trigger.c:3483

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

Definition at line 1821 of file trigger.c.

References i, 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().

1822 {
1823  TriggerDesc *newdesc;
1824  Trigger *trigger;
1825  int i;
1826 
1827  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
1828  return NULL;
1829 
1830  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
1831  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
1832 
1833  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
1834  memcpy(trigger, trigdesc->triggers,
1835  trigdesc->numtriggers * sizeof(Trigger));
1836  newdesc->triggers = trigger;
1837 
1838  for (i = 0; i < trigdesc->numtriggers; i++)
1839  {
1840  trigger->tgname = pstrdup(trigger->tgname);
1841  if (trigger->tgnattr > 0)
1842  {
1843  int16 *newattr;
1844 
1845  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
1846  memcpy(newattr, trigger->tgattr,
1847  trigger->tgnattr * sizeof(int16));
1848  trigger->tgattr = newattr;
1849  }
1850  if (trigger->tgnargs > 0)
1851  {
1852  char **newargs;
1853  int16 j;
1854 
1855  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
1856  for (j = 0; j < trigger->tgnargs; j++)
1857  newargs[j] = pstrdup(trigger->tgargs[j]);
1858  trigger->tgargs = newargs;
1859  }
1860  if (trigger->tgqual)
1861  trigger->tgqual = pstrdup(trigger->tgqual);
1862  if (trigger->tgoldtable)
1863  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
1864  if (trigger->tgnewtable)
1865  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
1866  trigger++;
1867  }
1868 
1869  return newdesc;
1870 }
signed short int16
Definition: c.h:354
char * pstrdup(const char *in)
Definition: mcxt.c:1186
char * tgqual
Definition: reltrigger.h:42
char * tgname
Definition: reltrigger.h:27
Trigger * triggers
Definition: reltrigger.h:49
int numtriggers
Definition: reltrigger.h:50
char ** tgargs
Definition: reltrigger.h:41
int16 * tgattr
Definition: reltrigger.h:40
char * tgnewtable
Definition: reltrigger.h:44
int16 tgnattr
Definition: reltrigger.h:39
void * palloc(Size size)
Definition: mcxt.c:949
int i
int16 tgnargs
Definition: reltrigger.h:38
char * tgoldtable
Definition: reltrigger.h:43

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

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

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

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

◆ EnableDisableTrigger()

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

Definition at line 1469 of file trigger.c.

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

Referenced by ATExecEnableDisableTrigger(), and EnableDisableTrigger().

1471 {
1472  Relation tgrel;
1473  int nkeys;
1474  ScanKeyData keys[2];
1475  SysScanDesc tgscan;
1476  HeapTuple tuple;
1477  bool found;
1478  bool changed;
1479 
1480  /* Scan the relevant entries in pg_triggers */
1481  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1482 
1483  ScanKeyInit(&keys[0],
1484  Anum_pg_trigger_tgrelid,
1485  BTEqualStrategyNumber, F_OIDEQ,
1487  if (tgname)
1488  {
1489  ScanKeyInit(&keys[1],
1490  Anum_pg_trigger_tgname,
1491  BTEqualStrategyNumber, F_NAMEEQ,
1492  CStringGetDatum(tgname));
1493  nkeys = 2;
1494  }
1495  else
1496  nkeys = 1;
1497 
1498  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1499  NULL, nkeys, keys);
1500 
1501  found = changed = false;
1502 
1503  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1504  {
1505  Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
1506 
1507  if (oldtrig->tgisinternal)
1508  {
1509  /* system trigger ... ok to process? */
1510  if (skip_system)
1511  continue;
1512  if (!superuser())
1513  ereport(ERROR,
1514  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1515  errmsg("permission denied: \"%s\" is a system trigger",
1516  NameStr(oldtrig->tgname))));
1517  }
1518 
1519  found = true;
1520 
1521  if (oldtrig->tgenabled != fires_when)
1522  {
1523  /* need to change this one ... make a copy to scribble on */
1524  HeapTuple newtup = heap_copytuple(tuple);
1525  Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
1526 
1527  newtrig->tgenabled = fires_when;
1528 
1529  CatalogTupleUpdate(tgrel, &newtup->t_self, newtup);
1530 
1531  heap_freetuple(newtup);
1532 
1533  /*
1534  * When altering FOR EACH ROW triggers on a partitioned table, do
1535  * the same on the partitions as well.
1536  */
1537  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
1538  (TRIGGER_FOR_ROW(oldtrig->tgtype)))
1539  {
1540  PartitionDesc partdesc = RelationGetPartitionDesc(rel);
1541  int i;
1542 
1543  for (i = 0; i < partdesc->nparts; i++)
1544  {
1545  Relation part;
1546 
1547  part = relation_open(partdesc->oids[i], lockmode);
1548  EnableDisableTrigger(part, NameStr(oldtrig->tgname),
1549  fires_when, skip_system, lockmode);
1550  table_close(part, NoLock); /* keep lock till commit */
1551  }
1552  }
1553 
1554  changed = true;
1555  }
1556 
1557  InvokeObjectPostAlterHook(TriggerRelationId,
1558  oldtrig->oid, 0);
1559  }
1560 
1561  systable_endscan(tgscan);
1562 
1563  table_close(tgrel, RowExclusiveLock);
1564 
1565  if (tgname && !found)
1566  ereport(ERROR,
1567  (errcode(ERRCODE_UNDEFINED_OBJECT),
1568  errmsg("trigger \"%s\" for table \"%s\" does not exist",
1569  tgname, RelationGetRelationName(rel))));
1570 
1571  /*
1572  * If we changed anything, broadcast a SI inval message to force each
1573  * backend (including our own!) to rebuild relation's relcache entry.
1574  * Otherwise they will fail to apply the change promptly.
1575  */
1576  if (changed)
1578 }
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:680
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:529
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
int errcode(int sqlerrcode)
Definition: elog.c:610
bool superuser(void)
Definition: superuser.c:46
Form_pg_class rd_rel
Definition: rel.h:89
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:356
#define TriggerRelidNameIndexId
Definition: indexing.h:253
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:448
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
ItemPointerData t_self
Definition: htup.h:65
void EnableDisableTrigger(Relation rel, const char *tgname, char fires_when, bool skip_system, LOCKMODE lockmode)
Definition: trigger.c:1469
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
#define CStringGetDatum(X)
Definition: postgres.h:578
PartitionDesc RelationGetPartitionDesc(Relation rel)
Definition: partdesc.c:65
#define RelationGetRelationName(relation)
Definition: rel.h:470
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:175
#define ereport(elevel,...)
Definition: elog.h:144
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:224
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:72
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1270
int errmsg(const char *fmt,...)
Definition: elog.c:824
int i
#define NameStr(name)
Definition: c.h:615
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define RelationGetRelid(relation)
Definition: rel.h:436
#define BTEqualStrategyNumber
Definition: stratnum.h:31

◆ ExecARDeleteTriggers()

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

Definition at line 2498 of file trigger.c.

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

2502 {
2503  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2504  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2505 
2506  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2507  (transition_capture && transition_capture->tcs_delete_old_table))
2508  {
2509  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2510  if (fdw_trigtuple == NULL)
2511  GetTupleForTrigger(estate,
2512  NULL,
2513  relinfo,
2514  tupleid,
2516  slot,
2517  NULL);
2518  else
2519  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2520 
2522  true, slot, NULL, NIL, NULL,
2523  transition_capture);
2524  }
2525 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define NIL
Definition: pg_list.h:65
static bool GetTupleForTrigger(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **newSlot)
Definition: trigger.c:2949
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:100
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1104
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1439
bool trig_delete_after_row
Definition: reltrigger.h:67
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:425
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, int event, bool row_trigger, TupleTableSlot *oldtup, TupleTableSlot *newtup, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture)
Definition: trigger.c:5357
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:738

◆ ExecARInsertTriggers()

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

Definition at line 2268 of file trigger.c.

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

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

2271 {
2272  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2273 
2274  if ((trigdesc && trigdesc->trig_insert_after_row) ||
2275  (transition_capture && transition_capture->tcs_insert_new_table))
2277  true, NULL, slot,
2278  recheckIndexes, NULL,
2279  transition_capture);
2280 }
bool trig_insert_after_row
Definition: reltrigger.h:57
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:425
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, int event, bool row_trigger, TupleTableSlot *oldtup, TupleTableSlot *newtup, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture)
Definition: trigger.c:5357
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:99

◆ ExecARUpdateTriggers()

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

Definition at line 2783 of file trigger.c.

References AfterTriggerSaveEvent(), ExecClearTuple(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), GetAllUpdatedColumns, 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 ExecDelete(), ExecInsert(), ExecSimpleRelationUpdate(), and ExecUpdate().

2789 {
2790  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2791  TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
2792 
2793  ExecClearTuple(oldslot);
2794 
2795  if ((trigdesc && trigdesc->trig_update_after_row) ||
2796  (transition_capture &&
2797  (transition_capture->tcs_update_old_table ||
2798  transition_capture->tcs_update_new_table)))
2799  {
2800  /*
2801  * Note: if the UPDATE is converted into a DELETE+INSERT as part of
2802  * update-partition-key operation, then this function is also called
2803  * separately for DELETE and INSERT to capture transition table rows.
2804  * In such case, either old tuple or new tuple can be NULL.
2805  */
2806  if (fdw_trigtuple == NULL && ItemPointerIsValid(tupleid))
2807  GetTupleForTrigger(estate,
2808  NULL,
2809  relinfo,
2810  tupleid,
2812  oldslot,
2813  NULL);
2814  else if (fdw_trigtuple != NULL)
2815  ExecForceStoreHeapTuple(fdw_trigtuple, oldslot, false);
2816 
2817