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 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 renametrig_internal (Relation tgrel, Relation targetrel, HeapTuple trigtup, const char *newname, const char *expected_name)
 
static void renametrig_partition (Relation tgrel, Oid partitionId, Oid parentTriggerOid, const char *newname, const char *expected_name)
 
static void SetTriggerFlags (TriggerDesc *trigdesc, Trigger *trigger)
 
static bool GetTupleForTrigger (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **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)
 
ObjectAddress CreateTriggerFiringOn (CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
 
void 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 TupleTableSlotGetAfterTriggersStoreSlot (AfterTriggersTableData *table, TupleDesc tupdesc)
 
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 3459 of file trigger.c.

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3461 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 3534 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 3545 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:3523
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3497
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3475

Definition at line 3536 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:3497
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3475

Definition at line 3547 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

◆ 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:3461
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3459
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3460

Definition at line 3497 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3475 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3463 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3628 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3630 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3629 of file trigger.c.

◆ SetConstraintState

Definition at line 3411 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3390 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3451 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

3791 {
3792  Size eventsize = SizeofTriggerEvent(event);
3793  Size needed = eventsize + sizeof(AfterTriggerSharedData);
3794  AfterTriggerEventChunk *chunk;
3795  AfterTriggerShared newshared;
3796  AfterTriggerEvent newevent;
3797 
3798  /*
3799  * If empty list or not enough room in the tail chunk, make a new chunk.
3800  * We assume here that a new shared record will always be needed.
3801  */
3802  chunk = events->tail;
3803  if (chunk == NULL ||
3804  chunk->endfree - chunk->freeptr < needed)
3805  {
3806  Size chunksize;
3807 
3808  /* Create event context if we didn't already */
3809  if (afterTriggers.event_cxt == NULL)
3812  "AfterTriggerEvents",
3814 
3815  /*
3816  * Chunk size starts at 1KB and is allowed to increase up to 1MB.
3817  * These numbers are fairly arbitrary, though there is a hard limit at
3818  * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
3819  * shared records using the available space in ate_flags. Another
3820  * constraint is that if the chunk size gets too huge, the search loop
3821  * below would get slow given a (not too common) usage pattern with
3822  * many distinct event types in a chunk. Therefore, we double the
3823  * preceding chunk size only if there weren't too many shared records
3824  * in the preceding chunk; otherwise we halve it. This gives us some
3825  * ability to adapt to the actual usage pattern of the current query
3826  * while still having large chunk sizes in typical usage. All chunk
3827  * sizes used should be MAXALIGN multiples, to ensure that the shared
3828  * records will be aligned safely.
3829  */
3830 #define MIN_CHUNK_SIZE 1024
3831 #define MAX_CHUNK_SIZE (1024*1024)
3832 
3833 #if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
3834 #error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
3835 #endif
3836 
3837  if (chunk == NULL)
3838  chunksize = MIN_CHUNK_SIZE;
3839  else
3840  {
3841  /* preceding chunk size... */
3842  chunksize = chunk->endptr - (char *) chunk;
3843  /* check number of shared records in preceding chunk */
3844  if ((chunk->endptr - chunk->endfree) <=
3845  (100 * sizeof(AfterTriggerSharedData)))
3846  chunksize *= 2; /* okay, double it */
3847  else
3848  chunksize /= 2; /* too many shared records */
3849  chunksize = Min(chunksize, MAX_CHUNK_SIZE);
3850  }
3851  chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize);
3852  chunk->next = NULL;
3853  chunk->freeptr = CHUNK_DATA_START(chunk);
3854  chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
3855  Assert(chunk->endfree - chunk->freeptr >= needed);
3856 
3857  if (events->head == NULL)
3858  events->head = chunk;
3859  else
3860  events->tail->next = chunk;
3861  events->tail = chunk;
3862  /* events->tailfree is now out of sync, but we'll fix it below */
3863  }
3864 
3865  /*
3866  * Try to locate a matching shared-data record already in the chunk. If
3867  * none, make a new one.
3868  */
3869  for (newshared = ((AfterTriggerShared) chunk->endptr) - 1;
3870  (char *) newshared >= chunk->endfree;
3871  newshared--)
3872  {
3873  if (newshared->ats_tgoid == evtshared->ats_tgoid &&
3874  newshared->ats_relid == evtshared->ats_relid &&
3875  newshared->ats_event == evtshared->ats_event &&
3876  newshared->ats_table == evtshared->ats_table &&
3877  newshared->ats_firing_id == 0)
3878  break;
3879  }
3880  if ((char *) newshared < chunk->endfree)
3881  {
3882  *newshared = *evtshared;
3883  newshared->ats_firing_id = 0; /* just to be sure */
3884  chunk->endfree = (char *) newshared;
3885  }
3886 
3887  /* Insert the data */
3888  newevent = (AfterTriggerEvent) chunk->freeptr;
3889  memcpy(newevent, event, eventsize);
3890  /* ... and link the new event to its shared record */
3891  newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
3892  newevent->ate_flags |= (char *) newshared - (char *) newevent;
3893 
3894  chunk->freeptr += eventsize;
3895  events->tailfree = chunk->freeptr;
3896 }
TriggerEvent ats_event
Definition: trigger.c:3467
#define AllocSetContextCreate
Definition: memutils.h:173
MemoryContext TopTransactionContext
Definition: mcxt.c:53
#define MIN_CHUNK_SIZE
TriggerFlags ate_flags
Definition: trigger.c:3479
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3523
struct AfterTriggerSharedData AfterTriggerSharedData
#define Min(x, y)
Definition: c.h:986
AfterTriggerEventChunk * tail
Definition: trigger.c:3529
struct AfterTriggerEventChunk * next
Definition: trigger.c:3516
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:195
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3497
#define AFTER_TRIGGER_OFFSET
Definition: trigger.c:3453
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3471
#define Assert(condition)
Definition: c.h:804
size_t Size
Definition: c.h:540
CommandId ats_firing_id
Definition: trigger.c:3470
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3475
#define MAX_CHUNK_SIZE
AfterTriggerEventChunk * head
Definition: trigger.c:3528
MemoryContext event_cxt
Definition: trigger.c:3637
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4670 of file trigger.c.

References AfterTriggersData::query_depth.

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

4671 {
4672  /* Increase the query stack depth */
4674 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

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

4941 {
4942  int my_level = GetCurrentTransactionNestLevel();
4943 
4944  /*
4945  * Allocate more space in the trans_stack if needed. (Note: because the
4946  * minimum nest level of a subtransaction is 2, we waste the first couple
4947  * entries of the array; not worth the notational effort to avoid it.)
4948  */
4949  while (my_level >= afterTriggers.maxtransdepth)
4950  {
4951  if (afterTriggers.maxtransdepth == 0)
4952  {
4953  /* Arbitrarily initialize for max of 8 subtransaction levels */
4956  8 * sizeof(AfterTriggersTransData));
4958  }
4959  else
4960  {
4961  /* repalloc will keep the stack in the same context */
4962  int new_alloc = afterTriggers.maxtransdepth * 2;
4963 
4966  new_alloc * sizeof(AfterTriggersTransData));
4967  afterTriggers.maxtransdepth = new_alloc;
4968  }
4969  }
4970 
4971  /*
4972  * Push the current information into the stack. The SET CONSTRAINTS state
4973  * is not saved until/unless changed. Likewise, we don't make a
4974  * per-subtransaction event context until needed.
4975  */
4976  afterTriggers.trans_stack[my_level].state = NULL;
4980 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3645
MemoryContext TopTransactionContext
Definition: mcxt.c:53
AfterTriggerEventList events
Definition: trigger.c:3660
CommandId firing_counter
Definition: trigger.c:3662
CommandId firing_counter
Definition: trigger.c:3634
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:858
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1182
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
AfterTriggerEventList events
Definition: trigger.c:3636
static AfterTriggersData afterTriggers
Definition: trigger.c:3679
SetConstraintState state
Definition: trigger.c:3659

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

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

4639 {
4640  /*
4641  * Initialize after-trigger state structure to empty
4642  */
4643  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4645 
4646  /*
4647  * Verify that there is no leftover state remaining. If these assertions
4648  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4649  * up properly.
4650  */
4651  Assert(afterTriggers.state == NULL);
4652  Assert(afterTriggers.query_stack == NULL);
4654  Assert(afterTriggers.event_cxt == NULL);
4655  Assert(afterTriggers.events.head == NULL);
4656  Assert(afterTriggers.trans_stack == NULL);
4658 }
uint32 CommandId
Definition: c.h:601
AfterTriggersTransData * trans_stack
Definition: trigger.c:3645
AfterTriggersQueryData * query_stack
Definition: trigger.c:3640
SetConstraintState state
Definition: trigger.c:3635
CommandId firing_counter
Definition: trigger.c:3634
#define Assert(condition)
Definition: c.h:804
AfterTriggerEventChunk * head
Definition: trigger.c:3528
MemoryContext event_cxt
Definition: trigger.c:3637
AfterTriggerEventList events
Definition: trigger.c:3636
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

