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

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3322 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 3395 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 3406 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:3384
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3358
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3336

Definition at line 3397 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:3358
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3336

Definition at line 3408 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:547
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:225

Definition at line 79 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:3322
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3320
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3321

Definition at line 3358 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3336 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3324 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3489 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3491 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3490 of file trigger.c.

◆ SetConstraintState

Definition at line 3272 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3251 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3312 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 3648 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().

3650 {
3651  Size eventsize = SizeofTriggerEvent(event);
3652  Size needed = eventsize + sizeof(AfterTriggerSharedData);
3653  AfterTriggerEventChunk *chunk;
3654  AfterTriggerShared newshared;
3655  AfterTriggerEvent newevent;
3656 
3657  /*
3658  * If empty list or not enough room in the tail chunk, make a new chunk.
3659  * We assume here that a new shared record will always be needed.
3660  */
3661  chunk = events->tail;
3662  if (chunk == NULL ||
3663  chunk->endfree - chunk->freeptr < needed)
3664  {
3665  Size chunksize;
3666 
3667  /* Create event context if we didn't already */
3668  if (afterTriggers.event_cxt == NULL)
3671  "AfterTriggerEvents",
3673 
3674  /*
3675  * Chunk size starts at 1KB and is allowed to increase up to 1MB.
3676  * These numbers are fairly arbitrary, though there is a hard limit at
3677  * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
3678  * shared records using the available space in ate_flags. Another
3679  * constraint is that if the chunk size gets too huge, the search loop
3680  * below would get slow given a (not too common) usage pattern with
3681  * many distinct event types in a chunk. Therefore, we double the
3682  * preceding chunk size only if there weren't too many shared records
3683  * in the preceding chunk; otherwise we halve it. This gives us some
3684  * ability to adapt to the actual usage pattern of the current query
3685  * while still having large chunk sizes in typical usage. All chunk
3686  * sizes used should be MAXALIGN multiples, to ensure that the shared
3687  * records will be aligned safely.
3688  */
3689 #define MIN_CHUNK_SIZE 1024
3690 #define MAX_CHUNK_SIZE (1024*1024)
3691 
3692 #if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
3693 #error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
3694 #endif
3695 
3696  if (chunk == NULL)
3697  chunksize = MIN_CHUNK_SIZE;
3698  else
3699  {
3700  /* preceding chunk size... */
3701  chunksize = chunk->endptr - (char *) chunk;
3702  /* check number of shared records in preceding chunk */
3703  if ((chunk->endptr - chunk->endfree) <=
3704  (100 * sizeof(AfterTriggerSharedData)))
3705  chunksize *= 2; /* okay, double it */
3706  else
3707  chunksize /= 2; /* too many shared records */
3708  chunksize = Min(chunksize, MAX_CHUNK_SIZE);
3709  }
3710  chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize);
3711  chunk->next = NULL;
3712  chunk->freeptr = CHUNK_DATA_START(chunk);
3713  chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
3714  Assert(chunk->endfree - chunk->freeptr >= needed);
3715 
3716  if (events->head == NULL)
3717  events->head = chunk;
3718  else
3719  events->tail->next = chunk;
3720  events->tail = chunk;
3721  /* events->tailfree is now out of sync, but we'll fix it below */
3722  }
3723 
3724  /*
3725  * Try to locate a matching shared-data record already in the chunk. If
3726  * none, make a new one.
3727  */
3728  for (newshared = ((AfterTriggerShared) chunk->endptr) - 1;
3729  (char *) newshared >= chunk->endfree;
3730  newshared--)
3731  {
3732  if (newshared->ats_tgoid == evtshared->ats_tgoid &&
3733  newshared->ats_relid == evtshared->ats_relid &&
3734  newshared->ats_event == evtshared->ats_event &&
3735  newshared->ats_table == evtshared->ats_table &&
3736  newshared->ats_firing_id == 0)
3737  break;
3738  }
3739  if ((char *) newshared < chunk->endfree)
3740  {
3741  *newshared = *evtshared;
3742  newshared->ats_firing_id = 0; /* just to be sure */
3743  chunk->endfree = (char *) newshared;
3744  }
3745 
3746  /* Insert the data */
3747  newevent = (AfterTriggerEvent) chunk->freeptr;
3748  memcpy(newevent, event, eventsize);
3749  /* ... and link the new event to its shared record */
3750  newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
3751  newevent->ate_flags |= (char *) newshared - (char *) newevent;
3752 
3753  chunk->freeptr += eventsize;
3754  events->tailfree = chunk->freeptr;
3755 }
TriggerEvent ats_event
Definition: trigger.c:3328
#define AllocSetContextCreate
Definition: memutils.h:170
MemoryContext TopTransactionContext
Definition: mcxt.c:49
#define MIN_CHUNK_SIZE
TriggerFlags ate_flags
Definition: trigger.c:3340
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3384
struct AfterTriggerSharedData AfterTriggerSharedData
#define Min(x, y)
Definition: c.h:982
AfterTriggerEventChunk * tail
Definition: trigger.c:3390
struct AfterTriggerEventChunk * next
Definition: trigger.c:3377
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3358
#define AFTER_TRIGGER_OFFSET
Definition: trigger.c:3314
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3332
#define Assert(condition)
Definition: c.h:800
size_t Size
Definition: c.h:528
CommandId ats_firing_id
Definition: trigger.c:3331
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3336
#define MAX_CHUNK_SIZE
AfterTriggerEventChunk * head
Definition: trigger.c:3389
MemoryContext event_cxt
Definition: trigger.c:3498
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4502 of file trigger.c.

References AfterTriggersData::query_depth.

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

4503 {
4504  /* Increase the query stack depth */
4506 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 4770 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().

4771 {
4772  int my_level = GetCurrentTransactionNestLevel();
4773 
4774  /*
4775  * Allocate more space in the trans_stack if needed. (Note: because the
4776  * minimum nest level of a subtransaction is 2, we waste the first couple
4777  * entries of the array; not worth the notational effort to avoid it.)
4778  */
4779  while (my_level >= afterTriggers.maxtransdepth)
4780  {
4781  if (afterTriggers.maxtransdepth == 0)
4782  {
4783  /* Arbitrarily initialize for max of 8 subtransaction levels */
4786  8 * sizeof(AfterTriggersTransData));
4788  }
4789  else
4790  {
4791  /* repalloc will keep the stack in the same context */
4792  int new_alloc = afterTriggers.maxtransdepth * 2;
4793 
4796  new_alloc * sizeof(AfterTriggersTransData));
4797  afterTriggers.maxtransdepth = new_alloc;
4798  }
4799  }
4800 
4801  /*
4802  * Push the current information into the stack. The SET CONSTRAINTS state
4803  * is not saved until/unless changed. Likewise, we don't make a
4804  * per-subtransaction event context until needed.
4805  */
4806  afterTriggers.trans_stack[my_level].state = NULL;
4810 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3506
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggerEventList events
Definition: trigger.c:3521
CommandId firing_counter
Definition: trigger.c:3523
CommandId firing_counter
Definition: trigger.c:3495
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:857
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1070
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
AfterTriggerEventList events
Definition: trigger.c:3497
static AfterTriggersData afterTriggers
Definition: trigger.c:3540
SetConstraintState state
Definition: trigger.c:3520

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

Definition at line 4470 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().

4471 {
4472  /*
4473  * Initialize after-trigger state structure to empty
4474  */
4475  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4477 
4478  /*
4479  * Verify that there is no leftover state remaining. If these assertions
4480  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4481  * up properly.
4482  */
4483  Assert(afterTriggers.state == NULL);
4484  Assert(afterTriggers.query_stack == NULL);
4486  Assert(afterTriggers.event_cxt == NULL);
4487  Assert(afterTriggers.events.head == NULL);
4488  Assert(afterTriggers.trans_stack == NULL);
4490 }
uint32 CommandId
Definition: c.h:589
AfterTriggersTransData * trans_stack
Definition: trigger.c:3506
AfterTriggersQueryData * query_stack
Definition: trigger.c:3501
SetConstraintState state
Definition: trigger.c:3496
CommandId firing_counter
Definition: trigger.c:3495
#define Assert(condition)
Definition: c.h:800
AfterTriggerEventChunk * head
Definition: trigger.c:3389
MemoryContext event_cxt
Definition: trigger.c:3498
AfterTriggerEventList events
Definition: trigger.c:3497
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

Definition at line 3602 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().

3603 {
3604  Oid tgoid = evtshared->ats_tgoid;
3606  int i;
3607 
3608  /*
3609  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3610  * constraints declared NOT DEFERRABLE), the state is always false.
3611  */
3612  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3613  return false;
3614 
3615  /*
3616  * If constraint state exists, SET CONSTRAINTS might have been executed
3617  * either for this trigger or for all triggers.
3618  */
3619  if (state != NULL)
3620  {
3621  /* Check for SET CONSTRAINTS for this specific trigger. */
3622  for (i = 0; i < state->numstates; i++)
3623  {
3624  if (state->trigstates[i].sct_tgoid == tgoid)
3625  return state->trigstates[i].sct_tgisdeferred;
3626  }
3627 
3628  /* Check for SET CONSTRAINTS ALL. */
3629  if (state->all_isset)
3630  return state->all_isdeferred;
3631  }
3632 
3633  /*
3634  * Otherwise return the default state for the trigger.
3635  */
3636  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3637 }
TriggerEvent ats_event
Definition: trigger.c:3328
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:107
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:106
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3269
unsigned int Oid
Definition: postgres_ext.h:31
SetConstraintState state
Definition: trigger.c:3496
Definition: regguts.h:298
int i
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 3825 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().

3826 {
3827  AfterTriggerEventChunk *target = qs->events.head;
3828  ListCell *lc;
3829 
3830  Assert(target && target->next);
3831 
3832  /*
3833  * First, update any pointers in the per-table data, so that they won't be
3834  * dangling. Resetting obsoleted pointers to NULL will make
3835  * cancel_prior_stmt_triggers start from the list head, which is fine.
3836  */
3837  foreach(lc, qs->tables)
3838  {
3840 
3841  if (table->after_trig_done &&
3842  table->after_trig_events.tail == target)
3843  {
3844  table->after_trig_events.head = NULL;
3845  table->after_trig_events.tail = NULL;
3846  table->after_trig_events.tailfree = NULL;
3847  }
3848  }
3849 
3850  /* Now we can flush the head chunk */
3851  qs->events.head = target->next;
3852  pfree(target);
3853 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3390
struct AfterTriggerEventChunk * next
Definition: trigger.c:3377
void pfree(void *pointer)
Definition: mcxt.c:1057
AfterTriggerEventList after_trig_events
Definition: trigger.c:3534
#define Assert(condition)
Definition: c.h:800
#define lfirst(lc)
Definition: pg_list.h:169
AfterTriggerEventChunk * head
Definition: trigger.c:3389
AfterTriggerEventList events
Definition: trigger.c:3512

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 4522 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().

4523 {
4525 
4526  /* Must be inside a query, too */
4528 
4529  /*
4530  * If we never even got as far as initializing the event stack, there
4531  * certainly won't be any events, so exit quickly.
4532  */
4534  {
4536  return;
4537  }
4538 
4539  /*
4540  * Process all immediate-mode triggers queued by the query, and move the
4541  * deferred ones to the main list of deferred events.
4542  *
4543  * Notice that we decide which ones will be fired, and put the deferred
4544  * ones on the main list, before anything is actually fired. This ensures
4545  * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
4546  * IMMEDIATE: all events we have decided to defer will be available for it
4547  * to fire.
4548  *
4549  * We loop in case a trigger queues more events at the same query level.
4550  * Ordinary trigger functions, including all PL/pgSQL trigger functions,
4551  * will instead fire any triggers in a dedicated query level. Foreign key
4552  * enforcement triggers do add to the current query level, thanks to their
4553  * passing fire_triggers = false to SPI_execute_snapshot(). Other
4554  * C-language triggers might do likewise.
4555  *
4556  * If we find no firable events, we don't have to increment
4557  * firing_counter.
4558  */
4560 
4561  for (;;)
4562  {
4564  {
4565  CommandId firing_id = afterTriggers.firing_counter++;
4566  AfterTriggerEventChunk *oldtail = qs->events.tail;
4567 
4568  if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
4569  break; /* all fired */
4570 
4571  /*
4572  * Firing a trigger could result in query_stack being repalloc'd,
4573  * so we must recalculate qs after each afterTriggerInvokeEvents
4574  * call. Furthermore, it's unsafe to pass delete_ok = true here,
4575  * because that could cause afterTriggerInvokeEvents to try to
4576  * access qs->events after the stack has been repalloc'd.
4577  */
4579 
4580  /*
4581  * We'll need to scan the events list again. To reduce the cost
4582  * of doing so, get rid of completely-fired chunks. We know that
4583  * all events were marked IN_PROGRESS or DONE at the conclusion of
4584  * afterTriggerMarkEvents, so any still-interesting events must
4585  * have been added after that, and so must be in the chunk that
4586  * was then the tail chunk, or in later chunks. So, zap all
4587  * chunks before oldtail. This is approximately the same set of
4588  * events we would have gotten rid of by passing delete_ok = true.
4589  */
4590  Assert(oldtail != NULL);
4591  while (qs->events.head != oldtail)
4593  }
4594  else
4595  break;
4596  }
4597 
4598  /* Release query-level-local storage, including tuplestores if any */
4600 
4602 }
uint32 CommandId
Definition: c.h:589
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition: trigger.c:3825
AfterTriggersQueryData * query_stack
Definition: trigger.c:3501
AfterTriggerEventChunk * tail
Definition: trigger.c:3390
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4177
CommandId firing_counter
Definition: trigger.c:3495
#define Assert(condition)
Definition: c.h:800
AfterTriggerEventChunk * head
Definition: trigger.c:3389
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4093
AfterTriggerEventList events
Definition: trigger.c:3497
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4613
AfterTriggerEventList events
Definition: trigger.c:3512
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

Definition at line 4818 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().

4819 {
4820  int my_level = GetCurrentTransactionNestLevel();
4822  AfterTriggerEvent event;
4823  AfterTriggerEventChunk *chunk;
4824  CommandId subxact_firing_id;
4825 
4826  /*
4827  * Pop the prior state if needed.
4828  */
4829  if (isCommit)
4830  {
4831  Assert(my_level < afterTriggers.maxtransdepth);
4832  /* If we saved a prior state, we don't need it anymore */
4833  state = afterTriggers.trans_stack[my_level].state;
4834  if (state != NULL)
4835  pfree(state);
4836  /* this avoids double pfree if error later: */
4837  afterTriggers.trans_stack[my_level].state = NULL;
4840  }
4841  else
4842  {
4843  /*
4844  * Aborting. It is possible subxact start failed before calling
4845  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
4846  * trans_stack levels that aren't there.
4847  */
4848  if (my_level >= afterTriggers.maxtransdepth)
4849  return;
4850 
4851  /*
4852  * Release query-level storage for queries being aborted, and restore
4853  * query_depth to its pre-subxact value. This assumes that a
4854  * subtransaction will not add events to query levels started in a
4855  * earlier transaction state.
4856  */
4858  {
4862  }
4865 
4866  /*
4867  * Restore the global deferred-event list to its former length,
4868  * discarding any events queued by the subxact.
4869  */
4871  &afterTriggers.trans_stack[my_level].events);
4872 
4873  /*
4874  * Restore the trigger state. If the saved state is NULL, then this
4875  * subxact didn't save it, so it doesn't need restoring.
4876  */
4877  state = afterTriggers.trans_stack[my_level].state;
4878  if (state != NULL)
4879  {
4881  afterTriggers.state = state;
4882  }
4883  /* this avoids double pfree if error later: */
4884  afterTriggers.trans_stack[my_level].state = NULL;
4885 
4886  /*
4887  * Scan for any remaining deferred events that were marked DONE or IN
4888  * PROGRESS by this subxact or a child, and un-mark them. We can
4889  * recognize such events because they have a firing ID greater than or
4890  * equal to the firing_counter value we saved at subtransaction start.
4891  * (This essentially assumes that the current subxact includes all
4892  * subxacts started after it.)
4893  */
4894  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
4896  {
4897  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4898 
4899  if (event->ate_flags &
4901  {
4902  if (evtshared->ats_firing_id >= subxact_firing_id)
4903  event->ate_flags &=
4905  }
4906  }
4907  }
4908 }
uint32 CommandId
Definition: c.h:589
AfterTriggersTransData * trans_stack
Definition: trigger.c:3506
TriggerFlags ate_flags
Definition: trigger.c:3340
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3315
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3402
AfterTriggerEventList events
Definition: trigger.c:3521
AfterTriggersQueryData * query_stack
Definition: trigger.c:3501
CommandId firing_counter
Definition: trigger.c:3523
#define GetTriggerSharedData(evt)
Definition: trigger.c:3365
void pfree(void *pointer)
Definition: mcxt.c:1057
SetConstraintState state
Definition: trigger.c:3496
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:857
#define Assert(condition)
Definition: c.h:800
Definition: regguts.h:298
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:3785
CommandId ats_firing_id
Definition: trigger.c:3331
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3316
AfterTriggerEventList events
Definition: trigger.c:3497
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4613
static AfterTriggersData afterTriggers
Definition: trigger.c:3540
SetConstraintState state
Definition: trigger.c:3520

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

Definition at line 4722 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().

4723 {
4724  /*
4725  * Forget the pending-events list.
4726  *
4727  * Since all the info is in TopTransactionContext or children thereof, we
4728  * don't really need to do anything to reclaim memory. However, the
4729  * pending-events list could be large, and so it's useful to discard it as
4730  * soon as possible --- especially if we are aborting because we ran out
4731  * of memory for the list!
4732  */
4734  {
4736  afterTriggers.event_cxt = NULL;
4737  afterTriggers.events.head = NULL;
4738  afterTriggers.events.tail = NULL;
4739  afterTriggers.events.tailfree = NULL;
4740  }
4741 
4742  /*
4743  * Forget any subtransaction state as well. Since this can't be very
4744  * large, we let the eventual reset of TopTransactionContext free the
4745  * memory instead of doing it here.
4746  */
4747  afterTriggers.trans_stack = NULL;
4749 
4750 
4751  /*
4752  * Forget the query stack and constraint-related state information. As
4753  * with the subtransaction state information, we don't bother freeing the
4754  * memory here.
4755  */
4756  afterTriggers.query_stack = NULL;
4758  afterTriggers.state = NULL;
4759 
4760  /* No more afterTriggers manipulation until next transaction starts. */
4762 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:212
AfterTriggersTransData * trans_stack
Definition: trigger.c:3506
AfterTriggersQueryData * query_stack
Definition: trigger.c:3501
AfterTriggerEventChunk * tail
Definition: trigger.c:3390
SetConstraintState state
Definition: trigger.c:3496
AfterTriggerEventChunk * head
Definition: trigger.c:3389
MemoryContext event_cxt
Definition: trigger.c:3498
AfterTriggerEventList events
Definition: trigger.c:3497
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

Definition at line 4920 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().

4921 {
4922  int init_depth = afterTriggers.maxquerydepth;
4923 
4925 
4926  if (afterTriggers.maxquerydepth == 0)
4927  {
4928  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
4929 
4932  new_alloc * sizeof(AfterTriggersQueryData));
4933  afterTriggers.maxquerydepth = new_alloc;
4934  }
4935  else
4936  {
4937  /* repalloc will keep the stack in the same context */
4938  int old_alloc = afterTriggers.maxquerydepth;
4939  int new_alloc = Max(afterTriggers.query_depth + 1,
4940  old_alloc * 2);
4941 
4944  new_alloc * sizeof(AfterTriggersQueryData));
4945  afterTriggers.maxquerydepth = new_alloc;
4946  }
4947 
4948  /* Initialize new array entries to empty */
4949  while (init_depth < afterTriggers.maxquerydepth)
4950  {
4952 
4953  qs->events.head = NULL;
4954  qs->events.tail = NULL;
4955  qs->events.tailfree = NULL;
4956  qs->fdw_tuplestore = NULL;
4957  qs->tables = NIL;
4958 
4959  ++init_depth;
4960  }
4961 }
#define NIL
Definition: pg_list.h:65
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggersQueryData * query_stack
Definition: trigger.c:3501
AfterTriggerEventChunk * tail
Definition: trigger.c:3390
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3513
#define Max(x, y)
Definition: c.h:976
#define Assert(condition)
Definition: c.h:800
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1070
AfterTriggerEventChunk * head
Definition: trigger.c:3389
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
AfterTriggerEventList events
Definition: trigger.c:3512
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

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