3744 {
3745  Oid tgoid = evtshared->ats_tgoid;
3747  int i;
3748 
3749  /*
3750  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3751  * constraints declared NOT DEFERRABLE), the state is always false.
3752  */
3753  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3754  return false;
3755 
3756  /*
3757  * If constraint state exists, SET CONSTRAINTS might have been executed
3758  * either for this trigger or for all triggers.
3759  */
3760  if (state != NULL)
3761  {
3762  /* Check for SET CONSTRAINTS for this specific trigger. */
3763  for (i = 0; i < state->numstates; i++)
3764  {
3765  if (state->trigstates[i].sct_tgoid == tgoid)
3766  return state->trigstates[i].sct_tgisdeferred;
3767  }
3768 
3769  /* Check for SET CONSTRAINTS ALL. */
3770  if (state->all_isset)
3771  return state->all_isdeferred;
3772  }
3773 
3774  /*
3775  * Otherwise return the default state for the trigger.
3776  */
3777  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3778 }
TriggerEvent ats_event
Definition: trigger.c:3467
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:107
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:106
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3408
unsigned int Oid
Definition: postgres_ext.h:31
SetConstraintState state
Definition: trigger.c:3635
Definition: regguts.h:317
int i
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

3967 {
3968  AfterTriggerEventChunk *target = qs->events.head;
3969  ListCell *lc;
3970 
3971  Assert(target && target->next);
3972 
3973  /*
3974  * First, update any pointers in the per-table data, so that they won't be
3975  * dangling. Resetting obsoleted pointers to NULL will make
3976  * cancel_prior_stmt_triggers start from the list head, which is fine.
3977  */
3978  foreach(lc, qs->tables)
3979  {
3981 
3982  if (table->after_trig_done &&
3983  table->after_trig_events.tail == target)
3984  {
3985  table->after_trig_events.head = NULL;
3986  table->after_trig_events.tail = NULL;
3987  table->after_trig_events.tailfree = NULL;
3988  }
3989  }
3990 
3991  /* Now we can flush the head chunk */
3992  qs->events.head = target->next;
3993  pfree(target);
3994 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3529
struct AfterTriggerEventChunk * next
Definition: trigger.c:3516
void pfree(void *pointer)
Definition: mcxt.c:1169
AfterTriggerEventList after_trig_events
Definition: trigger.c:3673
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
AfterTriggerEventChunk * head
Definition: trigger.c:3528
AfterTriggerEventList events
Definition: trigger.c:3651

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 4690 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 CopyFrom(), ExecuteTruncateGuts(), finish_edata(), and standard_ExecutorFinish().

4691 {
4693 
4694  /* Must be inside a query, too */
4696 
4697  /*
4698  * If we never even got as far as initializing the event stack, there
4699  * certainly won't be any events, so exit quickly.
4700  */
4702  {
4704  return;
4705  }
4706 
4707  /*
4708  * Process all immediate-mode triggers queued by the query, and move the
4709  * deferred ones to the main list of deferred events.
4710  *
4711  * Notice that we decide which ones will be fired, and put the deferred
4712  * ones on the main list, before anything is actually fired. This ensures
4713  * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
4714  * IMMEDIATE: all events we have decided to defer will be available for it
4715  * to fire.
4716  *
4717  * We loop in case a trigger queues more events at the same query level.
4718  * Ordinary trigger functions, including all PL/pgSQL trigger functions,
4719  * will instead fire any triggers in a dedicated query level. Foreign key
4720  * enforcement triggers do add to the current query level, thanks to their
4721  * passing fire_triggers = false to SPI_execute_snapshot(). Other
4722  * C-language triggers might do likewise.
4723  *
4724  * If we find no firable events, we don't have to increment
4725  * firing_counter.
4726  */
4728 
4729  for (;;)
4730  {
4732  {
4733  CommandId firing_id = afterTriggers.firing_counter++;
4734  AfterTriggerEventChunk *oldtail = qs->events.tail;
4735 
4736  if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
4737  break; /* all fired */
4738 
4739  /*
4740  * Firing a trigger could result in query_stack being repalloc'd,
4741  * so we must recalculate qs after each afterTriggerInvokeEvents
4742  * call. Furthermore, it's unsafe to pass delete_ok = true here,
4743  * because that could cause afterTriggerInvokeEvents to try to
4744  * access qs->events after the stack has been repalloc'd.
4745  */
4747 
4748  /*
4749  * We'll need to scan the events list again. To reduce the cost
4750  * of doing so, get rid of completely-fired chunks. We know that
4751  * all events were marked IN_PROGRESS or DONE at the conclusion of
4752  * afterTriggerMarkEvents, so any still-interesting events must
4753  * have been added after that, and so must be in the chunk that
4754  * was then the tail chunk, or in later chunks. So, zap all
4755  * chunks before oldtail. This is approximately the same set of
4756  * events we would have gotten rid of by passing delete_ok = true.
4757  */
4758  Assert(oldtail != NULL);
4759  while (qs->events.head != oldtail)
4761  }
4762  else
4763  break;
4764  }
4765 
4766  /* Release query-level-local storage, including tuplestores if any */
4768 
4770 }
uint32 CommandId
Definition: c.h:601
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition: trigger.c:3966
AfterTriggersQueryData * query_stack
Definition: trigger.c:3640
AfterTriggerEventChunk * tail
Definition: trigger.c:3529
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4318
CommandId firing_counter
Definition: trigger.c:3634
#define Assert(condition)
Definition: c.h:804
AfterTriggerEventChunk * head
Definition: trigger.c:3528
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4234
AfterTriggerEventList events
Definition: trigger.c:3636
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4781
AfterTriggerEventList events
Definition: trigger.c:3651
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

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

4989 {
4990  int my_level = GetCurrentTransactionNestLevel();
4992  AfterTriggerEvent event;
4993  AfterTriggerEventChunk *chunk;
4994  CommandId subxact_firing_id;
4995 
4996  /*
4997  * Pop the prior state if needed.
4998  */
4999  if (isCommit)
5000  {
5001  Assert(my_level < afterTriggers.maxtransdepth);
5002  /* If we saved a prior state, we don't need it anymore */
5003  state = afterTriggers.trans_stack[my_level].state;
5004  if (state != NULL)
5005  pfree(state);
5006  /* this avoids double pfree if error later: */
5007  afterTriggers.trans_stack[my_level].state = NULL;
5010  }
5011  else
5012  {
5013  /*
5014  * Aborting. It is possible subxact start failed before calling
5015  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5016  * trans_stack levels that aren't there.
5017  */
5018  if (my_level >= afterTriggers.maxtransdepth)
5019  return;
5020 
5021  /*
5022  * Release query-level storage for queries being aborted, and restore
5023  * query_depth to its pre-subxact value. This assumes that a
5024  * subtransaction will not add events to query levels started in a
5025  * earlier transaction state.
5026  */
5028  {
5032  }
5035 
5036  /*
5037  * Restore the global deferred-event list to its former length,
5038  * discarding any events queued by the subxact.
5039  */
5041  &afterTriggers.trans_stack[my_level].events);
5042 
5043  /*
5044  * Restore the trigger state. If the saved state is NULL, then this
5045  * subxact didn't save it, so it doesn't need restoring.
5046  */
5047  state = afterTriggers.trans_stack[my_level].state;
5048  if (state != NULL)
5049  {
5051  afterTriggers.state = state;
5052  }
5053  /* this avoids double pfree if error later: */
5054  afterTriggers.trans_stack[my_level].state = NULL;
5055 
5056  /*
5057  * Scan for any remaining deferred events that were marked DONE or IN
5058  * PROGRESS by this subxact or a child, and un-mark them. We can
5059  * recognize such events because they have a firing ID greater than or
5060  * equal to the firing_counter value we saved at subtransaction start.
5061  * (This essentially assumes that the current subxact includes all
5062  * subxacts started after it.)
5063  */
5064  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5066  {
5067  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5068 
5069  if (event->ate_flags &
5071  {
5072  if (evtshared->ats_firing_id >= subxact_firing_id)
5073  event->ate_flags &=
5075  }
5076  }
5077  }
5078 }
uint32 CommandId
Definition: c.h:601
AfterTriggersTransData * trans_stack
Definition: trigger.c:3645
TriggerFlags ate_flags
Definition: trigger.c:3479
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3454
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3541
AfterTriggerEventList events
Definition: trigger.c:3660
AfterTriggersQueryData * query_stack
Definition: trigger.c:3640
CommandId firing_counter
Definition: trigger.c:3662
#define GetTriggerSharedData(evt)
Definition: trigger.c:3504
void pfree(void *pointer)
Definition: mcxt.c:1169
SetConstraintState state
Definition: trigger.c:3635
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:858
#define Assert(condition)
Definition: c.h:804
Definition: regguts.h:317
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:3926
CommandId ats_firing_id
Definition: trigger.c:3470
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3455
AfterTriggerEventList events
Definition: trigger.c:3636
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4781
static AfterTriggersData afterTriggers
Definition: trigger.c:3679
SetConstraintState state
Definition: trigger.c:3659

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

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

4893 {
4894  /*
4895  * Forget the pending-events list.
4896  *
4897  * Since all the info is in TopTransactionContext or children thereof, we
4898  * don't really need to do anything to reclaim memory. However, the
4899  * pending-events list could be large, and so it's useful to discard it as
4900  * soon as possible --- especially if we are aborting because we ran out
4901  * of memory for the list!
4902  */
4904  {
4906  afterTriggers.event_cxt = NULL;
4907  afterTriggers.events.head = NULL;
4908  afterTriggers.events.tail = NULL;
4909  afterTriggers.events.tailfree = NULL;
4910  }
4911 
4912  /*
4913  * Forget any subtransaction state as well. Since this can't be very
4914  * large, we let the eventual reset of TopTransactionContext free the
4915  * memory instead of doing it here.
4916  */
4917  afterTriggers.trans_stack = NULL;
4919 
4920 
4921  /*
4922  * Forget the query stack and constraint-related state information. As
4923  * with the subtransaction state information, we don't bother freeing the
4924  * memory here.
4925  */
4926  afterTriggers.query_stack = NULL;
4928  afterTriggers.state = NULL;
4929 
4930  /* No more afterTriggers manipulation until next transaction starts. */
4932 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
AfterTriggersTransData * trans_stack
Definition: trigger.c:3645
AfterTriggersQueryData * query_stack
Definition: trigger.c:3640
AfterTriggerEventChunk * tail
Definition: trigger.c:3529
SetConstraintState state
Definition: trigger.c:3635
AfterTriggerEventChunk * head
Definition: trigger.c:3528
MemoryContext event_cxt
Definition: trigger.c:3637
AfterTriggerEventList events
Definition: trigger.c:3636
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

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

5091 {
5092  int init_depth = afterTriggers.maxquerydepth;
5093 
5095 
5096  if (afterTriggers.maxquerydepth == 0)
5097  {
5098  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5099 
5102  new_alloc * sizeof(AfterTriggersQueryData));
5103  afterTriggers.maxquerydepth = new_alloc;
5104  }
5105  else
5106  {
5107  /* repalloc will keep the stack in the same context */
5108  int old_alloc = afterTriggers.maxquerydepth;
5109  int new_alloc = Max(afterTriggers.query_depth + 1,
5110  old_alloc * 2);
5111 
5114  new_alloc * sizeof(AfterTriggersQueryData));
5115  afterTriggers.maxquerydepth = new_alloc;
5116  }
5117 
5118  /* Initialize new array entries to empty */
5119  while (init_depth < afterTriggers.maxquerydepth)
5120  {
5122 
5123  qs->events.head = NULL;
5124  qs->events.tail = NULL;
5125  qs->events.tailfree = NULL;
5126  qs->fdw_tuplestore = NULL;
5127  qs->tables = NIL;
5128 
5129  ++init_depth;
5130  }
5131 }
#define NIL
Definition: pg_list.h:65
MemoryContext TopTransactionContext
Definition: mcxt.c:53
AfterTriggersQueryData * query_stack
Definition: trigger.c:3640
AfterTriggerEventChunk * tail
Definition: trigger.c:3529
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3652
#define Max(x, y)
Definition: c.h:980
#define Assert(condition)
Definition: c.h:804
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1182
AfterTriggerEventChunk * head
Definition: trigger.c:3528
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
AfterTriggerEventList events
Definition: trigger.c:3651
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

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