3887 {
3888  Relation rel = relInfo->ri_RelationDesc;
3889  AfterTriggerShared evtshared = GetTriggerSharedData(event);
3890  Oid tgoid = evtshared->ats_tgoid;
3891  TriggerData LocTriggerData = {0};
3892  HeapTuple rettuple;
3893  int tgindx;
3894  bool should_free_trig = false;
3895  bool should_free_new = false;
3896 
3897  /*
3898  * Locate trigger in trigdesc.
3899  */
3900  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
3901  {
3902  if (trigdesc->triggers[tgindx].tgoid == tgoid)
3903  {
3904  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
3905  break;
3906  }
3907  }
3908  if (LocTriggerData.tg_trigger == NULL)
3909  elog(ERROR, "could not find trigger %u", tgoid);
3910 
3911  /*
3912  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
3913  * to include time spent re-fetching tuples in the trigger cost.
3914  */
3915  if (instr)
3916  InstrStartNode(instr + tgindx);
3917 
3918  /*
3919  * Fetch the required tuple(s).
3920  */
3921  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
3922  {
3924  {
3925  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
3926 
3927  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
3928  trig_tuple_slot1))
3929  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
3930 
3931  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
3933  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
3934  trig_tuple_slot2))
3935  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
3936  }
3937  /* fall through */
3939 
3940  /*
3941  * Store tuple in the slot so that tg_trigtuple does not reference
3942  * tuplestore memory. (It is formally possible for the trigger
3943  * function to queue trigger events that add to the same
3944  * tuplestore, which can push other tuples out of memory.) The
3945  * distinction is academic, because we start with a minimal tuple
3946  * that is stored as a heap tuple, constructed in different memory
3947  * context, in the slot anyway.
3948  */
3949  LocTriggerData.tg_trigslot = trig_tuple_slot1;
3950  LocTriggerData.tg_trigtuple =
3951  ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
3952 
3953  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
3955  {
3956  LocTriggerData.tg_newslot = trig_tuple_slot2;
3957  LocTriggerData.tg_newtuple =
3958  ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new);
3959  }
3960  else
3961  {
3962  LocTriggerData.tg_newtuple = NULL;
3963  }
3964  break;
3965 
3966  default:
3967  if (ItemPointerIsValid(&(event->ate_ctid1)))
3968  {
3969  LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
3970 
3971  if (!table_tuple_fetch_row_version(rel, &(event->ate_ctid1),
3972  SnapshotAny,
3973  LocTriggerData.tg_trigslot))
3974  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
3975  LocTriggerData.tg_trigtuple =
3976  ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, &should_free_trig);
3977  }
3978  else
3979  {
3980  LocTriggerData.tg_trigtuple = NULL;
3981  }
3982 
3983  /* don't touch ctid2 if not there */
3984  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
3986  ItemPointerIsValid(&(event->ate_ctid2)))
3987  {
3988  LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
3989 
3990  if (!table_tuple_fetch_row_version(rel, &(event->ate_ctid2),
3991  SnapshotAny,
3992  LocTriggerData.tg_newslot))
3993  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
3994  LocTriggerData.tg_newtuple =
3995  ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, &should_free_new);
3996  }
3997  else
3998  {
3999  LocTriggerData.tg_newtuple = NULL;
4000  }
4001  }
4002 
4003  /*
4004  * Set up the tuplestore information to let the trigger have access to
4005  * transition tables. When we first make a transition table available to
4006  * a trigger, mark it "closed" so that it cannot change anymore. If any
4007  * additional events of the same type get queued in the current trigger
4008  * query level, they'll go into new transition tables.
4009  */
4010  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4011  if (evtshared->ats_table)
4012  {
4013  if (LocTriggerData.tg_trigger->tgoldtable)
4014  {
4015  LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4016  evtshared->ats_table->closed = true;
4017  }
4018 
4019  if (LocTriggerData.tg_trigger->tgnewtable)
4020  {
4021  LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4022  evtshared->ats_table->closed = true;
4023  }
4024  }
4025 
4026  /*
4027  * Setup the remaining trigger information
4028  */
4029  LocTriggerData.type = T_TriggerData;
4030  LocTriggerData.tg_event =
4032  LocTriggerData.tg_relation = rel;
4033  if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4034  LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4035 
4036  MemoryContextReset(per_tuple_context);
4037 
4038  /*
4039  * Call the trigger and throw away any possibly returned updated tuple.
4040  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4041  */
4042  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4043  tgindx,
4044  finfo,
4045  NULL,
4046  per_tuple_context);
4047  if (rettuple != NULL &&
4048  rettuple != LocTriggerData.tg_trigtuple &&
4049  rettuple != LocTriggerData.tg_newtuple)
4050  heap_freetuple(rettuple);
4051 
4052  /*
4053  * Release resources
4054  */
4055  if (should_free_trig)
4056  heap_freetuple(LocTriggerData.tg_trigtuple);
4057  if (should_free_new)
4058  heap_freetuple(LocTriggerData.tg_newtuple);
4059 
4060  /* don't clear slots' contents if foreign table */
4061  if (trig_tuple_slot1 == NULL)
4062  {
4063  if (LocTriggerData.tg_trigslot)
4064  ExecClearTuple(LocTriggerData.tg_trigslot);
4065  if (LocTriggerData.tg_newslot)
4066  ExecClearTuple(LocTriggerData.tg_newslot);
4067  }
4068 
4069  /*
4070  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4071  * one "tuple returned" (really the number of firings).
4072  */
4073  if (instr)
4074  InstrStopNode(instr + tgindx, 1);
4075 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define TRIGGER_EVENT_ROW
Definition: trigger.h:97
TriggerEvent ats_event
Definition: trigger.c:3328
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:83
TupleTableSlot * tg_trigslot
Definition: trigger.h:38
Relation ri_RelationDesc
Definition: execnodes.h:412
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3318
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3566
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
ItemPointerData ate_ctid2
Definition: trigger.c:3342
TriggerFlags ate_flags
Definition: trigger.c:3340
const Bitmapset * tg_updatedcols
Definition: trigger.h:42
Oid tgoid
Definition: reltrigger.h:25
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3322
Tuplestorestate * old_tuplestore
Definition: trigger.c:3535
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1165
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:137
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:95
TupleTableSlot * ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1187
HeapTuple tg_trigtuple
Definition: trigger.h:35
#define GetTriggerSharedData(evt)
Definition: trigger.c:3365
Bitmapset * ats_modifiedcols
Definition: trigger.c:3333
#define ERROR
Definition: elog.h:43
void InstrStartNode(Instrumentation *instr)
Definition: instrument.c:67
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:3536
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3321
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:1071
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:3332
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:3319
#define SnapshotAny
Definition: snapmgr.h:68
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:93
Tuplestorestate * tg_oldtable
Definition: trigger.h:40
NodeTag type
Definition: trigger.h:32
#define elog(elevel,...)
Definition: elog.h:228
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:2092
ItemPointerData ate_ctid1
Definition: trigger.c:3341
char * tgoldtable
Definition: reltrigger.h:43
Relation tg_relation
Definition: trigger.h:34

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

Definition at line 4666 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().

4667 {
4668  AfterTriggerEventList *events;
4669  bool snap_pushed = false;
4670 
4671  /* Must not be inside a query */
4673 
4674  /*
4675  * If there are any triggers to fire, make sure we have set a snapshot for
4676  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4677  * can't assume ActiveSnapshot is valid on entry.)
4678  */
4679  events = &afterTriggers.events;
4680  if (events->head != NULL)
4681  {
4683  snap_pushed = true;
4684  }
4685 
4686  /*
4687  * Run all the remaining triggers. Loop until they are all gone, in case
4688  * some trigger queues more for us to do.
4689  */
4690  while (afterTriggerMarkEvents(events, NULL, false))
4691  {
4692  CommandId firing_id = afterTriggers.firing_counter++;
4693 
4694  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4695  break; /* all fired */
4696  }
4697 
4698  /*
4699  * We don't bother freeing the event list, since it will go away anyway
4700  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4701  */
4702 
4703  if (snap_pushed)
4705 }
uint32 CommandId
Definition: c.h:589
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4177
void PopActiveSnapshot(void)
Definition: snapmgr.c:759
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:680
CommandId firing_counter
Definition: trigger.c:3495
#define Assert(condition)
Definition: c.h:800
AfterTriggerEventChunk * head
Definition: trigger.c:3389
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4093
AfterTriggerEventList events
Definition: trigger.c:3497
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 3764 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