4028 {
4029  Relation rel = relInfo->ri_RelationDesc;
4030  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4031  Oid tgoid = evtshared->ats_tgoid;
4032  TriggerData LocTriggerData = {0};
4033  HeapTuple rettuple;
4034  int tgindx;
4035  bool should_free_trig = false;
4036  bool should_free_new = false;
4037 
4038  /*
4039  * Locate trigger in trigdesc.
4040  */
4041  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4042  {
4043  if (trigdesc->triggers[tgindx].tgoid == tgoid)
4044  {
4045  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4046  break;
4047  }
4048  }
4049  if (LocTriggerData.tg_trigger == NULL)
4050  elog(ERROR, "could not find trigger %u", tgoid);
4051 
4052  /*
4053  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4054  * to include time spent re-fetching tuples in the trigger cost.
4055  */
4056  if (instr)
4057  InstrStartNode(instr + tgindx);
4058 
4059  /*
4060  * Fetch the required tuple(s).
4061  */
4062  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4063  {
4065  {
4066  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4067 
4068  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4069  trig_tuple_slot1))
4070  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4071 
4072  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4074  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4075  trig_tuple_slot2))
4076  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4077  }
4078  /* fall through */
4080 
4081  /*
4082  * Store tuple in the slot so that tg_trigtuple does not reference
4083  * tuplestore memory. (It is formally possible for the trigger
4084  * function to queue trigger events that add to the same
4085  * tuplestore, which can push other tuples out of memory.) The
4086  * distinction is academic, because we start with a minimal tuple
4087  * that is stored as a heap tuple, constructed in different memory
4088  * context, in the slot anyway.
4089  */
4090  LocTriggerData.tg_trigslot = trig_tuple_slot1;
4091  LocTriggerData.tg_trigtuple =
4092  ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
4093 
4094  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4096  {
4097  LocTriggerData.tg_newslot = trig_tuple_slot2;
4098  LocTriggerData.tg_newtuple =
4099  ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new);
4100  }
4101  else
4102  {
4103  LocTriggerData.tg_newtuple = NULL;
4104  }
4105  break;
4106 
4107  default:
4108  if (ItemPointerIsValid(&(event->ate_ctid1)))
4109  {
4110  LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4111 
4112  if (!table_tuple_fetch_row_version(rel, &(event->ate_ctid1),
4113  SnapshotAny,
4114  LocTriggerData.tg_trigslot))
4115  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4116  LocTriggerData.tg_trigtuple =
4117  ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, &should_free_trig);
4118  }
4119  else
4120  {
4121  LocTriggerData.tg_trigtuple = NULL;
4122  }
4123 
4124  /* don't touch ctid2 if not there */
4125  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4127  ItemPointerIsValid(&(event->ate_ctid2)))
4128  {
4129  LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4130 
4131  if (!table_tuple_fetch_row_version(rel, &(event->ate_ctid2),
4132  SnapshotAny,
4133  LocTriggerData.tg_newslot))
4134  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4135  LocTriggerData.tg_newtuple =
4136  ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, &should_free_new);
4137  }
4138  else
4139  {
4140  LocTriggerData.tg_newtuple = NULL;
4141  }
4142  }
4143 
4144  /*
4145  * Set up the tuplestore information to let the trigger have access to
4146  * transition tables. When we first make a transition table available to
4147  * a trigger, mark it "closed" so that it cannot change anymore. If any
4148  * additional events of the same type get queued in the current trigger
4149  * query level, they'll go into new transition tables.
4150  */
4151  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4152  if (evtshared->ats_table)
4153  {
4154  if (LocTriggerData.tg_trigger->tgoldtable)
4155  {
4156  LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4157  evtshared->ats_table->closed = true;
4158  }
4159 
4160  if (LocTriggerData.tg_trigger->tgnewtable)
4161  {
4162  LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4163  evtshared->ats_table->closed = true;
4164  }
4165  }
4166 
4167  /*
4168  * Setup the remaining trigger information
4169  */
4170  LocTriggerData.type = T_TriggerData;
4171  LocTriggerData.tg_event =
4173  LocTriggerData.tg_relation = rel;
4174  if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4175  LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4176 
4177  MemoryContextReset(per_tuple_context);
4178 
4179  /*
4180  * Call the trigger and throw away any possibly returned updated tuple.
4181  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4182  */
4183  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4184  tgindx,
4185  finfo,
4186  NULL,
4187  per_tuple_context);
4188  if (rettuple != NULL &&
4189  rettuple != LocTriggerData.tg_trigtuple &&
4190  rettuple != LocTriggerData.tg_newtuple)
4191  heap_freetuple(rettuple);
4192 
4193  /*
4194  * Release resources
4195  */
4196  if (should_free_trig)
4197  heap_freetuple(LocTriggerData.tg_trigtuple);
4198  if (should_free_new)
4199  heap_freetuple(LocTriggerData.tg_newtuple);
4200 
4201  /* don't clear slots' contents if foreign table */
4202  if (trig_tuple_slot1 == NULL)
4203  {
4204  if (LocTriggerData.tg_trigslot)
4205  ExecClearTuple(LocTriggerData.tg_trigslot);
4206  if (LocTriggerData.tg_newslot)
4207  ExecClearTuple(LocTriggerData.tg_newslot);
4208  }
4209 
4210  /*
4211  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4212  * one "tuple returned" (really the number of firings).
4213  */
4214  if (instr)
4215  InstrStopNode(instr + tgindx, 1);
4216 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define TRIGGER_EVENT_ROW
Definition: trigger.h:97
TriggerEvent ats_event
Definition: trigger.c:3467
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:84
TupleTableSlot * tg_trigslot
Definition: trigger.h:38
Relation ri_RelationDesc
Definition: execnodes.h:412
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3457
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3707
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
ItemPointerData ate_ctid2
Definition: trigger.c:3481
TriggerFlags ate_flags
Definition: trigger.c:3479
const Bitmapset * tg_updatedcols
Definition: trigger.h:42
Oid tgoid
Definition: reltrigger.h:25
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3461
Tuplestorestate * old_tuplestore
Definition: trigger.c:3674
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1166
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
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:1188
HeapTuple tg_trigtuple
Definition: trigger.h:35
#define GetTriggerSharedData(evt)
Definition: trigger.c:3504
Bitmapset * ats_modifiedcols
Definition: trigger.c:3472
#define ERROR
Definition: elog.h:46
void InstrStartNode(Instrumentation *instr)
Definition: instrument.c:68
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:3675
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3460
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1644
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1257
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:3471
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:3458
#define SnapshotAny
Definition: snapmgr.h:67
#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:232
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:2231
ItemPointerData ate_ctid1
Definition: trigger.c:3480
char * tgoldtable
Definition: reltrigger.h:43
Relation tg_relation
Definition: trigger.h:34

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

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