3765 {
3766  AfterTriggerEventChunk *chunk;
3767 
3768  while ((chunk = events->head) != NULL)
3769  {
3770  events->head = chunk->next;
3771  pfree(chunk);
3772  }
3773  events->tail = NULL;
3774  events->tailfree = NULL;
3775 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3390
struct AfterTriggerEventChunk * next
Definition: trigger.c:3377
void pfree(void *pointer)
Definition: mcxt.c:1057
AfterTriggerEventChunk * head
Definition: trigger.c:3389

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 4613 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().

4614 {
4615  Tuplestorestate *ts;
4616  List *tables;
4617  ListCell *lc;
4618 
4619  /* Drop the trigger events */
4621 
4622  /* Drop FDW tuplestore if any */
4623  ts = qs->fdw_tuplestore;
4624  qs->fdw_tuplestore = NULL;
4625  if (ts)
4626  tuplestore_end(ts);
4627 
4628  /* Release per-table subsidiary storage */
4629  tables = qs->tables;
4630  foreach(lc, tables)
4631  {
4633 
4634  ts = table->old_tuplestore;
4635  table->old_tuplestore = NULL;
4636  if (ts)
4637  tuplestore_end(ts);
4638  ts = table->new_tuplestore;
4639  table->new_tuplestore = NULL;
4640  if (ts)
4641  tuplestore_end(ts);
4642  }
4643 
4644  /*
4645  * Now free the AfterTriggersTableData structs and list cells. Reset list
4646  * pointer first; if list_free_deep somehow gets an error, better to leak
4647  * that storage than have an infinite loop.
4648  */
4649  qs->tables = NIL;
4650  list_free_deep(tables);
4651 }
#define NIL
Definition: pg_list.h:65
Tuplestorestate * old_tuplestore
Definition: trigger.c:3535
void list_free_deep(List *list)
Definition: list.c:1390
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3764
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3513
Tuplestorestate * new_tuplestore
Definition: trigger.c:3536
#define lfirst(lc)
Definition: pg_list.h:169
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3512
Definition: pg_list.h:50

◆ afterTriggerInvokeEvents()

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

Definition at line 4177 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, ExecCloseResultRelations(), 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().

4181 {
4182  bool all_fired = true;
4183  AfterTriggerEventChunk *chunk;
4184  MemoryContext per_tuple_context;
4185  bool local_estate = false;
4186  ResultRelInfo *rInfo = NULL;
4187  Relation rel = NULL;
4188  TriggerDesc *trigdesc = NULL;
4189  FmgrInfo *finfo = NULL;
4190  Instrumentation *instr = NULL;
4191  TupleTableSlot *slot1 = NULL,
4192  *slot2 = NULL;
4193 
4194  /* Make a local EState if need be */
4195  if (estate == NULL)
4196  {
4197  estate = CreateExecutorState();
4198  local_estate = true;
4199  }
4200 
4201  /* Make a per-tuple memory context for trigger function calls */
4202  per_tuple_context =
4204  "AfterTriggerTupleContext",
4206 
4207  for_each_chunk(chunk, *events)
4208  {
4209  AfterTriggerEvent event;
4210  bool all_fired_in_chunk = true;
4211 
4212  for_each_event(event, chunk)
4213  {
4214  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4215 
4216  /*
4217  * Is it one for me to fire?
4218  */
4219  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4220  evtshared->ats_firing_id == firing_id)
4221  {
4222  /*
4223  * So let's fire it... but first, find the correct relation if
4224  * this is not the same relation as before.
4225  */
4226  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4227  {
4228  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4229  rel = rInfo->ri_RelationDesc;
4230  trigdesc = rInfo->ri_TrigDesc;
4231  finfo = rInfo->ri_TrigFunctions;
4232  instr = rInfo->ri_TrigInstrument;
4233  if (slot1 != NULL)
4234  {
4237  slot1 = slot2 = NULL;
4238  }
4239  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4240  {
4241  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4243  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4245  }
4246  if (trigdesc == NULL) /* should not happen */
4247  elog(ERROR, "relation %u has no triggers",
4248  evtshared->ats_relid);
4249  }
4250 
4251  /*
4252  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4253  * still set, so recursive examinations of the event list
4254  * won't try to re-fire it.
4255  */
4256  AfterTriggerExecute(estate, event, rInfo, trigdesc, finfo, instr,
4257  per_tuple_context, slot1, slot2);
4258 
4259  /*
4260  * Mark the event as done.
4261  */
4262  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4263  event->ate_flags |= AFTER_TRIGGER_DONE;
4264  }
4265  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4266  {
4267  /* something remains to be done */
4268  all_fired = all_fired_in_chunk = false;
4269  }
4270  }
4271 
4272  /* Clear the chunk if delete_ok and nothing left of interest */
4273  if (delete_ok && all_fired_in_chunk)
4274  {
4275  chunk->freeptr = CHUNK_DATA_START(chunk);
4276  chunk->endfree = chunk->endptr;
4277 
4278  /*
4279  * If it's last chunk, must sync event list's tailfree too. Note
4280  * that delete_ok must NOT be passed as true if there could be
4281  * additional AfterTriggerEventList values pointing at this event
4282  * list, since we'd fail to fix their copies of tailfree.
4283  */
4284  if (chunk == events->tail)
4285  events->tailfree = chunk->freeptr;
4286  }
4287  }
4288  if (slot1 != NULL)
4289  {
4292  }
4293 
4294  /* Release working resources */
4295  MemoryContextDelete(per_tuple_context);
4296 
4297  if (local_estate)
4298  {
4299  ExecCloseResultRelations(estate);
4300  ExecResetTupleTable(estate->es_tupleTable, false);
4301  FreeExecutorState(estate);
4302  }
4303 
4304  return all_fired;
4305 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:412
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:212
#define AllocSetContextCreate
Definition: memutils.h:170
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1208
TriggerFlags ate_flags
Definition: trigger.c:3340
void ExecCloseResultRelations(EState *estate)
Definition: execMain.c:1430
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3315
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3384
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:433
AfterTriggerEventChunk * tail
Definition: trigger.c:3390
Form_pg_class rd_rel
Definition: rel.h:110
#define GetTriggerSharedData(evt)
Definition: trigger.c:3365
#define for_each_event(eptr, cptr)
Definition: trigger.c:3397
void FreeExecutorState(EState *estate)
Definition: execUtils.c:185
#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:424
EState * CreateExecutorState(void)
Definition: execUtils.c:89
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1273
List * es_tupleTable
Definition: execnodes.h:561
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1161
TupleDesc rd_att
Definition: rel.h:111
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3395
CommandId ats_firing_id
Definition: trigger.c:3331
#define elog(elevel,...)
Definition: elog.h:228
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3316
#define RelationGetRelid(relation)
Definition: rel.h:457
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:427
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:3879

◆ afterTriggerMarkEvents()

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

Definition at line 4093 of file trigger.c.

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

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

4096 {
4097  bool found = false;
4098  bool deferred_found = false;
4099  AfterTriggerEvent event;
4100  AfterTriggerEventChunk *chunk;
4101 
4102  for_each_event_chunk(event, chunk, *events)
4103  {
4104  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4105  bool defer_it = false;
4106 
4107  if (!(event->ate_flags &
4109  {
4110  /*
4111  * This trigger hasn't been called or scheduled yet. Check if we
4112  * should call it now.
4113  */
4114  if (immediate_only && afterTriggerCheckState(evtshared))
4115  {
4116  defer_it = true;
4117  }
4118  else
4119  {
4120  /*
4121  * Mark it as to be fired in this firing cycle.
4122  */
4124  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4125  found = true;
4126  }
4127  }
4128 
4129  /*
4130  * If it's deferred, move it to move_list, if requested.
4131  */
4132  if (defer_it && move_list != NULL)
4133  {
4134  deferred_found = true;
4135  /* add it to move_list */
4136  afterTriggerAddEvent(move_list, event, evtshared);
4137  /* mark original copy "done" so we don't do it again */
4138  event->ate_flags |= AFTER_TRIGGER_DONE;
4139  }
4140  }
4141 
4142  /*
4143  * We could allow deferred triggers if, before the end of the
4144  * security-restricted operation, we were to verify that a SET CONSTRAINTS
4145  * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4146  */
4147  if (deferred_found && InSecurityRestrictedOperation())
4148  ereport(ERROR,
4149  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4150  errmsg("cannot fire deferred trigger within security-restricted operation")));
4151 
4152  return found;
4153 }
TriggerFlags ate_flags
Definition: trigger.c:3340
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3315
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3402
int errcode(int sqlerrcode)
Definition: elog.c:691
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3602
#define GetTriggerSharedData(evt)
Definition: trigger.c:3365
#define ERROR
Definition: elog.h:43
CommandId firing_counter
Definition: trigger.c:3495
#define ereport(elevel,...)
Definition: elog.h:155
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:608
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3648
CommandId ats_firing_id
Definition: trigger.c:3331
int errmsg(const char *fmt,...)
Definition: elog.c:902
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3316
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

Definition at line 5357 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().

5358 {
5359  AfterTriggerEvent event;
5360  AfterTriggerEventChunk *chunk;
5361  int depth;
5362 
5363  /* Scan queued events */
5365  {
5366  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5367 
5368  /*
5369  * We can ignore completed events. (Even if a DONE flag is rolled
5370  * back by subxact abort, it's OK because the effects of the TRUNCATE
5371  * or whatever must get rolled back too.)
5372  */
5373  if (event->ate_flags & AFTER_TRIGGER_DONE)
5374  continue;
5375 
5376  if (evtshared->ats_relid == relid)
5377  return true;
5378  }
5379 
5380  /*
5381  * Also scan events queued by incomplete queries. This could only matter
5382  * if TRUNCATE/etc is executed by a function or trigger within an updating
5383  * query on the same relation, which is pretty perverse, but let's check.
5384  */
5385  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5386  {
5388  {
5389  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5390 
5391  if (event->ate_flags & AFTER_TRIGGER_DONE)
5392  continue;
5393 
5394  if (evtshared->ats_relid == relid)
5395  return true;
5396  }
5397  }
5398 
5399  return false;
5400 }
TriggerFlags ate_flags
Definition: trigger.c:3340
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3315
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3402
AfterTriggersQueryData * query_stack
Definition: trigger.c:3501
#define GetTriggerSharedData(evt)
Definition: trigger.c:3365
AfterTriggerEventList events
Definition: trigger.c:3497
AfterTriggerEventList events
Definition: trigger.c:3512
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ afterTriggerRestoreEventList()

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

Definition at line 3785 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

3787 {
3788  AfterTriggerEventChunk *chunk;
3789  AfterTriggerEventChunk *next_chunk;
3790 
3791  if (old_events->tail == NULL)
3792  {
3793  /* restoring to a completely empty state, so free everything */
3794  afterTriggerFreeEventList(events);
3795  }
3796  else
3797  {
3798  *events = *old_events;
3799  /* free any chunks after the last one we want to keep */
3800  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
3801  {
3802  next_chunk = chunk->next;
3803  pfree(chunk);
3804  }
3805  /* and clean up the tail chunk to be the right length */
3806  events->tail->next = NULL;
3807  events->tail->freeptr = events->tailfree;
3808 
3809  /*
3810  * We don't make any effort to remove now-unused shared data records.
3811  * They might still be useful, anyway.
3812  */
3813  }
3814 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3390
struct AfterTriggerEventChunk * next
Definition: trigger.c:3377
void pfree(void *pointer)
Definition: mcxt.c:1057
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3764

◆ 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 5427 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, ResultRelInfo::ri_ChildToRootMap, 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_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().

5432 {
5433  Relation rel = relinfo->ri_RelationDesc;
5434  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
5435  AfterTriggerEventData new_event;
5436  AfterTriggerSharedData new_shared;
5437  char relkind = rel->rd_rel->relkind;
5438  int tgtype_event;
5439  int tgtype_level;
5440  int i;
5441  Tuplestorestate *fdw_tuplestore = NULL;
5442 
5443  /*
5444  * Check state. We use a normal test not Assert because it is possible to
5445  * reach here in the wrong state given misconfigured RI triggers, in
5446  * particular deferring a cascade action trigger.
5447  */
5448  if (afterTriggers.query_depth < 0)
5449  elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
5450 
5451  /* Be sure we have enough space to record events at this query depth. */
5454 
5455  /*
5456  * If the directly named relation has any triggers with transition tables,
5457  * then we need to capture transition tuples.
5458  */
5459  if (row_trigger && transition_capture != NULL)
5460  {
5461  TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
5462  TupleConversionMap *map = relinfo->ri_ChildToRootMap;
5463  bool delete_old_table = transition_capture->tcs_delete_old_table;
5464  bool update_old_table = transition_capture->tcs_update_old_table;
5465  bool update_new_table = transition_capture->tcs_update_new_table;
5466  bool insert_new_table = transition_capture->tcs_insert_new_table;
5467 
5468  /*
5469  * For INSERT events NEW should be non-NULL, for DELETE events OLD
5470  * should be non-NULL, whereas for UPDATE events normally both OLD and
5471  * NEW are non-NULL. But for UPDATE events fired for capturing
5472  * transition tuples during UPDATE partition-key row movement, OLD is
5473  * NULL when the event is for a row being inserted, whereas NEW is
5474  * NULL when the event is for a row being deleted.
5475  */
5476  Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
5477  TupIsNull(oldslot)));
5478  Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
5479  TupIsNull(newslot)));
5480 
5481  if (!TupIsNull(oldslot) &&
5482  ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
5483  (event == TRIGGER_EVENT_UPDATE && update_old_table)))
5484  {
5485  Tuplestorestate *old_tuplestore;
5486 
5487  old_tuplestore = transition_capture->tcs_private->old_tuplestore;
5488 
5489  if (map != NULL)
5490  {
5491  TupleTableSlot *storeslot;
5492 
5493  storeslot = transition_capture->tcs_private->storeslot;
5494  if (!storeslot)
5495  {
5496  storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
5497  map->outdesc,
5498  &TTSOpsVirtual);
5499  transition_capture->tcs_private->storeslot = storeslot;
5500  }
5501 
5502  execute_attr_map_slot(map->attrMap, oldslot, storeslot);
5503  tuplestore_puttupleslot(old_tuplestore, storeslot);
5504  }
5505  else
5506  tuplestore_puttupleslot(old_tuplestore, oldslot);
5507  }
5508  if (!TupIsNull(newslot) &&
5509  ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
5510  (event == TRIGGER_EVENT_UPDATE && update_new_table)))
5511  {
5512  Tuplestorestate *new_tuplestore;
5513 
5514  new_tuplestore = transition_capture->tcs_private->new_tuplestore;
5515 
5516  if (original_insert_tuple != NULL)
5517  tuplestore_puttupleslot(new_tuplestore,
5518  original_insert_tuple);
5519  else if (map != NULL)
5520  {
5521  TupleTableSlot *storeslot;
5522 
5523  storeslot = transition_capture->tcs_private->storeslot;
5524 
5525  if (!storeslot)
5526  {
5527  storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
5528  map->outdesc,
5529  &TTSOpsVirtual);
5530  transition_capture->tcs_private->storeslot = storeslot;
5531  }
5532 
5533  execute_attr_map_slot(map->attrMap, newslot, storeslot);
5534  tuplestore_puttupleslot(new_tuplestore, storeslot);
5535  }
5536  else
5537  tuplestore_puttupleslot(new_tuplestore, newslot);
5538  }
5539 
5540  /*
5541  * If transition tables are the only reason we're here, return. As
5542  * mentioned above, we can also be here during update tuple routing in
5543  * presence of transition tables, in which case this function is
5544  * called separately for oldtup and newtup, so we expect exactly one
5545  * of them to be NULL.
5546  */
5547  if (trigdesc == NULL ||
5548  (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
5549  (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
5550  (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
5551  (event == TRIGGER_EVENT_UPDATE && (TupIsNull(oldslot) ^ TupIsNull(newslot))))
5552  return;
5553  }
5554 
5555  /*
5556  * Validate the event code and collect the associated tuple CTIDs.
5557  *
5558  * The event code will be used both as a bitmask and an array offset, so
5559  * validation is important to make sure we don't walk off the edge of our
5560  * arrays.
5561  *
5562  * Also, if we're considering statement-level triggers, check whether we
5563  * already queued a set of them for this event, and cancel the prior set
5564  * if so. This preserves the behavior that statement-level triggers fire
5565  * just once per statement and fire after row-level triggers.
5566  */
5567  switch (event)
5568  {
5569  case TRIGGER_EVENT_INSERT:
5570  tgtype_event = TRIGGER_TYPE_INSERT;
5571  if (row_trigger)
5572  {
5573  Assert(oldslot == NULL);
5574  Assert(newslot != NULL);
5575  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
5576  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5577  }
5578  else
5579  {
5580  Assert(oldslot == NULL);
5581  Assert(newslot == NULL);
5582  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5583  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5585  CMD_INSERT, event);
5586  }
5587  break;
5588  case TRIGGER_EVENT_DELETE:
5589  tgtype_event = TRIGGER_TYPE_DELETE;
5590  if (row_trigger)
5591  {
5592  Assert(oldslot != NULL);
5593  Assert(newslot == NULL);
5594  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
5595  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5596  }
5597  else
5598  {
5599  Assert(oldslot == NULL);
5600  Assert(newslot == NULL);
5601  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5602  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5604  CMD_DELETE, event);
5605  }
5606  break;
5607  case TRIGGER_EVENT_UPDATE:
5608  tgtype_event = TRIGGER_TYPE_UPDATE;
5609  if (row_trigger)
5610  {
5611  Assert(oldslot != NULL);
5612  Assert(newslot != NULL);
5613  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
5614  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
5615  }
5616  else
5617  {
5618  Assert(oldslot == NULL);
5619  Assert(newslot == NULL);
5620  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5621  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5623  CMD_UPDATE, event);
5624  }
5625  break;
5627  tgtype_event = TRIGGER_TYPE_TRUNCATE;
5628  Assert(oldslot == NULL);
5629  Assert(newslot == NULL);
5630  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5631  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5632  break;
5633  default:
5634  elog(ERROR, "invalid after-trigger event code: %d", event);
5635  tgtype_event = 0; /* keep compiler quiet */
5636  break;
5637  }
5638 
5639  if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
5640  new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ?
5642  /* else, we'll initialize ate_flags for each trigger */
5643 
5644  tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
5645 
5646  for (i = 0; i < trigdesc->numtriggers; i++)
5647  {
5648  Trigger *trigger = &trigdesc->triggers[i];
5649 
5650  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
5651  tgtype_level,
5652  TRIGGER_TYPE_AFTER,
5653  tgtype_event))
5654  continue;
5655  if (!TriggerEnabled(estate, relinfo, trigger, event,
5656  modifiedCols, oldslot, newslot))
5657  continue;
5658 
5659  if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
5660  {
5661  if (fdw_tuplestore == NULL)
5662  {
5663  fdw_tuplestore = GetCurrentFDWTuplestore();
5664  new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH;
5665  }
5666  else
5667  /* subsequent event for the same tuple */
5668  new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE;
5669  }
5670 
5671  /*
5672  * If the trigger is a foreign key enforcement trigger, there are
5673  * certain cases where we can skip queueing the event because we can
5674  * tell by inspection that the FK constraint will still pass.
5675  */
5676  if (TRIGGER_FIRED_BY_UPDATE(event) || TRIGGER_FIRED_BY_DELETE(event))
5677  {
5678  switch (RI_FKey_trigger_type(trigger->tgfoid))
5679  {
5680  case RI_TRIGGER_PK:
5681  /* Update or delete on trigger's PK table */
5682  if (!RI_FKey_pk_upd_check_required(trigger, rel,
5683  oldslot, newslot))
5684  {
5685  /* skip queuing this event */
5686  continue;
5687  }
5688  break;
5689 
5690  case RI_TRIGGER_FK:
5691  /* Update on trigger's FK table */
5692  if (!RI_FKey_fk_upd_check_required(trigger, rel,
5693  oldslot, newslot))
5694  {
5695  /* skip queuing this event */
5696  continue;
5697  }
5698  break;
5699 
5700  case RI_TRIGGER_NONE:
5701  /* Not an FK trigger */
5702  break;
5703  }
5704  }
5705 
5706  /*
5707  * If the trigger is a deferred unique constraint check trigger, only
5708  * queue it if the unique constraint was potentially violated, which
5709  * we know from index insertion time.
5710  */
5711  if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
5712  {
5713  if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
5714  continue; /* Uniqueness definitely not violated */
5715  }
5716 
5717  /*
5718  * Fill in event structure and add it to the current query's queue.
5719  * Note we set ats_table to NULL whenever this trigger doesn't use
5720  * transition tables, to improve sharability of the shared event data.
5721  */
5722  new_shared.ats_event =
5723  (event & TRIGGER_EVENT_OPMASK) |
5724  (row_trigger ? TRIGGER_EVENT_ROW : 0) |
5725  (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
5726  (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
5727  new_shared.ats_tgoid = trigger->tgoid;
5728  new_shared.ats_relid = RelationGetRelid(rel);
5729  new_shared.ats_firing_id = 0;
5730  if ((trigger->tgoldtable || trigger->tgnewtable) &&
5731  transition_capture != NULL)
5732  new_shared.ats_table = transition_capture->tcs_private;
5733  else
5734  new_shared.ats_table = NULL;
5735  new_shared.ats_modifiedcols = modifiedCols;
5736 
5738  &new_event, &new_shared);
5739  }
5740 
5741  /*
5742  * Finally, spool any foreign tuple(s). The tuplestore squashes them to
5743  * minimal tuples, so this loses any system columns. The executor lost
5744  * those columns before us, for an unrelated reason, so this is fine.
5745  */
5746  if (fdw_tuplestore)
5747  {
5748  if (oldslot != NULL)
5749  tuplestore_puttupleslot(fdw_tuplestore, oldslot);
5750  if (newslot != NULL)
5751  tuplestore_puttupleslot(fdw_tuplestore, newslot);
5752  }
5753 }
#define TRIGGER_EVENT_ROW
Definition: trigger.h:97
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:708
TriggerEvent ats_event
Definition: trigger.c:3328
Relation ri_RelationDesc
Definition: execnodes.h:412
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:2870
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3318
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:107
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:3122
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3566
ItemPointerData ate_ctid2
Definition: trigger.c:3342
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:92
Oid tgfoid
Definition: reltrigger.h:28
TriggerFlags ate_flags
Definition: trigger.c:3340
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:5806
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:106
Oid tgoid
Definition: reltrigger.h:25
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
TupleConversionMap * ri_ChildToRootMap
Definition: execnodes.h:499
AfterTriggersQueryData * query_stack
Definition: trigger.c:3501
Tuplestorestate * old_tuplestore
Definition: trigger.c:3535
Form_pg_class rd_rel
Definition: rel.h:110
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:95
Bitmapset * ats_modifiedcols
Definition: trigger.c:3333
struct AfterTriggersTableData * tcs_private
Definition: trigger.h:80
#define ERROR
Definition: elog.h:43
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:3537
#define TupIsNull(slot)
Definition: tuptable.h:292
bool trig_insert_after_row
Definition: reltrigger.h:57
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3320
Tuplestorestate * new_tuplestore
Definition: trigger.c:3536
AttrMap * attrMap
Definition: tupconvert.h:27
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3321
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:424
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:112
List * es_tupleTable
Definition: execnodes.h:561
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:3332
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4920
#define RI_TRIGGER_FK
Definition: trigger.h:264
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:674
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:177
#define Assert(condition)
Definition: c.h:800
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3648
#define TRIGGER_EVENT_TRUNCATE
Definition: trigger.h:94
Oid tgconstrindid
Definition: reltrigger.h:34
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3319
CommandId ats_firing_id
Definition: trigger.c:3331
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:93
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:91
#define RI_TRIGGER_PK
Definition: trigger.h:263
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
#define elog(elevel,...)
Definition: elog.h:228
int i
ItemPointerData ate_ctid1
Definition: trigger.c:3341
#define RI_TRIGGER_NONE
Definition: trigger.h:265
char * tgoldtable
Definition: reltrigger.h:43
AfterTriggerEventList events
Definition: trigger.c:3512
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:115
static AfterTriggersData afterTriggers
Definition: trigger.c:3540
#define RelationGetRelid(relation)
Definition: rel.h:457
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:161
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:75

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5042 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().

5043 {
5044  int my_level = GetCurrentTransactionNestLevel();
5045 
5046  /* If we haven't already done so, initialize our state. */
5047  if (afterTriggers.state == NULL)
5049 
5050  /*
5051  * If in a subtransaction, and we didn't save the current state already,
5052  * save it so it can be restored if the subtransaction aborts.
5053  */
5054  if (my_level > 1 &&
5055  afterTriggers.trans_stack[my_level].state == NULL)
5056  {
5057  afterTriggers.trans_stack[my_level].state =
5059  }
5060 
5061  /*
5062  * Handle SET CONSTRAINTS ALL ...
5063  */
5064  if (stmt->constraints == NIL)
5065  {
5066  /*
5067  * Forget any previous SET CONSTRAINTS commands in this transaction.
5068  */
5070 
5071  /*
5072  * Set the per-transaction ALL state to known.
5073  */
5074  afterTriggers.state->all_isset = true;
5076  }
5077  else
5078  {
5079  Relation conrel;
5080  Relation tgrel;
5081  List *conoidlist = NIL;
5082  List *tgoidlist = NIL;
5083  ListCell *lc;
5084 
5085  /*
5086  * Handle SET CONSTRAINTS constraint-name [, ...]
5087  *
5088  * First, identify all the named constraints and make a list of their
5089  * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5090  * the same name within a schema, the specifications are not
5091  * necessarily unique. Our strategy is to target all matching
5092  * constraints within the first search-path schema that has any
5093  * matches, but disregard matches in schemas beyond the first match.
5094  * (This is a bit odd but it's the historical behavior.)
5095  *
5096  * A constraint in a partitioned table may have corresponding
5097  * constraints in the partitions. Grab those too.
5098  */
5099  conrel = table_open(ConstraintRelationId, AccessShareLock);
5100 
5101  foreach(lc, stmt->constraints)
5102  {
5103  RangeVar *constraint = lfirst(lc);
5104  bool found;
5105  List *namespacelist;
5106  ListCell *nslc;
5107 
5108  if (constraint->catalogname)
5109  {
5110  if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5111  ereport(ERROR,
5112  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5113  errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5114  constraint->catalogname, constraint->schemaname,
5115  constraint->relname)));
5116  }
5117 
5118  /*
5119  * If we're given the schema name with the constraint, look only
5120  * in that schema. If given a bare constraint name, use the
5121  * search path to find the first matching constraint.
5122  */
5123  if (constraint->schemaname)
5124  {
5125  Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5126  false);
5127 
5128  namespacelist = list_make1_oid(namespaceId);
5129  }
5130  else
5131  {
5132  namespacelist = fetch_search_path(true);
5133  }
5134 
5135  found = false;
5136  foreach(nslc, namespacelist)
5137  {
5138  Oid namespaceId = lfirst_oid(nslc);
5139  SysScanDesc conscan;
5140  ScanKeyData skey[2];
5141  HeapTuple tup;
5142 
5143  ScanKeyInit(&skey[0],
5144  Anum_pg_constraint_conname,
5145  BTEqualStrategyNumber, F_NAMEEQ,
5146  CStringGetDatum(constraint->relname));
5147  ScanKeyInit(&skey[1],
5148  Anum_pg_constraint_connamespace,
5149  BTEqualStrategyNumber, F_OIDEQ,
5150  ObjectIdGetDatum(namespaceId));
5151 
5152  conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5153  true, NULL, 2, skey);
5154 
5155  while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5156  {
5158 
5159  if (con->condeferrable)
5160  conoidlist = lappend_oid(conoidlist, con->oid);
5161  else if (stmt->deferred)
5162  ereport(ERROR,
5163  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5164  errmsg("constraint \"%s\" is not deferrable",
5165  constraint->relname)));
5166  found = true;
5167  }
5168 
5169  systable_endscan(conscan);
5170 
5171  /*
5172  * Once we've found a matching constraint we do not search
5173  * later parts of the search path.
5174  */
5175  if (found)
5176  break;
5177  }
5178 
5179  list_free(namespacelist);
5180 
5181  /*
5182  * Not found ?
5183  */
5184  if (!found)
5185  ereport(ERROR,
5186  (errcode(ERRCODE_UNDEFINED_OBJECT),
5187  errmsg("constraint \"%s\" does not exist",
5188  constraint->relname)));
5189  }
5190 
5191  /*
5192  * Scan for any possible descendants of the constraints. We append
5193  * whatever we find to the same list that we're scanning; this has the
5194  * effect that we create new scans for those, too, so if there are
5195  * further descendents, we'll also catch them.
5196  */
5197  foreach(lc, conoidlist)
5198  {
5199  Oid parent = lfirst_oid(lc);
5200  ScanKeyData key;
5201  SysScanDesc scan;
5202  HeapTuple tuple;
5203 
5204  ScanKeyInit(&key,
5205  Anum_pg_constraint_conparentid,
5206  BTEqualStrategyNumber, F_OIDEQ,
5207  ObjectIdGetDatum(parent));
5208 
5209  scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5210 
5211  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5212  {
5214 
5215  conoidlist = lappend_oid(conoidlist, con->oid);
5216  }
5217 
5218  systable_endscan(scan);
5219  }
5220 
5221  table_close(conrel, AccessShareLock);
5222 
5223  /*
5224  * Now, locate the trigger(s) implementing each of these constraints,
5225  * and make a list of their OIDs.
5226  */
5227  tgrel = table_open(TriggerRelationId, AccessShareLock);
5228 
5229  foreach(lc, conoidlist)
5230  {
5231  Oid conoid = lfirst_oid(lc);
5232  ScanKeyData skey;
5233  SysScanDesc tgscan;
5234  HeapTuple htup;
5235 
5236  ScanKeyInit(&skey,
5237  Anum_pg_trigger_tgconstraint,
5238  BTEqualStrategyNumber, F_OIDEQ,
5239  ObjectIdGetDatum(conoid));
5240 
5241  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5242  NULL, 1, &skey);
5243 
5244  while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5245  {
5246  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5247 
5248  /*
5249  * Silently skip triggers that are marked as non-deferrable in
5250  * pg_trigger. This is not an error condition, since a
5251  * deferrable RI constraint may have some non-deferrable
5252  * actions.
5253  */
5254  if (pg_trigger->tgdeferrable)
5255  tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid);
5256  }
5257 
5258  systable_endscan(tgscan);
5259  }
5260 
5261  table_close(tgrel, AccessShareLock);
5262 
5263  /*
5264  * Now we can set the trigger states of individual triggers for this
5265  * xact.
5266  */
5267  foreach(lc, tgoidlist)
5268  {
5269  Oid tgoid = lfirst_oid(lc);
5271  bool found = false;
5272  int i;
5273 
5274  for (i = 0; i < state->numstates; i++)
5275  {
5276  if (state->trigstates[i].sct_tgoid == tgoid)
5277  {
5278  state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5279  found = true;
5280  break;
5281  }
5282  }
5283  if (!found)
5284  {
5286  SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5287  }
5288  }
5289  }
5290 
5291  /*
5292  * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5293  * checks against that constraint must be made when the SET CONSTRAINTS
5294  * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5295  * apply retroactively. We've updated the constraints state, so scan the
5296  * list of previously deferred events to fire any that have now become
5297  * immediate.
5298  *
5299  * Obviously, if this was SET ... DEFERRED then it can't have converted
5300  * any unfired events to immediate, so we need do nothing in that case.
5301  */
5302  if (!stmt->deferred)
5303  {
5305  bool snapshot_set = false;
5306 
5307  while (afterTriggerMarkEvents(events, NULL, true))
5308  {
5309  CommandId firing_id = afterTriggers.firing_counter++;
5310 
5311  /*
5312  * Make sure a snapshot has been established in case trigger
5313  * functions need one. Note that we avoid setting a snapshot if
5314  * we don't find at least one trigger that has to be fired now.
5315  * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
5316  * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
5317  * at the start of a transaction it's not possible for any trigger
5318  * events to be queued yet.)
5319  */
5320  if (!snapshot_set)
5321  {
5323  snapshot_set = true;
5324  }
5325 
5326  /*
5327  * We can delete fired events if we are at top transaction level,
5328  * but we'd better not if inside a subtransaction, since the
5329  * subtransaction could later get rolled back.
5330  */
5331  if (afterTriggerInvokeEvents(events, firing_id, NULL,
5332  !IsSubTransaction()))
5333  break; /* all fired */
5334  }
5335 
5336  if (snapshot_set)
5338  }
5339 }
#define NIL
Definition: pg_list.h:65
uint32 CommandId
Definition: c.h:589
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2892
AfterTriggersTransData * trans_stack
Definition: trigger.c:3506
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:569
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
#define TriggerConstraintIndexId
Definition: pg_trigger.h:78
#define ConstraintNameNspIndexId
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3269
#define AccessShareLock
Definition: lockdefs.h:36
int errcode(int sqlerrcode)
Definition: elog.c:691
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition: trigger.c:5012
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4177
void PopActiveSnapshot(void)
Definition: snapmgr.c:759
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:357
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:357
char * schemaname
Definition: primnodes.h:67
char * relname
Definition: primnodes.h:68
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:476
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
SetConstraintState state
Definition: trigger.c:3496
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2155
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:680
#define CStringGetDatum(X)
Definition: postgres.h:578
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:4967
#define list_make1_oid(x1)
Definition: pg_list.h:228
Oid MyDatabaseId
Definition: globals.c:85
CommandId firing_counter
Definition: trigger.c:3495
#define ereport(elevel,...)
Definition: elog.h:155
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:857
FormData_pg_constraint * Form_pg_constraint
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:169
Definition: regguts.h:298
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:73
bool IsSubTransaction(void)
Definition: xact.c:4758
int errmsg(const char *fmt,...)
Definition: elog.c:902
void list_free(List *list)
Definition: list.c:1376
int i
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4093
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
AfterTriggerEventList events
Definition: trigger.c:3497
#define ConstraintParentIndexId
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:4382
static AfterTriggersData afterTriggers
Definition: trigger.c:3540
char * catalogname
Definition: primnodes.h:66
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define lfirst_oid(lc)
Definition: pg_list.h:171
SetConstraintState state
Definition: trigger.c:3520
static SetConstraintState SetConstraintStateCopy(SetConstraintState state)
Definition: trigger.c:4992

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 5760 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().