4837 {
4838  AfterTriggerEventList *events;
4839  bool snap_pushed = false;
4840 
4841  /* Must not be inside a query */
4843 
4844  /*
4845  * If there are any triggers to fire, make sure we have set a snapshot for
4846  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4847  * can't assume ActiveSnapshot is valid on entry.)
4848  */
4849  events = &afterTriggers.events;
4850  if (events->head != NULL)
4851  {
4853  snap_pushed = true;
4854  }
4855 
4856  /*
4857  * Run all the remaining triggers. Loop until they are all gone, in case
4858  * some trigger queues more for us to do.
4859  */
4860  while (afterTriggerMarkEvents(events, NULL, false))
4861  {
4862  CommandId firing_id = afterTriggers.firing_counter++;
4863 
4864  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4865  break; /* all fired */
4866  }
4867 
4868  /*
4869  * We don't bother freeing the event list, since it will go away anyway
4870  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4871  */
4872 
4873  if (snap_pushed)
4875 }
uint32 CommandId
Definition: c.h:601
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4318
void PopActiveSnapshot(void)
Definition: snapmgr.c:774
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:680
CommandId firing_counter
Definition: trigger.c:3634
#define Assert(condition)
Definition: c.h:804
AfterTriggerEventChunk * head
Definition: trigger.c:3528
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4234
AfterTriggerEventList events
Definition: trigger.c:3636
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 3905 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

3906 {
3907  AfterTriggerEventChunk *chunk;
3908 
3909  while ((chunk = events->head) != NULL)
3910  {
3911  events->head = chunk->next;
3912  pfree(chunk);
3913  }
3914  events->tail = NULL;
3915  events->tailfree = NULL;
3916 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3529
struct AfterTriggerEventChunk * next
Definition: trigger.c:3516
void pfree(void *pointer)
Definition: mcxt.c:1169
AfterTriggerEventChunk * head
Definition: trigger.c:3528

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 4781 of file trigger.c.

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

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

4782 {
4783  Tuplestorestate *ts;
4784  List *tables;
4785  ListCell *lc;
4786 
4787  /* Drop the trigger events */
4789 
4790  /* Drop FDW tuplestore if any */
4791  ts = qs->fdw_tuplestore;
4792  qs->fdw_tuplestore = NULL;
4793  if (ts)
4794  tuplestore_end(ts);
4795 
4796  /* Release per-table subsidiary storage */
4797  tables = qs->tables;
4798  foreach(lc, tables)
4799  {
4801 
4802  ts = table->old_tuplestore;
4803  table->old_tuplestore = NULL;
4804  if (ts)
4805  tuplestore_end(ts);
4806  ts = table->new_tuplestore;
4807  table->new_tuplestore = NULL;
4808  if (ts)
4809  tuplestore_end(ts);
4810  if (table->storeslot)
4812  }
4813 
4814  /*
4815  * Now free the AfterTriggersTableData structs and list cells. Reset list
4816  * pointer first; if list_free_deep somehow gets an error, better to leak
4817  * that storage than have an infinite loop.
4818  */
4819  qs->tables = NIL;
4820  list_free_deep(tables);
4821 }
#define NIL
Definition: pg_list.h:65
Tuplestorestate * old_tuplestore
Definition: trigger.c:3674
void list_free_deep(List *list)
Definition: list.c:1405
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3905
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
TupleTableSlot * storeslot
Definition: trigger.c:3676
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3652
Tuplestorestate * new_tuplestore
Definition: trigger.c:3675
#define lfirst(lc)
Definition: pg_list.h:169
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3651
Definition: pg_list.h:50

◆ afterTriggerInvokeEvents()

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

Definition at line 4318 of file trigger.c.

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AfterTriggerExecute(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, 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, RelationHasReferenceCountZero, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TTSOpsMinimalTuple.

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

4322 {
4323  bool all_fired = true;
4324  AfterTriggerEventChunk *chunk;
4325  MemoryContext per_tuple_context;
4326  bool local_estate = false;
4327  ResultRelInfo *rInfo = NULL;
4328  Relation rel = NULL;
4329  TriggerDesc *trigdesc = NULL;
4330  FmgrInfo *finfo = NULL;
4331  Instrumentation *instr = NULL;
4332  TupleTableSlot *slot1 = NULL,
4333  *slot2 = NULL;
4334 
4335  /* Make a local EState if need be */
4336  if (estate == NULL)
4337  {
4338  estate = CreateExecutorState();
4339  local_estate = true;
4340  }
4341 
4342  /* Make a per-tuple memory context for trigger function calls */
4343  per_tuple_context =
4345  "AfterTriggerTupleContext",
4347 
4348  for_each_chunk(chunk, *events)
4349  {
4350  AfterTriggerEvent event;
4351  bool all_fired_in_chunk = true;
4352 
4353  for_each_event(event, chunk)
4354  {
4355  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4356 
4357  /*
4358  * Is it one for me to fire?
4359  */
4360  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4361  evtshared->ats_firing_id == firing_id)
4362  {
4363  /*
4364  * So let's fire it... but first, find the correct relation if
4365  * this is not the same relation as before.
4366  */
4367  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4368  {
4369  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4370  rel = rInfo->ri_RelationDesc;
4371  /* Catch calls with insufficient relcache refcounting */
4373  trigdesc = rInfo->ri_TrigDesc;
4374  finfo = rInfo->ri_TrigFunctions;
4375  instr = rInfo->ri_TrigInstrument;
4376  if (slot1 != NULL)
4377  {
4380  slot1 = slot2 = NULL;
4381  }
4382  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4383  {
4384  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4386  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4388  }
4389  if (trigdesc == NULL) /* should not happen */
4390  elog(ERROR, "relation %u has no triggers",
4391  evtshared->ats_relid);
4392  }
4393 
4394  /*
4395  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4396  * still set, so recursive examinations of the event list
4397  * won't try to re-fire it.
4398  */
4399  AfterTriggerExecute(estate, event, rInfo, trigdesc, finfo, instr,
4400  per_tuple_context, slot1, slot2);
4401 
4402  /*
4403  * Mark the event as done.
4404  */
4405  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4406  event->ate_flags |= AFTER_TRIGGER_DONE;
4407  }
4408  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4409  {
4410  /* something remains to be done */
4411  all_fired = all_fired_in_chunk = false;
4412  }
4413  }
4414 
4415  /* Clear the chunk if delete_ok and nothing left of interest */
4416  if (delete_ok && all_fired_in_chunk)
4417  {
4418  chunk->freeptr = CHUNK_DATA_START(chunk);
4419  chunk->endfree = chunk->endptr;
4420 
4421  /*
4422  * If it's last chunk, must sync event list's tailfree too. Note
4423  * that delete_ok must NOT be passed as true if there could be
4424  * additional AfterTriggerEventList values pointing at this event
4425  * list, since we'd fail to fix their copies of tailfree.
4426  */
4427  if (chunk == events->tail)
4428  events->tailfree = chunk->freeptr;
4429  }
4430  }
4431  if (slot1 != NULL)
4432  {
4435  }
4436 
4437  /* Release working resources */
4438  MemoryContextDelete(per_tuple_context);
4439 
4440  if (local_estate)
4441  {
4442  ExecCloseResultRelations(estate);
4443  ExecResetTupleTable(estate->es_tupleTable, false);
4444  FreeExecutorState(estate);
4445  }
4446 
4447  return all_fired;
4448 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:412
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
#define AllocSetContextCreate
Definition: memutils.h:173
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1238
TriggerFlags ate_flags
Definition: trigger.c:3479
void ExecCloseResultRelations(EState *estate)
Definition: execMain.c:1439
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3454
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3523
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:448
AfterTriggerEventChunk * tail
Definition: trigger.c:3529
Form_pg_class rd_rel
Definition: rel.h:109
#define GetTriggerSharedData(evt)
Definition: trigger.c:3504
#define for_each_event(eptr, cptr)
Definition: trigger.c:3536
void FreeExecutorState(EState *estate)
Definition: execUtils.c:186
#define ERROR
Definition: elog.h:46
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:195
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:439
EState * CreateExecutorState(void)
Definition: execUtils.c:90
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1282
List * es_tupleTable
Definition: execnodes.h:603
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1191
TupleDesc rd_att
Definition: rel.h:110
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3534
#define Assert(condition)
Definition: c.h:804
CommandId ats_firing_id
Definition: trigger.c:3470
#define RelationHasReferenceCountZero(relation)
Definition: rel.h:461
#define elog(elevel,...)
Definition: elog.h:232
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3455
#define RelationGetRelid(relation)
Definition: rel.h:477
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:442
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:4020

◆ afterTriggerMarkEvents()

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

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

4237 {
4238  bool found = false;
4239  bool deferred_found = false;
4240  AfterTriggerEvent event;
4241  AfterTriggerEventChunk *chunk;
4242 
4243  for_each_event_chunk(event, chunk, *events)
4244  {
4245  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4246  bool defer_it = false;
4247 
4248  if (!(event->ate_flags &
4250  {
4251  /*
4252  * This trigger hasn't been called or scheduled yet. Check if we
4253  * should call it now.
4254  */
4255  if (immediate_only && afterTriggerCheckState(evtshared))
4256  {
4257  defer_it = true;
4258  }
4259  else
4260  {
4261  /*
4262  * Mark it as to be fired in this firing cycle.
4263  */
4265  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4266  found = true;
4267  }
4268  }
4269 
4270  /*
4271  * If it's deferred, move it to move_list, if requested.
4272  */
4273  if (defer_it && move_list != NULL)
4274  {
4275  deferred_found = true;
4276  /* add it to move_list */
4277  afterTriggerAddEvent(move_list, event, evtshared);
4278  /* mark original copy "done" so we don't do it again */
4279  event->ate_flags |= AFTER_TRIGGER_DONE;
4280  }
4281  }
4282 
4283  /*
4284  * We could allow deferred triggers if, before the end of the
4285  * security-restricted operation, we were to verify that a SET CONSTRAINTS
4286  * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4287  */
4288  if (deferred_found && InSecurityRestrictedOperation())
4289  ereport(ERROR,
4290  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4291  errmsg("cannot fire deferred trigger within security-restricted operation")));
4292 
4293  return found;
4294 }
TriggerFlags ate_flags
Definition: trigger.c:3479
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3454
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3541
int errcode(int sqlerrcode)
Definition: elog.c:698
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3743
#define GetTriggerSharedData(evt)
Definition: trigger.c:3504
#define ERROR
Definition: elog.h:46
CommandId firing_counter
Definition: trigger.c:3634
#define ereport(elevel,...)
Definition: elog.h:157
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:627
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3789
CommandId ats_firing_id
Definition: trigger.c:3470
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3455
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

5528 {
5529  AfterTriggerEvent event;
5530  AfterTriggerEventChunk *chunk;
5531  int depth;
5532 
5533  /* Scan queued events */
5535  {
5536  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5537 
5538  /*
5539  * We can ignore completed events. (Even if a DONE flag is rolled
5540  * back by subxact abort, it's OK because the effects of the TRUNCATE
5541  * or whatever must get rolled back too.)
5542  */
5543  if (event->ate_flags & AFTER_TRIGGER_DONE)
5544  continue;
5545 
5546  if (evtshared->ats_relid == relid)
5547  return true;
5548  }
5549 
5550  /*
5551  * Also scan events queued by incomplete queries. This could only matter
5552  * if TRUNCATE/etc is executed by a function or trigger within an updating
5553  * query on the same relation, which is pretty perverse, but let's check.
5554  */
5555  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5556  {
5558  {
5559  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5560 
5561  if (event->ate_flags & AFTER_TRIGGER_DONE)
5562  continue;
5563 
5564  if (evtshared->ats_relid == relid)
5565  return true;
5566  }
5567  }
5568 
5569  return false;
5570 }
TriggerFlags ate_flags
Definition: trigger.c:3479
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3454
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3541
AfterTriggersQueryData * query_stack
Definition: trigger.c:3640
#define GetTriggerSharedData(evt)
Definition: trigger.c:3504
AfterTriggerEventList events
Definition: trigger.c:3636
AfterTriggerEventList events
Definition: trigger.c:3651
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ afterTriggerRestoreEventList()

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

Definition at line 3926 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

3928 {
3929  AfterTriggerEventChunk *chunk;
3930  AfterTriggerEventChunk *next_chunk;
3931 
3932  if (old_events->tail == NULL)
3933  {
3934  /* restoring to a completely empty state, so free everything */
3935  afterTriggerFreeEventList(events);
3936  }
3937  else
3938  {
3939  *events = *old_events;
3940  /* free any chunks after the last one we want to keep */
3941  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
3942  {
3943  next_chunk = chunk->next;
3944  pfree(chunk);
3945  }
3946  /* and clean up the tail chunk to be the right length */
3947  events->tail->next = NULL;
3948  events->tail->freeptr = events->tailfree;
3949 
3950  /*
3951  * We don't make any effort to remove now-unused shared data records.
3952  * They might still be useful, anyway.
3953  */
3954  }
3955 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3529
struct AfterTriggerEventChunk * next
Definition: trigger.c:3516
void pfree(void *pointer)
Definition: mcxt.c:1169
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3905

◆ 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 5597 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, AfterTriggersQueryData::events, ExecGetChildToRootMap(), execute_attr_map_slot(), GetAfterTriggersStoreSlot(), GetCurrentFDWTuplestore(), i, ItemPointerCopy, ItemPointerSetInvalid, list_member_oid(), AfterTriggersData::maxquerydepth, AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, TupleConversionMap::outdesc, AfterTriggersData::query_depth, AfterTriggersData::query_stack, RelationData::rd_rel, RelationGetRelid, RI_FKey_fk_upd_check_required(), RI_FKey_pk_upd_check_required(), RI_FKey_trigger_type(), ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, RI_TRIGGER_FK, RI_TRIGGER_NONE, RI_TRIGGER_PK, 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, TupIsNull, and tuplestore_puttupleslot().

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

5602 {
5603  Relation rel = relinfo->ri_RelationDesc;
5604  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
5605  AfterTriggerEventData new_event;
5606  AfterTriggerSharedData new_shared;
5607  char relkind = rel->rd_rel->relkind;
5608  int tgtype_event;
5609  int tgtype_level;
5610  int i;
5611  Tuplestorestate *fdw_tuplestore = NULL;
5612 
5613  /*
5614  * Check state. We use a normal test not Assert because it is possible to
5615  * reach here in the wrong state given misconfigured RI triggers, in
5616  * particular deferring a cascade action trigger.
5617  */
5618  if (afterTriggers.query_depth < 0)
5619  elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
5620 
5621  /* Be sure we have enough space to record events at this query depth. */
5624 
5625  /*
5626  * If the directly named relation has any triggers with transition tables,
5627  * then we need to capture transition tuples.
5628  */
5629  if (row_trigger && transition_capture != NULL)
5630  {
5631  TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
5632  TupleConversionMap *map = ExecGetChildToRootMap(relinfo);
5633  bool delete_old_table = transition_capture->tcs_delete_old_table;
5634  bool update_old_table = transition_capture->tcs_update_old_table;
5635  bool update_new_table = transition_capture->tcs_update_new_table;
5636  bool insert_new_table = transition_capture->tcs_insert_new_table;
5637 
5638  /*
5639  * For INSERT events NEW should be non-NULL, for DELETE events OLD
5640  * should be non-NULL, whereas for UPDATE events normally both OLD and
5641  * NEW are non-NULL. But for UPDATE events fired for capturing
5642  * transition tuples during UPDATE partition-key row movement, OLD is
5643  * NULL when the event is for a row being inserted, whereas NEW is
5644  * NULL when the event is for a row being deleted.
5645  */
5646  Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
5647  TupIsNull(oldslot)));
5648  Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
5649  TupIsNull(newslot)));
5650 
5651  if (!TupIsNull(oldslot) &&
5652  ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
5653  (event == TRIGGER_EVENT_UPDATE && update_old_table)))
5654  {
5655  Tuplestorestate *old_tuplestore;
5656 
5657  old_tuplestore = transition_capture->tcs_private->old_tuplestore;
5658 
5659  if (map != NULL)
5660  {
5661  AfterTriggersTableData *table = transition_capture->tcs_private;
5662  TupleTableSlot *storeslot;
5663 
5664  storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5665  execute_attr_map_slot(map->attrMap, oldslot, storeslot);
5666  tuplestore_puttupleslot(old_tuplestore, storeslot);
5667  }
5668  else
5669  tuplestore_puttupleslot(old_tuplestore, oldslot);
5670  }
5671  if (!TupIsNull(newslot) &&
5672  ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
5673  (event == TRIGGER_EVENT_UPDATE && update_new_table)))
5674  {
5675  Tuplestorestate *new_tuplestore;
5676 
5677  new_tuplestore = transition_capture->tcs_private->new_tuplestore;
5678 
5679  if (original_insert_tuple != NULL)
5680  tuplestore_puttupleslot(new_tuplestore,
5681  original_insert_tuple);
5682  else if (map != NULL)
5683  {
5684  AfterTriggersTableData *table = transition_capture->tcs_private;
5685  TupleTableSlot *storeslot;
5686 
5687  storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
5688  execute_attr_map_slot(map->attrMap, newslot, storeslot);
5689  tuplestore_puttupleslot(new_tuplestore, storeslot);
5690  }
5691  else
5692  tuplestore_puttupleslot(new_tuplestore, newslot);
5693  }
5694 
5695  /*
5696  * If transition tables are the only reason we're here, return. As
5697  * mentioned above, we can also be here during update tuple routing in
5698  * presence of transition tables, in which case this function is
5699  * called separately for oldtup and newtup, so we expect exactly one
5700  * of them to be NULL.
5701  */
5702  if (trigdesc == NULL ||
5703  (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
5704  (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
5705  (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
5706  (event == TRIGGER_EVENT_UPDATE && (TupIsNull(oldslot) ^ TupIsNull(newslot))))
5707  return;
5708  }
5709 
5710  /*
5711  * Validate the event code and collect the associated tuple CTIDs.
5712  *
5713  * The event code will be used both as a bitmask and an array offset, so
5714  * validation is important to make sure we don't walk off the edge of our
5715  * arrays.
5716  *
5717  * Also, if we're considering statement-level triggers, check whether we
5718  * already queued a set of them for this event, and cancel the prior set
5719  * if so. This preserves the behavior that statement-level triggers fire
5720  * just once per statement and fire after row-level triggers.
5721  */
5722  switch (event)
5723  {
5724  case TRIGGER_EVENT_INSERT:
5725  tgtype_event = TRIGGER_TYPE_INSERT;
5726  if (row_trigger)
5727  {
5728  Assert(oldslot == NULL);
5729  Assert(newslot != NULL);
5730  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
5731  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5732  }
5733  else
5734  {
5735  Assert(oldslot == NULL);
5736  Assert(newslot == NULL);
5737  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5738  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5740  CMD_INSERT, event);
5741  }
5742  break;
5743  case TRIGGER_EVENT_DELETE:
5744  tgtype_event = TRIGGER_TYPE_DELETE;
5745  if (row_trigger)
5746  {
5747  Assert(oldslot != NULL);
5748  Assert(newslot == NULL);
5749  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
5750  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5751  }
5752  else
5753  {
5754  Assert(oldslot == NULL);
5755  Assert(newslot == NULL);
5756  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5757  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5759  CMD_DELETE, event);
5760  }
5761  break;
5762  case TRIGGER_EVENT_UPDATE:
5763  tgtype_event = TRIGGER_TYPE_UPDATE;
5764  if (row_trigger)
5765  {
5766  Assert(oldslot != NULL);
5767  Assert(newslot != NULL);
5768  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
5769  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
5770  }
5771  else
5772  {
5773  Assert(oldslot == NULL);
5774  Assert(newslot == NULL);
5775  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5776  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5778  CMD_UPDATE, event);
5779  }
5780  break;
5782  tgtype_event = TRIGGER_TYPE_TRUNCATE;
5783  Assert(oldslot == NULL);
5784  Assert(newslot == NULL);
5785  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5786  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5787  break;
5788  default:
5789  elog(ERROR, "invalid after-trigger event code: %d", event);
5790  tgtype_event = 0; /* keep compiler quiet */
5791  break;
5792  }
5793 
5794  if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
5795  new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ?
5797  /* else, we'll initialize ate_flags for each trigger */
5798 
5799  tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
5800 
5801  for (i = 0; i < trigdesc->numtriggers; i++)
5802  {
5803  Trigger *trigger = &trigdesc->triggers[i];
5804 
5805  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
5806  tgtype_level,
5807  TRIGGER_TYPE_AFTER,
5808  tgtype_event))
5809  continue;
5810  if (!TriggerEnabled(estate, relinfo, trigger, event,
5811  modifiedCols, oldslot, newslot))
5812  continue;
5813 
5814  if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
5815  {
5816  if (fdw_tuplestore == NULL)
5817  {
5818  fdw_tuplestore = GetCurrentFDWTuplestore();
5819  new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH;
5820  }
5821  else
5822  /* subsequent event for the same tuple */
5823  new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE;
5824  }
5825 
5826  /*
5827  * If the trigger is a foreign key enforcement trigger, there are
5828  * certain cases where we can skip queueing the event because we can
5829  * tell by inspection that the FK constraint will still pass.
5830  */
5831  if (TRIGGER_FIRED_BY_UPDATE(event) || TRIGGER_FIRED_BY_DELETE(event))
5832  {
5833  switch (RI_FKey_trigger_type(trigger->tgfoid))
5834  {
5835  case RI_TRIGGER_PK:
5836  /* Update or delete on trigger's PK table */
5837  if (!RI_FKey_pk_upd_check_required(trigger, rel,
5838  oldslot, newslot))
5839  {
5840  /* skip queuing this event */
5841  continue;
5842  }
5843  break;
5844 
5845  case RI_TRIGGER_FK:
5846  /* Update on trigger's FK table */
5847  if (!RI_FKey_fk_upd_check_required(trigger, rel,
5848  oldslot, newslot))
5849  {
5850  /* skip queuing this event */
5851  continue;
5852  }
5853  break;
5854 
5855  case RI_TRIGGER_NONE:
5856  /* Not an FK trigger */
5857  break;
5858  }
5859  }
5860 
5861  /*
5862  * If the trigger is a deferred unique constraint check trigger, only
5863  * queue it if the unique constraint was potentially violated, which
5864  * we know from index insertion time.
5865  */
5866  if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
5867  {
5868  if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
5869  continue; /* Uniqueness definitely not violated */
5870  }
5871 
5872  /*
5873  * Fill in event structure and add it to the current query's queue.
5874  * Note we set ats_table to NULL whenever this trigger doesn't use
5875  * transition tables, to improve sharability of the shared event data.
5876  */
5877  new_shared.ats_event =
5878  (event & TRIGGER_EVENT_OPMASK) |
5879  (row_trigger ? TRIGGER_EVENT_ROW : 0) |
5880  (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
5881  (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
5882  new_shared.ats_tgoid = trigger->tgoid;
5883  new_shared.ats_relid = RelationGetRelid(rel);
5884  new_shared.ats_firing_id = 0;
5885  if ((trigger->tgoldtable || trigger->tgnewtable) &&
5886  transition_capture != NULL)
5887  new_shared.ats_table = transition_capture->tcs_private;
5888  else
5889  new_shared.ats_table = NULL;
5890  new_shared.ats_modifiedcols = modifiedCols;
5891 
5893  &new_event, &new_shared);
5894  }
5895 
5896  /*
5897  * Finally, spool any foreign tuple(s). The tuplestore squashes them to
5898  * minimal tuples, so this loses any system columns. The executor lost
5899  * those columns before us, for an unrelated reason, so this is fine.
5900  */
5901  if (fdw_tuplestore)
5902  {
5903  if (oldslot != NULL)
5904  tuplestore_puttupleslot(fdw_tuplestore, oldslot);
5905  if (newslot != NULL)
5906  tuplestore_puttupleslot(fdw_tuplestore, newslot);
5907  }
5908 }
#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:3467
Relation ri_RelationDesc
Definition: execnodes.h:412
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:2930
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3457
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:107
TupleDesc outdesc
Definition: tupconvert.h:27
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: trigger.c:3261
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3707
ItemPointerData ate_ctid2
Definition: trigger.c:3481
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:92
Oid tgfoid
Definition: reltrigger.h:28
TriggerFlags ate_flags
Definition: trigger.c:3479
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:5961
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:106
Oid tgoid
Definition: reltrigger.h:25
AfterTriggersQueryData * query_stack
Definition: trigger.c:3640
Tuplestorestate * old_tuplestore
Definition: trigger.c:3674
Form_pg_class rd_rel
Definition: rel.h:109
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:95
Bitmapset * ats_modifiedcols
Definition: trigger.c:3472
struct AfterTriggersTableData * tcs_private
Definition: trigger.h:80
#define ERROR
Definition: elog.h:46
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
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1234
#define TupIsNull(slot)
Definition: tuptable.h:292
bool trig_insert_after_row
Definition: reltrigger.h:57
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3459
Tuplestorestate * new_tuplestore
Definition: trigger.c:3675
AttrMap * attrMap
Definition: tupconvert.h:28
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3460
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:439
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
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:1170
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3471
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:5090
#define RI_TRIGGER_FK
Definition: trigger.h:269
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:689
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:177
#define Assert(condition)
Definition: c.h:804
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3789
#define TRIGGER_EVENT_TRUNCATE
Definition: trigger.h:94
Oid tgconstrindid
Definition: reltrigger.h:34
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3458
CommandId ats_firing_id
Definition: trigger.c:3470
#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:1202
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:91
#define RI_TRIGGER_PK
Definition: trigger.h:268
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
static TupleTableSlot * GetAfterTriggersStoreSlot(AfterTriggersTableData *table, TupleDesc tupdesc)
Definition: trigger.c:4501
#define elog(elevel,...)
Definition: elog.h:232
int i
ItemPointerData ate_ctid1
Definition: trigger.c:3480
#define RI_TRIGGER_NONE
Definition: trigger.h:270
char * tgoldtable
Definition: reltrigger.h:43
AfterTriggerEventList events
Definition: trigger.c:3651
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:115
static AfterTriggersData afterTriggers
Definition: trigger.c:3679
#define RelationGetRelid(relation)
Definition: rel.h:477
#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 5212 of file trigger.c.

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, BTEqualStrategyNumber, RangeVar::catalogname, 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, and SetConstraintStateData::trigstates.