5761 {
5762  bool result;
5763  AfterTriggersTableData *table;
5764 
5765  /* Check state, like AfterTriggerSaveEvent. */
5766  if (afterTriggers.query_depth < 0)
5767  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
5768 
5769  /* Be sure we have enough space to record events at this query depth. */
5772 
5773  /*
5774  * We keep this state in the AfterTriggersTableData that also holds
5775  * transition tables for the relation + operation. In this way, if we are
5776  * forced to make a new set of transition tables because more tuples get
5777  * entered after we've already fired triggers, we will allow a new set of
5778  * statement triggers to get queued.
5779  */
5780  table = GetAfterTriggersTableData(relid, cmdType);
5781  result = table->before_trig_done;
5782  table->before_trig_done = true;
5783  return result;
5784 }
#define ERROR
Definition: elog.h:43
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4321
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4920
#define elog(elevel,...)
Definition: elog.h:228
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ cancel_prior_stmt_triggers()

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

Definition at line 5806 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().

5807 {
5808  AfterTriggersTableData *table;
5810 
5811  /*
5812  * We keep this state in the AfterTriggersTableData that also holds
5813  * transition tables for the relation + operation. In this way, if we are
5814  * forced to make a new set of transition tables because more tuples get
5815  * entered after we've already fired triggers, we will allow a new set of
5816  * statement triggers to get queued without canceling the old ones.
5817  */
5818  table = GetAfterTriggersTableData(relid, cmdType);
5819 
5820  if (table->after_trig_done)
5821  {
5822  /*
5823  * We want to start scanning from the tail location that existed just
5824  * before we inserted any statement triggers. But the events list
5825  * might've been entirely empty then, in which case scan from the
5826  * current head.
5827  */
5828  AfterTriggerEvent event;
5829  AfterTriggerEventChunk *chunk;
5830 
5831  if (table->after_trig_events.tail)
5832  {
5833  chunk = table->after_trig_events.tail;
5834  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
5835  }
5836  else
5837  {
5838  chunk = qs->events.head;
5839  event = NULL;
5840  }
5841 
5842  for_each_chunk_from(chunk)
5843  {
5844  if (event == NULL)
5845  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
5846  for_each_event_from(event, chunk)
5847  {
5848  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5849 
5850  /*
5851  * Exit loop when we reach events that aren't AS triggers for
5852  * the target relation.
5853  */
5854  if (evtshared->ats_relid != relid)
5855  goto done;
5856  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
5857  goto done;
5858  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
5859  goto done;
5860  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
5861  goto done;
5862  /* OK, mark it DONE */
5863  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
5864  event->ate_flags |= AFTER_TRIGGER_DONE;
5865  }
5866  /* signal we must reinitialize event ptr for next chunk */
5867  event = NULL;
5868  }
5869  }
5870 done:
5871 
5872  /* In any case, save current insertion point for next time */
5873  table->after_trig_done = true;
5874  table->after_trig_events = qs->events;
5875 }
TriggerEvent ats_event
Definition: trigger.c:3328
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3315
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3384
AfterTriggersQueryData * query_stack
Definition: trigger.c:3501
AfterTriggerEventChunk * tail
Definition: trigger.c:3390
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:130
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:95
#define TRIGGER_FIRED_FOR_STATEMENT(event)
Definition: trigger.h:124
#define GetTriggerSharedData(evt)
Definition: trigger.c:3365
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4321
AfterTriggerEventList after_trig_events
Definition: trigger.c:3534
#define for_each_chunk_from(cptr)
Definition: trigger.c:3406
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3408
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3336
AfterTriggerEventChunk * head
Definition: trigger.c:3389
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3316
AfterTriggerEventList events
Definition: trigger.c:3512
static AfterTriggersData afterTriggers
Definition: trigger.c:3540

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