Referenced by standard_ProcessUtility().

5213 {
5214  int my_level = GetCurrentTransactionNestLevel();
5215 
5216  /* If we haven't already done so, initialize our state. */
5217  if (afterTriggers.state == NULL)
5219 
5220  /*
5221  * If in a subtransaction, and we didn't save the current state already,
5222  * save it so it can be restored if the subtransaction aborts.
5223  */
5224  if (my_level > 1 &&
5225  afterTriggers.trans_stack[my_level].state == NULL)
5226  {
5227  afterTriggers.trans_stack[my_level].state =
5229  }
5230 
5231  /*
5232  * Handle SET CONSTRAINTS ALL ...
5233  */
5234  if (stmt->constraints == NIL)
5235  {
5236  /*
5237  * Forget any previous SET CONSTRAINTS commands in this transaction.
5238  */
5240 
5241  /*
5242  * Set the per-transaction ALL state to known.
5243  */
5244  afterTriggers.state->all_isset = true;
5246  }
5247  else
5248  {
5249  Relation conrel;
5250  Relation tgrel;
5251  List *conoidlist = NIL;
5252  List *tgoidlist = NIL;
5253  ListCell *lc;
5254 
5255  /*
5256  * Handle SET CONSTRAINTS constraint-name [, ...]
5257  *
5258  * First, identify all the named constraints and make a list of their
5259  * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5260  * the same name within a schema, the specifications are not
5261  * necessarily unique. Our strategy is to target all matching
5262  * constraints within the first search-path schema that has any
5263  * matches, but disregard matches in schemas beyond the first match.
5264  * (This is a bit odd but it's the historical behavior.)
5265  *
5266  * A constraint in a partitioned table may have corresponding
5267  * constraints in the partitions. Grab those too.
5268  */
5269  conrel = table_open(ConstraintRelationId, AccessShareLock);
5270 
5271  foreach(lc, stmt->constraints)
5272  {
5273  RangeVar *constraint = lfirst(lc);
5274  bool found;
5275  List *namespacelist;
5276  ListCell *nslc;
5277 
5278  if (constraint->catalogname)
5279  {
5280  if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5281  ereport(ERROR,
5282  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5283  errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5284  constraint->catalogname, constraint->schemaname,
5285  constraint->relname)));
5286  }
5287 
5288  /*
5289  * If we're given the schema name with the constraint, look only
5290  * in that schema. If given a bare constraint name, use the
5291  * search path to find the first matching constraint.
5292  */
5293  if (constraint->schemaname)
5294  {
5295  Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5296  false);
5297 
5298  namespacelist = list_make1_oid(namespaceId);
5299  }
5300  else
5301  {
5302  namespacelist = fetch_search_path(true);
5303  }
5304 
5305  found = false;
5306  foreach(nslc, namespacelist)
5307  {
5308  Oid namespaceId = lfirst_oid(nslc);
5309  SysScanDesc conscan;
5310  ScanKeyData skey[2];
5311  HeapTuple tup;
5312 
5313  ScanKeyInit(&skey[0],
5314  Anum_pg_constraint_conname,
5315  BTEqualStrategyNumber, F_NAMEEQ,
5316  CStringGetDatum(constraint->relname));
5317  ScanKeyInit(&skey[1],
5318  Anum_pg_constraint_connamespace,
5319  BTEqualStrategyNumber, F_OIDEQ,
5320  ObjectIdGetDatum(namespaceId));
5321 
5322  conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5323  true, NULL, 2, skey);
5324 
5325  while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5326  {
5328 
5329  if (con->condeferrable)
5330  conoidlist = lappend_oid(conoidlist, con->oid);
5331  else if (stmt->deferred)
5332  ereport(ERROR,
5333  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5334  errmsg("constraint \"%s\" is not deferrable",
5335  constraint->relname)));
5336  found = true;
5337  }
5338 
5339  systable_endscan(conscan);
5340 
5341  /*
5342  * Once we've found a matching constraint we do not search
5343  * later parts of the search path.
5344  */
5345  if (found)
5346  break;
5347  }
5348 
5349  list_free(namespacelist);
5350 
5351  /*
5352  * Not found ?
5353  */
5354  if (!found)
5355  ereport(ERROR,
5356  (errcode(ERRCODE_UNDEFINED_OBJECT),
5357  errmsg("constraint \"%s\" does not exist",
5358  constraint->relname)));
5359  }
5360 
5361  /*
5362  * Scan for any possible descendants of the constraints. We append
5363  * whatever we find to the same list that we're scanning; this has the
5364  * effect that we create new scans for those, too, so if there are
5365  * further descendents, we'll also catch them.
5366  */
5367  foreach(lc, conoidlist)
5368  {
5369  Oid parent = lfirst_oid(lc);
5370  ScanKeyData key;
5371  SysScanDesc scan;
5372  HeapTuple tuple;
5373 
5374  ScanKeyInit(&key,
5375  Anum_pg_constraint_conparentid,
5376  BTEqualStrategyNumber, F_OIDEQ,
5377  ObjectIdGetDatum(parent));
5378 
5379  scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5380 
5381  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5382  {
5384 
5385  conoidlist = lappend_oid(conoidlist, con->oid);
5386  }
5387 
5388  systable_endscan(scan);
5389  }
5390 
5391  table_close(conrel, AccessShareLock);
5392 
5393  /*
5394  * Now, locate the trigger(s) implementing each of these constraints,
5395  * and make a list of their OIDs.
5396  */
5397  tgrel = table_open(TriggerRelationId, AccessShareLock);
5398 
5399  foreach(lc, conoidlist)
5400  {
5401  Oid conoid = lfirst_oid(lc);
5402  ScanKeyData skey;
5403  SysScanDesc tgscan;
5404  HeapTuple htup;
5405 
5406  ScanKeyInit(&skey,
5407  Anum_pg_trigger_tgconstraint,
5408  BTEqualStrategyNumber, F_OIDEQ,
5409  ObjectIdGetDatum(conoid));
5410 
5411  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5412  NULL, 1, &skey);
5413 
5414  while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5415  {
5416  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5417 
5418  /*
5419  * Silently skip triggers that are marked as non-deferrable in
5420  * pg_trigger. This is not an error condition, since a
5421  * deferrable RI constraint may have some non-deferrable
5422  * actions.
5423  */
5424  if (pg_trigger->tgdeferrable)
5425  tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid);
5426  }
5427 
5428  systable_endscan(tgscan);
5429  }
5430 
5431  table_close(tgrel, AccessShareLock);
5432 
5433  /*
5434  * Now we can set the trigger states of individual triggers for this
5435  * xact.
5436  */
5437  foreach(lc, tgoidlist)
5438  {
5439  Oid tgoid = lfirst_oid(lc);
5441  bool found = false;
5442  int i;
5443 
5444  for (i = 0; i < state->numstates; i++)
5445  {
5446  if (state->trigstates[i].sct_tgoid == tgoid)
5447  {
5448  state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5449  found = true;
5450  break;
5451  }
5452  }
5453  if (!found)
5454  {
5456  SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5457  }
5458  }
5459  }
5460 
5461  /*
5462  * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5463  * checks against that constraint must be made when the SET CONSTRAINTS
5464  * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5465  * apply retroactively. We've updated the constraints state, so scan the
5466  * list of previously deferred events to fire any that have now become
5467  * immediate.
5468  *
5469  * Obviously, if this was SET ... DEFERRED then it can't have converted
5470  * any unfired events to immediate, so we need do nothing in that case.
5471  */
5472  if (!stmt->deferred)
5473  {
5475  bool snapshot_set = false;
5476 
5477  while (afterTriggerMarkEvents(events, NULL, true))
5478  {
5479  CommandId firing_id = afterTriggers.firing_counter++;
5480 
5481  /*
5482  * Make sure a snapshot has been established in case trigger
5483  * functions need one. Note that we avoid setting a snapshot if
5484  * we don't find at least one trigger that has to be fired now.
5485  * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
5486  * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
5487  * at the start of a transaction it's not possible for any trigger
5488  * events to be queued yet.)
5489  */
5490  if (!snapshot_set)
5491  {
5493  snapshot_set = true;
5494  }
5495 
5496  /*
5497  * We can delete fired events if we are at top transaction level,
5498  * but we'd better not if inside a subtransaction, since the
5499  * subtransaction could later get rolled back.
5500  */
5501  if (afterTriggerInvokeEvents(events, firing_id, NULL,
5502  !IsSubTransaction()))
5503  break; /* all fired */
5504  }
5505 
5506  if (snapshot_set)
5508  }
5509 }
#define NIL
Definition: pg_list.h:65
uint32 CommandId
Definition: c.h:601
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2938
AfterTriggersTransData * trans_stack
Definition: trigger.c:3645
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:595
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3408
#define AccessShareLock
Definition: lockdefs.h:36
int errcode(int sqlerrcode)
Definition: elog.c:698
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition: trigger.c:5182
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4318
void PopActiveSnapshot(void)
Definition: snapmgr.c:774
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:372
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:383
char * schemaname
Definition: primnodes.h:67
char * relname
Definition: primnodes.h:68
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:502
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
SetConstraintState state
Definition: trigger.c:3635
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2113
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:680
#define CStringGetDatum(X)
Definition: postgres.h:622
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:5137
#define list_make1_oid(x1)
Definition: pg_list.h:236
Oid MyDatabaseId
Definition: globals.c:88
CommandId firing_counter
Definition: trigger.c:3634
#define ereport(elevel,...)
Definition: elog.h:157
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:858
FormData_pg_constraint * Form_pg_constraint
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:169
Definition: regguts.h:317
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
bool IsSubTransaction(void)
Definition: xact.c:4770
int errmsg(const char *fmt,...)
Definition: elog.c:909
void list_free(List *list)
Definition: list.c:1391
int i
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4234
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
AfterTriggerEventList events
Definition: trigger.c:3636
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:4428
static AfterTriggersData afterTriggers
Definition: trigger.c:3679
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:3659
static SetConstraintState SetConstraintStateCopy(SetConstraintState state)
Definition: trigger.c:5162

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