Definition at line 1875 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().

1876 {
1877  TriggerDesc *newdesc;
1878  Trigger *trigger;
1879  int i;
1880 
1881  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
1882  return NULL;
1883 
1884  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
1885  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
1886 
1887  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
1888  memcpy(trigger, trigdesc->triggers,
1889  trigdesc->numtriggers * sizeof(Trigger));
1890  newdesc->triggers = trigger;
1891 
1892  for (i = 0; i < trigdesc->numtriggers; i++)
1893  {
1894  trigger->tgname = pstrdup(trigger->tgname);
1895  if (trigger->tgnattr > 0)
1896  {
1897  int16 *newattr;
1898 
1899  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
1900  memcpy(newattr, trigger->tgattr,
1901  trigger->tgnattr * sizeof(int16));
1902  trigger->tgattr = newattr;
1903  }
1904  if (trigger->tgnargs > 0)
1905  {
1906  char **newargs;
1907  int16 j;
1908 
1909  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
1910  for (j = 0; j < trigger->tgnargs; j++)
1911  newargs[j] = pstrdup(trigger->tgargs[j]);
1912  trigger->tgargs = newargs;
1913  }
1914  if (trigger->tgqual)
1915  trigger->tgqual = pstrdup(trigger->tgqual);
1916  if (trigger->tgoldtable)
1917  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
1918  if (trigger->tgnewtable)
1919  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
1920  trigger++;
1921  }
1922 
1923  return newdesc;
1924 }
signed short int16
Definition: c.h:416
char * pstrdup(const char *in)
Definition: mcxt.c:1187
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:950
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 160 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, deleteDependencyRecordsFor(), 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_copytuple(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, IndexGetRelation(), CreateTrigStmt::initdeferred, Int16GetDatum, InvalidAttrNumber, InvalidOid, InvokeObjectPostCreateHookArg, CreateTrigStmt::isconstraint, TriggerTransition::isNew, IsSystemRelation(), TriggerTransition::isTable, 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, 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::replace, 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().

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

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), CStringGetDatum, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, NameStr, ObjectIdGetDatum, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), superuser(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), and TriggerRelidNameIndexId.

Referenced by ATExecEnableDisableTrigger().

1546 {
1547  Relation tgrel;
1548  int nkeys;
1549  ScanKeyData keys[2];
1550  SysScanDesc tgscan;
1551  HeapTuple tuple;
1552  bool found;
1553  bool changed;
1554 
1555  /* Scan the relevant entries in pg_triggers */
1556  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1557 
1558  ScanKeyInit(&keys[0],
1559  Anum_pg_trigger_tgrelid,
1560  BTEqualStrategyNumber, F_OIDEQ,
1562  if (tgname)
1563  {
1564  ScanKeyInit(&keys[1],
1565  Anum_pg_trigger_tgname,
1566  BTEqualStrategyNumber, F_NAMEEQ,
1567  CStringGetDatum(tgname));
1568  nkeys = 2;
1569  }
1570  else
1571  nkeys = 1;
1572 
1573  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1574  NULL, nkeys, keys);
1575 
1576  found = changed = false;
1577 
1578  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1579  {
1580  Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
1581 
1582  if (oldtrig->tgisinternal)
1583  {
1584  /* system trigger ... ok to process? */
1585  if (skip_system)
1586  continue;
1587  if (!superuser())
1588  ereport(ERROR,
1589  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1590  errmsg("permission denied: \"%s\" is a system trigger",
1591  NameStr(oldtrig->tgname))));
1592  }
1593 
1594  found = true;
1595 
1596  if (oldtrig->tgenabled != fires_when)
1597  {
1598  /* need to change this one ... make a copy to scribble on */
1599  HeapTuple newtup = heap_copytuple(tuple);
1600  Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
1601 
1602  newtrig->tgenabled = fires_when;
1603 
1604  CatalogTupleUpdate(tgrel, &newtup->t_self, newtup);
1605 
1606  heap_freetuple(newtup);
1607 
1608  changed = true;
1609  }
1610 
1611  InvokeObjectPostAlterHook(TriggerRelationId,
1612  oldtrig->oid, 0);
1613  }
1614 
1615  systable_endscan(tgscan);
1616 
1617  table_close(tgrel, RowExclusiveLock);
1618 
1619  if (tgname && !found)
1620  ereport(ERROR,
1621  (errcode(ERRCODE_UNDEFINED_OBJECT),
1622  errmsg("trigger \"%s\" for table \"%s\" does not exist",
1623  tgname, RelationGetRelationName(rel))));
1624 
1625  /*
1626  * If we changed anything, broadcast a SI inval message to force each
1627  * backend (including our own!) to rebuild relation's relcache entry.
1628  * Otherwise they will fail to apply the change promptly.
1629  */
1630  if (changed)
1632 }
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:680
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:569
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
#define TriggerRelidNameIndexId
Definition: pg_trigger.h:80
int errcode(int sqlerrcode)
Definition: elog.c:691
bool superuser(void)
Definition: superuser.c:46
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:357
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:476
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
#define RowExclusiveLock
Definition: lockdefs.h:38
#define CStringGetDatum(X)
Definition: postgres.h:578
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:175
#define ereport(elevel,...)
Definition: elog.h:155
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:73
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1278
int errmsg(const char *fmt,...)
Definition: elog.c:902
#define NameStr(name)
Definition: c.h:677
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:457
#define BTEqualStrategyNumber
Definition: stratnum.h:31

◆ ExecARDeleteTriggers()

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

Definition at line 2552 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().

2556 {
2557  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2558  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2559 
2560  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2561  (transition_capture && transition_capture->tcs_delete_old_table))
2562  {
2563  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2564  if (fdw_trigtuple == NULL)
2565  GetTupleForTrigger(estate,
2566  NULL,
2567  relinfo,
2568  tupleid,
2570  slot,
2571  NULL);
2572  else
2573  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2574 
2576  true, slot, NULL, NIL, NULL,
2577  transition_capture);
2578  }
2579 }
#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:3006
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:92
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1165
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:424
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:5427
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:800

◆ ExecARInsertTriggers()

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

Definition at line 2322 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().

2325 {
2326  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2327 
2328  if ((trigdesc && trigdesc->trig_insert_after_row) ||
2329  (transition_capture && transition_capture->tcs_insert_new_table))
2331  true, NULL, slot,
2332  recheckIndexes, NULL,
2333  transition_capture);
2334 }
bool trig_insert_after_row
Definition: reltrigger.h:57
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:424
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, int event, bool row_trigger, TupleTableSlot *oldtup, TupleTableSlot *newtup