5916 {
5917  bool result;
5918  AfterTriggersTableData *table;
5919 
5920  /* Check state, like AfterTriggerSaveEvent. */
5921  if (afterTriggers.query_depth < 0)
5922  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
5923 
5924  /* Be sure we have enough space to record events at this query depth. */
5927 
5928  /*
5929  * We keep this state in the AfterTriggersTableData that also holds
5930  * transition tables for the relation + operation. In this way, if we are
5931  * forced to make a new set of transition tables because more tuples get
5932  * entered after we've already fired triggers, we will allow a new set of
5933  * statement triggers to get queued.
5934  */
5935  table = GetAfterTriggersTableData(relid, cmdType);
5936  result = table->before_trig_done;
5937  table->before_trig_done = true;
5938  return result;
5939 }
#define ERROR
Definition: elog.h:46
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4464
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:5090
#define elog(elevel,...)
Definition: elog.h:232
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ cancel_prior_stmt_triggers()

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

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

5962 {
5963  AfterTriggersTableData *table;
5965 
5966  /*
5967  * We keep this state in the AfterTriggersTableData that also holds
5968  * transition tables for the relation + operation. In this way, if we are
5969  * forced to make a new set of transition tables because more tuples get
5970  * entered after we've already fired triggers, we will allow a new set of
5971  * statement triggers to get queued without canceling the old ones.
5972  */
5973  table = GetAfterTriggersTableData(relid, cmdType);
5974 
5975  if (table->after_trig_done)
5976  {
5977  /*
5978  * We want to start scanning from the tail location that existed just
5979  * before we inserted any statement triggers. But the events list
5980  * might've been entirely empty then, in which case scan from the
5981  * current head.
5982  */
5983  AfterTriggerEvent event;
5984  AfterTriggerEventChunk *chunk;
5985 
5986  if (table->after_trig_events.tail)
5987  {
5988  chunk = table->after_trig_events.tail;
5989  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
5990  }
5991  else
5992  {
5993  chunk = qs->events.head;
5994  event = NULL;
5995  }
5996 
5997  for_each_chunk_from(chunk)
5998  {
5999  if (event == NULL)
6000  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6001  for_each_event_from(event, chunk)
6002  {
6003  AfterTriggerShared evtshared = GetTriggerSharedData(event);
6004 
6005  /*
6006  * Exit loop when we reach events that aren't AS triggers for
6007  * the target relation.
6008  */
6009  if (evtshared->ats_relid != relid)
6010  goto done;
6011  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6012  goto done;
6013  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6014  goto done;
6015  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6016  goto done;
6017  /* OK, mark it DONE */
6018  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6019  event->ate_flags |= AFTER_TRIGGER_DONE;
6020  }
6021  /* signal we must reinitialize event ptr for next chunk */
6022  event = NULL;
6023  }
6024  }
6025 done:
6026 
6027  /* In any case, save current insertion point for next time */
6028  table->after_trig_done = true;
6029  table->after_trig_events = qs->events;
6030 }
TriggerEvent ats_event
Definition: trigger.c:3467
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3454
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3523
AfterTriggersQueryData * query_stack
Definition: trigger.c:3640
AfterTriggerEventChunk * tail
Definition: trigger.c:3529
#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:3504
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4464
AfterTriggerEventList after_trig_events
Definition: trigger.c:3673
#define for_each_chunk_from(cptr)
Definition: trigger.c:3545
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3547
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3475
AfterTriggerEventChunk * head
Definition: trigger.c:3528
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3455
AfterTriggerEventList events
Definition: trigger.c:3651
static AfterTriggersData afterTriggers
Definition: trigger.c:3679

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

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

2015 {
2016  TriggerDesc *newdesc;
2017  Trigger *trigger;
2018  int i;
2019 
2020  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2021  return NULL;
2022 
2023  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
2024  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2025 
2026  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2027  memcpy(trigger, trigdesc->triggers,
2028  trigdesc->numtriggers * sizeof(Trigger));
2029  newdesc->triggers = trigger;
2030 
2031  for (i = 0; i < trigdesc->numtriggers; i++)
2032  {
2033  trigger->tgname = pstrdup(trigger->tgname);
2034  if (trigger->tgnattr > 0)
2035  {
2036  int16 *newattr;
2037 
2038  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2039  memcpy(newattr, trigger->tgattr,
2040  trigger->tgnattr * sizeof(int16));
2041  trigger->tgattr = newattr;
2042  }
2043  if (trigger->tgnargs > 0)
2044  {
2045  char **newargs;
2046  int16 j;
2047 
2048  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2049  for (j = 0; j < trigger->tgnargs; j++)
2050  newargs[j] = pstrdup(trigger->tgargs[j]);
2051  trigger->tgargs = newargs;
2052  }
2053  if (trigger->tgqual)
2054  trigger->tgqual = pstrdup(trigger->tgqual);
2055  if (trigger->tgoldtable)
2056  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2057  if (trigger->tgnewtable)
2058  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2059  trigger++;
2060  }
2061 
2062  return newdesc;
2063 }
signed short int16
Definition: c.h:428
char * pstrdup(const char *in)
Definition: mcxt.c:1299
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:1062
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 156 of file trigger.c.

References CreateTriggerFiringOn(), and TRIGGER_FIRES_ON_ORIGIN.

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

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

◆ CreateTriggerFiringOn()

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

Definition at line 173 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(), ObjectAddress::classId, CreateTrigStmt::columns, CommandCounterIncrement(), CreateTrigStmt::constrrel, copyObject, CreateConstraintEntry(), CreateTriggerFiringOn(), 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(), errdetail_relkind_not_supported(), 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, CreateTrigStmt::trigname, TupleDescAttr, values, Var::varattno, Var::varno, and CreateTrigStmt::whenClause.

Referenced by CloneRowTriggersToPartition(), CreateTrigger(), and CreateTriggerFiringOn().

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

◆ EnableDisableTrigger()

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

Definition at line 1683 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(), and table_open().

Referenced by ATExecEnableDisableTrigger().

1685 {
1686  Relation tgrel;
1687  int nkeys;
1688  ScanKeyData keys[2];
1689  SysScanDesc tgscan;
1690  HeapTuple tuple;
1691  bool found;
1692  bool changed;
1693 
1694  /* Scan the relevant entries in pg_triggers */
1695  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1696 
1697  ScanKeyInit(&keys[0],
1698  Anum_pg_trigger_tgrelid,
1699  BTEqualStrategyNumber, F_OIDEQ,
1701  if (tgname)
1702  {
1703  ScanKeyInit(&keys[1],
1704  Anum_pg_trigger_tgname,
1705  BTEqualStrategyNumber, F_NAMEEQ,
1706  CStringGetDatum(tgname));
1707  nkeys = 2;
1708  }
1709  else
1710  nkeys = 1;
1711 
1712  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1713  NULL, nkeys, keys);
1714 
1715  found = changed = false;
1716 
1717  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1718  {
1719  Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
1720 
1721  if (oldtrig->tgisinternal)
1722  {
1723  /* system trigger ... ok to process? */
1724  if (skip_system)
1725  continue;
1726  if (!superuser())
1727  ereport(ERROR,
1728  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1729  errmsg("permission denied: \"%s\" is a system trigger",
1730  NameStr(oldtrig->tgname))));
1731  }
1732 
1733  found = true;
1734 
1735  if (oldtrig->tgenabled != fires_when)
1736  {
1737  /* need to change this one ... make a copy to scribble on */
1738  HeapTuple newtup = heap_copytuple(tuple);
1739  Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
1740 
1741  newtrig->tgenabled = fires_when;
1742 
1743  CatalogTupleUpdate(tgrel, &newtup->t_self, newtup);
1744 
1745  heap_freetuple(newtup);
1746 
1747  changed = true;
1748  }
1749 
1750  InvokeObjectPostAlterHook(TriggerRelationId,
1751  oldtrig->oid, 0);
1752  }
1753 
1754  systable_endscan(tgscan);
1755 
1756  table_close(tgrel, RowExclusiveLock);
1757 
1758  if (tgname && !found)
1759  ereport(ERROR,
1760  (errcode(ERRCODE_UNDEFINED_OBJECT),
1761  errmsg("trigger \"%s\" for table \"%s\" does not exist",
1762  tgname, RelationGetRelationName(rel))));
1763 
1764  /*
1765  * If we changed anything, broadcast a SI inval message to force each
1766  * backend (including our own!) to rebuild relation's relcache entry.
1767  * Otherwise they will fail to apply the change promptly.
1768  */
1769  if (changed)
1771 }
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:595
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
int errcode(int sqlerrcode)
Definition: elog.c:698
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:383
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:502
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
#define RowExclusiveLock
Definition: lockdefs.h:38
#define CStringGetDatum(X)
Definition: postgres.h:622
#define RelationGetRelationName(relation)
Definition: rel.h:511
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:175
#define ereport(elevel,...)
Definition: elog.h:157
#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:80
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1342
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define NameStr(name)
Definition: c.h:681
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:477
#define BTEqualStrategyNumber
Definition: stratnum.h:31

◆ ExecARDeleteTriggers()

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

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

2695 {
2696  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2697 
2698  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2699  (transition_capture && transition_capture->tcs_delete_old_table))
2700  {
2701  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2702 
2703  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2704  if (fdw_trigtuple == NULL)
2705  GetTupleForTrigger(estate,
2706  NULL,
2707  relinfo,
2708  tupleid,
2710  slot,
2711  NULL);
2712  else
2713  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2714 
2716  true, slot, NULL, NIL, NULL,
2717  transition_capture);
2718  }
2719 }
#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:3145
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:92
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1166
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1469
bool trig_delete_after_row
Definition: reltrigger.h:67
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:439
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:5597
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:804

◆ ExecARInsertTriggers()

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

Definition at line 2461 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(), ExecBatchInsert(), ExecInsert(), and ExecSimpleRelationInsert().

2464 {
2465  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2466 
2467  if ((trigdesc && trigdesc->trig_insert_after_row) ||
2468  (transition_capture && transition_capture->tcs_insert_new_table))
2469