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 SetTriggerFlags (TriggerDesc *trigdesc, Trigger *trigger)
 
static bool GetTupleForTrigger (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **newSlot)
 
static bool TriggerEnabled (EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
static HeapTuple ExecCallTriggerFunc (TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
 
static void AfterTriggerSaveEvent (EState *estate, ResultRelInfo *relinfo, int event, bool row_trigger, TupleTableSlot *oldtup, TupleTableSlot *newtup, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture)
 
static void AfterTriggerEnlargeQueryState (void)
 
static bool before_stmt_triggers_fired (Oid relid, CmdType cmdType)
 
ObjectAddress CreateTrigger (CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
 
void RemoveTriggerById (Oid trigOid)
 
Oid get_trigger_oid (Oid relid, const char *trigname, bool missing_ok)
 
static void RangeVarCallbackForRenameTrigger (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
ObjectAddress renametrig (RenameStmt *stmt)
 
void EnableDisableTrigger (Relation rel, const char *tgname, char fires_when, bool skip_system, LOCKMODE lockmode)
 
void RelationBuildTriggers (Relation relation)
 
TriggerDescCopyTriggerDesc (TriggerDesc *trigdesc)
 
void FreeTriggerDesc (TriggerDesc *trigdesc)
 
const char * FindTriggerIncompatibleWithInheritance (TriggerDesc *trigdesc)
 
void ExecBSInsertTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASInsertTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecARInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
bool ExecIRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecBSDeleteTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASDeleteTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRDeleteTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot)
 
void ExecARDeleteTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
 
bool ExecIRDeleteTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
 
void ExecBSUpdateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASUpdateTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRUpdateTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot)
 
void ExecARUpdateTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
bool ExecIRUpdateTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
 
void ExecBSTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
static void AfterTriggerExecute (EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
 
static AfterTriggersTableDataGetAfterTriggersTableData (Oid relid, CmdType cmdType)
 
static void AfterTriggerFreeQuery (AfterTriggersQueryData *qs)
 
static SetConstraintState SetConstraintStateCreate (int numalloc)
 
static SetConstraintState SetConstraintStateCopy (SetConstraintState state)
 
static SetConstraintState SetConstraintStateAddItem (SetConstraintState state, Oid tgoid, bool tgisdeferred)
 
static void cancel_prior_stmt_triggers (Oid relid, CmdType cmdType, int tgevent)
 
static TuplestorestateGetCurrentFDWTuplestore (void)
 
static bool afterTriggerCheckState (AfterTriggerShared evtshared)
 
static void afterTriggerAddEvent (AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
 
static void afterTriggerFreeEventList (AfterTriggerEventList *events)
 
static void afterTriggerRestoreEventList (AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
 
static void afterTriggerDeleteHeadEventChunk (AfterTriggersQueryData *qs)
 
static bool afterTriggerMarkEvents (AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
 
static bool afterTriggerInvokeEvents (AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
 
TransitionCaptureStateMakeTransitionCaptureState (TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
 
void AfterTriggerBeginXact (void)
 
void AfterTriggerBeginQuery (void)
 
void AfterTriggerEndQuery (EState *estate)
 
void AfterTriggerFireDeferred (void)
 
void AfterTriggerEndXact (bool isCommit)
 
void AfterTriggerBeginSubXact (void)
 
void AfterTriggerEndSubXact (bool isCommit)
 
void AfterTriggerSetState (ConstraintsSetStmt *stmt)
 
bool AfterTriggerPendingOnRel (Oid relid)
 
Datum pg_trigger_depth (PG_FUNCTION_ARGS)
 

Variables

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

Macro Definition Documentation

◆ AFTER_TRIGGER_1CTID

#define AFTER_TRIGGER_1CTID   0x40000000

Definition at line 3306 of file trigger.c.

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3308 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 3381 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 3392 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:3370
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3344
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3322

Definition at line 3383 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:3344
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3322

Definition at line 3394 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:3308
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3306
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3307

Definition at line 3344 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3322 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3310 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3475 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3477 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3476 of file trigger.c.

◆ SetConstraintState

Definition at line 3258 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3237 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3298 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

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

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4488 of file trigger.c.

References AfterTriggersData::query_depth.

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

4489 {
4490  /* Increase the query stack depth */
4492 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

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

4757 {
4758  int my_level = GetCurrentTransactionNestLevel();
4759 
4760  /*
4761  * Allocate more space in the trans_stack if needed. (Note: because the
4762  * minimum nest level of a subtransaction is 2, we waste the first couple
4763  * entries of the array; not worth the notational effort to avoid it.)
4764  */
4765  while (my_level >= afterTriggers.maxtransdepth)
4766  {
4767  if (afterTriggers.maxtransdepth == 0)
4768  {
4769  /* Arbitrarily initialize for max of 8 subtransaction levels */
4772  8 * sizeof(AfterTriggersTransData));
4774  }
4775  else
4776  {
4777  /* repalloc will keep the stack in the same context */
4778  int new_alloc = afterTriggers.maxtransdepth * 2;
4779 
4782  new_alloc * sizeof(AfterTriggersTransData));
4783  afterTriggers.maxtransdepth = new_alloc;
4784  }
4785  }
4786 
4787  /*
4788  * Push the current information into the stack. The SET CONSTRAINTS state
4789  * is not saved until/unless changed. Likewise, we don't make a
4790  * per-subtransaction event context until needed.
4791  */
4792  afterTriggers.trans_stack[my_level].state = NULL;
4796 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3492
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggerEventList events
Definition: trigger.c:3507
CommandId firing_counter
Definition: trigger.c:3509
CommandId firing_counter
Definition: trigger.c:3481
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:857
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1070
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
AfterTriggerEventList events
Definition: trigger.c:3483
static AfterTriggersData afterTriggers
Definition: trigger.c:3526
SetConstraintState state
Definition: trigger.c:3506

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

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

4457 {
4458  /*
4459  * Initialize after-trigger state structure to empty
4460  */
4461  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4463 
4464  /*
4465  * Verify that there is no leftover state remaining. If these assertions
4466  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4467  * up properly.
4468  */
4469  Assert(afterTriggers.state == NULL);
4470  Assert(afterTriggers.query_stack == NULL);
4472  Assert(afterTriggers.event_cxt == NULL);
4473  Assert(afterTriggers.events.head == NULL);
4474  Assert(afterTriggers.trans_stack == NULL);
4476 }
uint32 CommandId
Definition: c.h:601
AfterTriggersTransData * trans_stack
Definition: trigger.c:3492
AfterTriggersQueryData * query_stack
Definition: trigger.c:3487
SetConstraintState state
Definition: trigger.c:3482
CommandId firing_counter
Definition: trigger.c:3481
#define Assert(condition)
Definition: c.h:804
AfterTriggerEventChunk * head
Definition: trigger.c:3375
MemoryContext event_cxt
Definition: trigger.c:3484
AfterTriggerEventList events
Definition: trigger.c:3483
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

3589 {
3590  Oid tgoid = evtshared->ats_tgoid;
3592  int i;
3593 
3594  /*
3595  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3596  * constraints declared NOT DEFERRABLE), the state is always false.
3597  */
3598  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3599  return false;
3600 
3601  /*
3602  * If constraint state exists, SET CONSTRAINTS might have been executed
3603  * either for this trigger or for all triggers.
3604  */
3605  if (state != NULL)
3606  {
3607  /* Check for SET CONSTRAINTS for this specific trigger. */
3608  for (i = 0; i < state->numstates; i++)
3609  {
3610  if (state->trigstates[i].sct_tgoid == tgoid)
3611  return state->trigstates[i].sct_tgisdeferred;
3612  }
3613 
3614  /* Check for SET CONSTRAINTS ALL. */
3615  if (state->all_isset)
3616  return state->all_isdeferred;
3617  }
3618 
3619  /*
3620  * Otherwise return the default state for the trigger.
3621  */
3622  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3623 }
TriggerEvent ats_event
Definition: trigger.c:3314
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:107
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:106
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3255
unsigned int Oid
Definition: postgres_ext.h:31
SetConstraintState state
Definition: trigger.c:3482
Definition: regguts.h:317
int i
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

3812 {
3813  AfterTriggerEventChunk *target = qs->events.head;
3814  ListCell *lc;
3815 
3816  Assert(target && target->next);
3817 
3818  /*
3819  * First, update any pointers in the per-table data, so that they won't be
3820  * dangling. Resetting obsoleted pointers to NULL will make
3821  * cancel_prior_stmt_triggers start from the list head, which is fine.
3822  */
3823  foreach(lc, qs->tables)
3824  {
3826 
3827  if (table->after_trig_done &&
3828  table->after_trig_events.tail == target)
3829  {
3830  table->after_trig_events.head = NULL;
3831  table->after_trig_events.tail = NULL;
3832  table->after_trig_events.tailfree = NULL;
3833  }
3834  }
3835 
3836  /* Now we can flush the head chunk */
3837  qs->events.head = target->next;
3838  pfree(target);
3839 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3376
struct AfterTriggerEventChunk * next
Definition: trigger.c:3363
void pfree(void *pointer)
Definition: mcxt.c:1057
AfterTriggerEventList after_trig_events
Definition: trigger.c:3520
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
AfterTriggerEventChunk * head
Definition: trigger.c:3375
AfterTriggerEventList events
Definition: trigger.c:3498

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 4508 of file trigger.c.

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

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

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

4805 {
4806  int my_level = GetCurrentTransactionNestLevel();
4808  AfterTriggerEvent event;
4809  AfterTriggerEventChunk *chunk;
4810  CommandId subxact_firing_id;
4811 
4812  /*
4813  * Pop the prior state if needed.
4814  */
4815  if (isCommit)
4816  {
4817  Assert(my_level < afterTriggers.maxtransdepth);
4818  /* If we saved a prior state, we don't need it anymore */
4819  state = afterTriggers.trans_stack[my_level].state;
4820  if (state != NULL)
4821  pfree(state);
4822  /* this avoids double pfree if error later: */
4823  afterTriggers.trans_stack[my_level].state = NULL;
4826  }
4827  else
4828  {
4829  /*
4830  * Aborting. It is possible subxact start failed before calling
4831  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
4832  * trans_stack levels that aren't there.
4833  */
4834  if (my_level >= afterTriggers.maxtransdepth)
4835  return;
4836 
4837  /*
4838  * Release query-level storage for queries being aborted, and restore
4839  * query_depth to its pre-subxact value. This assumes that a
4840  * subtransaction will not add events to query levels started in a
4841  * earlier transaction state.
4842  */
4844  {
4848  }
4851 
4852  /*
4853  * Restore the global deferred-event list to its former length,
4854  * discarding any events queued by the subxact.
4855  */
4857  &afterTriggers.trans_stack[my_level].events);
4858 
4859  /*
4860  * Restore the trigger state. If the saved state is NULL, then this
4861  * subxact didn't save it, so it doesn't need restoring.
4862  */
4863  state = afterTriggers.trans_stack[my_level].state;
4864  if (state != NULL)
4865  {
4867  afterTriggers.state = state;
4868  }
4869  /* this avoids double pfree if error later: */
4870  afterTriggers.trans_stack[my_level].state = NULL;
4871 
4872  /*
4873  * Scan for any remaining deferred events that were marked DONE or IN
4874  * PROGRESS by this subxact or a child, and un-mark them. We can
4875  * recognize such events because they have a firing ID greater than or
4876  * equal to the firing_counter value we saved at subtransaction start.
4877  * (This essentially assumes that the current subxact includes all
4878  * subxacts started after it.)
4879  */
4880  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
4882  {
4883  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4884 
4885  if (event->ate_flags &
4887  {
4888  if (evtshared->ats_firing_id >= subxact_firing_id)
4889  event->ate_flags &=
4891  }
4892  }
4893  }
4894 }
uint32 CommandId
Definition: c.h:601
AfterTriggersTransData * trans_stack
Definition: trigger.c:3492
TriggerFlags ate_flags
Definition: trigger.c:3326
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3301
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3388
AfterTriggerEventList events
Definition: trigger.c:3507
AfterTriggersQueryData * query_stack
Definition: trigger.c:3487
CommandId firing_counter
Definition: trigger.c:3509
#define GetTriggerSharedData(evt)
Definition: trigger.c:3351
void pfree(void *pointer)
Definition: mcxt.c:1057
SetConstraintState state
Definition: trigger.c:3482
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:857
#define Assert(condition)
Definition: c.h:804
Definition: regguts.h:317
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:3771
CommandId ats_firing_id
Definition: trigger.c:3317
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3302
AfterTriggerEventList events
Definition: trigger.c:3483
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4599
static AfterTriggersData afterTriggers
Definition: trigger.c:3526
SetConstraintState state
Definition: trigger.c:3506

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

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

4709 {
4710  /*
4711  * Forget the pending-events list.
4712  *
4713  * Since all the info is in TopTransactionContext or children thereof, we
4714  * don't really need to do anything to reclaim memory. However, the
4715  * pending-events list could be large, and so it's useful to discard it as
4716  * soon as possible --- especially if we are aborting because we ran out
4717  * of memory for the list!
4718  */
4720  {
4722  afterTriggers.event_cxt = NULL;
4723  afterTriggers.events.head = NULL;
4724  afterTriggers.events.tail = NULL;
4725  afterTriggers.events.tailfree = NULL;
4726  }
4727 
4728  /*
4729  * Forget any subtransaction state as well. Since this can't be very
4730  * large, we let the eventual reset of TopTransactionContext free the
4731  * memory instead of doing it here.
4732  */
4733  afterTriggers.trans_stack = NULL;
4735 
4736 
4737  /*
4738  * Forget the query stack and constraint-related state information. As
4739  * with the subtransaction state information, we don't bother freeing the
4740  * memory here.
4741  */
4742  afterTriggers.query_stack = NULL;
4744  afterTriggers.state = NULL;
4745 
4746  /* No more afterTriggers manipulation until next transaction starts. */
4748 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:212
AfterTriggersTransData * trans_stack
Definition: trigger.c:3492
AfterTriggersQueryData * query_stack
Definition: trigger.c:3487
AfterTriggerEventChunk * tail
Definition: trigger.c:3376
SetConstraintState state
Definition: trigger.c:3482
AfterTriggerEventChunk * head
Definition: trigger.c:3375
MemoryContext event_cxt
Definition: trigger.c:3484
AfterTriggerEventList events
Definition: trigger.c:3483
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

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

4907 {
4908  int init_depth = afterTriggers.maxquerydepth;
4909 
4911 
4912  if (afterTriggers.maxquerydepth == 0)
4913  {
4914  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
4915 
4918  new_alloc * sizeof(AfterTriggersQueryData));
4919  afterTriggers.maxquerydepth = new_alloc;
4920  }
4921  else
4922  {
4923  /* repalloc will keep the stack in the same context */
4924  int old_alloc = afterTriggers.maxquerydepth;
4925  int new_alloc = Max(afterTriggers.query_depth + 1,
4926  old_alloc * 2);
4927 
4930  new_alloc * sizeof(AfterTriggersQueryData));
4931  afterTriggers.maxquerydepth = new_alloc;
4932  }
4933 
4934  /* Initialize new array entries to empty */
4935  while (init_depth < afterTriggers.maxquerydepth)
4936  {
4938 
4939  qs->events.head = NULL;
4940  qs->events.tail = NULL;
4941  qs->events.tailfree = NULL;
4942  qs->fdw_tuplestore = NULL;
4943  qs->tables = NIL;
4944 
4945  ++init_depth;
4946  }
4947 }
#define NIL
Definition: pg_list.h:65
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggersQueryData * query_stack
Definition: trigger.c:3487
AfterTriggerEventChunk * tail
Definition: trigger.c:3376
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3499
#define Max(x, y)
Definition: c.h:980
#define Assert(condition)
Definition: c.h:804
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1070
AfterTriggerEventChunk * head
Definition: trigger.c:3375
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
AfterTriggerEventList events
Definition: trigger.c:3498
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

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

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

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

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

4653 {
4654  AfterTriggerEventList *events;
4655  bool snap_pushed = false;
4656 
4657  /* Must not be inside a query */
4659 
4660  /*
4661  * If there are any triggers to fire, make sure we have set a snapshot for
4662  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4663  * can't assume ActiveSnapshot is valid on entry.)
4664  */
4665  events = &afterTriggers.events;
4666  if (events->head != NULL)
4667  {
4669  snap_pushed = true;
4670  }
4671 
4672  /*
4673  * Run all the remaining triggers. Loop until they are all gone, in case
4674  * some trigger queues more for us to do.
4675  */
4676  while (afterTriggerMarkEvents(events, NULL, false))
4677  {
4678  CommandId firing_id = afterTriggers.firing_counter++;
4679 
4680  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4681  break; /* all fired */
4682  }
4683 
4684  /*
4685  * We don't bother freeing the event list, since it will go away anyway
4686  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4687  */
4688 
4689  if (snap_pushed)
4691 }
uint32 CommandId
Definition: c.h:601
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4163
void PopActiveSnapshot(void)
Definition: snapmgr.c:759
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:680
CommandId firing_counter
Definition: trigger.c:3481
#define Assert(condition)
Definition: c.h:804
AfterTriggerEventChunk * head
Definition: trigger.c:3375
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4079
AfterTriggerEventList events
Definition: trigger.c:3483
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 3750 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

3751 {
3752  AfterTriggerEventChunk *chunk;
3753 
3754  while ((chunk = events->head) != NULL)
3755  {
3756  events->head = chunk->next;
3757  pfree(chunk);
3758  }
3759  events->tail = NULL;
3760  events->tailfree = NULL;
3761 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3376
struct AfterTriggerEventChunk * next
Definition: trigger.c:3363
void pfree(void *pointer)
Definition: mcxt.c:1057
AfterTriggerEventChunk * head
Definition: trigger.c:3375

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 4599 of file trigger.c.

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

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

4600 {
4601  Tuplestorestate *ts;
4602  List *tables;
4603  ListCell *lc;
4604 
4605  /* Drop the trigger events */
4607 
4608  /* Drop FDW tuplestore if any */
4609  ts = qs->fdw_tuplestore;
4610  qs->fdw_tuplestore = NULL;
4611  if (ts)
4612  tuplestore_end(ts);
4613 
4614  /* Release per-table subsidiary storage */
4615  tables = qs->tables;
4616  foreach(lc, tables)
4617  {
4619 
4620  ts = table->old_tuplestore;
4621  table->old_tuplestore = NULL;
4622  if (ts)
4623  tuplestore_end(ts);
4624  ts = table->new_tuplestore;
4625  table->new_tuplestore = NULL;
4626  if (ts)
4627  tuplestore_end(ts);
4628  }
4629 
4630  /*
4631  * Now free the AfterTriggersTableData structs and list cells. Reset list
4632  * pointer first; if list_free_deep somehow gets an error, better to leak
4633  * that storage than have an infinite loop.
4634  */
4635  qs->tables = NIL;
4636  list_free_deep(tables);
4637 }
#define NIL
Definition: pg_list.h:65
Tuplestorestate * old_tuplestore
Definition: trigger.c:3521
void list_free_deep(List *list)
Definition: list.c:1405
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3750
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3499
Tuplestorestate * new_tuplestore
Definition: trigger.c:3522
#define lfirst(lc)
Definition: pg_list.h:169
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3498
Definition: pg_list.h:50

◆ afterTriggerInvokeEvents()

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

Definition at line 4163 of file trigger.c.

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

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

4167 {
4168  bool all_fired = true;
4169  AfterTriggerEventChunk *chunk;
4170  MemoryContext per_tuple_context;
4171  bool local_estate = false;
4172  ResultRelInfo *rInfo = NULL;
4173  Relation rel = NULL;
4174  TriggerDesc *trigdesc = NULL;
4175  FmgrInfo *finfo = NULL;
4176  Instrumentation *instr = NULL;
4177  TupleTableSlot *slot1 = NULL,
4178  *slot2 = NULL;
4179 
4180  /* Make a local EState if need be */
4181  if (estate == NULL)
4182  {
4183  estate = CreateExecutorState();
4184  local_estate = true;
4185  }
4186 
4187  /* Make a per-tuple memory context for trigger function calls */
4188  per_tuple_context =
4190  "AfterTriggerTupleContext",
4192 
4193  for_each_chunk(chunk, *events)
4194  {
4195  AfterTriggerEvent event;
4196  bool all_fired_in_chunk = true;
4197 
4198  for_each_event(event, chunk)
4199  {
4200  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4201 
4202  /*
4203  * Is it one for me to fire?
4204  */
4205  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4206  evtshared->ats_firing_id == firing_id)
4207  {
4208  /*
4209  * So let's fire it... but first, find the correct relation if
4210  * this is not the same relation as before.
4211  */
4212  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4213  {
4214  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4215  rel = rInfo->ri_RelationDesc;
4216  trigdesc = rInfo->ri_TrigDesc;
4217  finfo = rInfo->ri_TrigFunctions;
4218  instr = rInfo->ri_TrigInstrument;
4219  if (slot1 != NULL)
4220  {
4223  slot1 = slot2 = NULL;
4224  }
4225  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4226  {
4227  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4229  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4231  }
4232  if (trigdesc == NULL) /* should not happen */
4233  elog(ERROR, "relation %u has no triggers",
4234  evtshared->ats_relid);
4235  }
4236 
4237  /*
4238  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4239  * still set, so recursive examinations of the event list
4240  * won't try to re-fire it.
4241  */
4242  AfterTriggerExecute(estate, event, rInfo, trigdesc, finfo, instr,
4243  per_tuple_context, slot1, slot2);
4244 
4245  /*
4246  * Mark the event as done.
4247  */
4248  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4249  event->ate_flags |= AFTER_TRIGGER_DONE;
4250  }
4251  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4252  {
4253  /* something remains to be done */
4254  all_fired = all_fired_in_chunk = false;
4255  }
4256  }
4257 
4258  /* Clear the chunk if delete_ok and nothing left of interest */
4259  if (delete_ok && all_fired_in_chunk)
4260  {
4261  chunk->freeptr = CHUNK_DATA_START(chunk);
4262  chunk->endfree = chunk->endptr;
4263 
4264  /*
4265  * If it's last chunk, must sync event list's tailfree too. Note
4266  * that delete_ok must NOT be passed as true if there could be
4267  * additional AfterTriggerEventList values pointing at this event
4268  * list, since we'd fail to fix their copies of tailfree.
4269  */
4270  if (chunk == events->tail)
4271  events->tailfree = chunk->freeptr;
4272  }
4273  }
4274  if (slot1 != NULL)
4275  {
4278  }
4279 
4280  /* Release working resources */
4281  MemoryContextDelete(per_tuple_context);
4282 
4283  if (local_estate)
4284  {
4285  ExecCloseResultRelations(estate);
4286  ExecResetTupleTable(estate->es_tupleTable, false);
4287  FreeExecutorState(estate);
4288  }
4289 
4290  return all_fired;
4291 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:415
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:212
#define AllocSetContextCreate
Definition: memutils.h:170
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1208
TriggerFlags ate_flags
Definition: trigger.c:3326
void ExecCloseResultRelations(EState *estate)
Definition: execMain.c:1416
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3301
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3370
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:436
AfterTriggerEventChunk * tail
Definition: trigger.c:3376
Form_pg_class rd_rel
Definition: rel.h:110
#define GetTriggerSharedData(evt)
Definition: trigger.c:3351
#define for_each_event(eptr, cptr)
Definition: trigger.c:3383
void FreeExecutorState(EState *estate)
Definition: execUtils.c:186
#define ERROR
Definition: elog.h:45
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:427
EState * CreateExecutorState(void)
Definition: execUtils.c:90
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1259
List * es_tupleTable
Definition: execnodes.h:574
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1161
TupleDesc rd_att
Definition: rel.h:111
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3381
CommandId ats_firing_id
Definition: trigger.c:3317
#define elog(elevel,...)
Definition: elog.h:228
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3302
#define RelationGetRelid(relation)
Definition: rel.h:457
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:430
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:3865

◆ afterTriggerMarkEvents()

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

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

4082 {
4083  bool found = false;
4084  bool deferred_found = false;
4085  AfterTriggerEvent event;
4086  AfterTriggerEventChunk *chunk;
4087 
4088  for_each_event_chunk(event, chunk, *events)
4089  {
4090  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4091  bool defer_it = false;
4092 
4093  if (!(event->ate_flags &
4095  {
4096  /*
4097  * This trigger hasn't been called or scheduled yet. Check if we
4098  * should call it now.
4099  */
4100  if (immediate_only && afterTriggerCheckState(evtshared))
4101  {
4102  defer_it = true;
4103  }
4104  else
4105  {
4106  /*
4107  * Mark it as to be fired in this firing cycle.
4108  */
4110  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4111  found = true;
4112  }
4113  }
4114 
4115  /*
4116  * If it's deferred, move it to move_list, if requested.
4117  */
4118  if (defer_it && move_list != NULL)
4119  {
4120  deferred_found = true;
4121  /* add it to move_list */
4122  afterTriggerAddEvent(move_list, event, evtshared);
4123  /* mark original copy "done" so we don't do it again */
4124  event->ate_flags |= AFTER_TRIGGER_DONE;
4125  }
4126  }
4127 
4128  /*
4129  * We could allow deferred triggers if, before the end of the
4130  * security-restricted operation, we were to verify that a SET CONSTRAINTS
4131  * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4132  */
4133  if (deferred_found && InSecurityRestrictedOperation())
4134  ereport(ERROR,
4135  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4136  errmsg("cannot fire deferred trigger within security-restricted operation")));
4137 
4138  return found;
4139 }
TriggerFlags ate_flags
Definition: trigger.c:3326
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3301
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3388
int errcode(int sqlerrcode)
Definition: elog.c:704
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3588
#define GetTriggerSharedData(evt)
Definition: trigger.c:3351
#define ERROR
Definition: elog.h:45
CommandId firing_counter
Definition: trigger.c:3481
#define ereport(elevel,...)
Definition: elog.h:155
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:608
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3634
CommandId ats_firing_id
Definition: trigger.c:3317
int errmsg(const char *fmt,...)
Definition: elog.c:915
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3302
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

5344 {
5345  AfterTriggerEvent event;
5346  AfterTriggerEventChunk *chunk;
5347  int depth;
5348 
5349  /* Scan queued events */
5351  {
5352  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5353 
5354  /*
5355  * We can ignore completed events. (Even if a DONE flag is rolled
5356  * back by subxact abort, it's OK because the effects of the TRUNCATE
5357  * or whatever must get rolled back too.)
5358  */
5359  if (event->ate_flags & AFTER_TRIGGER_DONE)
5360  continue;
5361 
5362  if (evtshared->ats_relid == relid)
5363  return true;
5364  }
5365 
5366  /*
5367  * Also scan events queued by incomplete queries. This could only matter
5368  * if TRUNCATE/etc is executed by a function or trigger within an updating
5369  * query on the same relation, which is pretty perverse, but let's check.
5370  */
5371  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5372  {
5374  {
5375  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5376 
5377  if (event->ate_flags & AFTER_TRIGGER_DONE)
5378  continue;
5379 
5380  if (evtshared->ats_relid == relid)
5381  return true;
5382  }
5383  }
5384 
5385  return false;
5386 }
TriggerFlags ate_flags
Definition: trigger.c:3326
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3301
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3388
AfterTriggersQueryData * query_stack
Definition: trigger.c:3487
#define GetTriggerSharedData(evt)
Definition: trigger.c:3351
AfterTriggerEventList events
Definition: trigger.c:3483
AfterTriggerEventList events
Definition: trigger.c:3498
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

◆ afterTriggerRestoreEventList()

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

Definition at line 3771 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

3773 {
3774  AfterTriggerEventChunk *chunk;
3775  AfterTriggerEventChunk *next_chunk;
3776 
3777  if (old_events->tail == NULL)
3778  {
3779  /* restoring to a completely empty state, so free everything */
3780  afterTriggerFreeEventList(events);
3781  }
3782  else
3783  {
3784  *events = *old_events;
3785  /* free any chunks after the last one we want to keep */
3786  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
3787  {
3788  next_chunk = chunk->next;
3789  pfree(chunk);
3790  }
3791  /* and clean up the tail chunk to be the right length */
3792  events->tail->next = NULL;
3793  events->tail->freeptr = events->tailfree;
3794 
3795  /*
3796  * We don't make any effort to remove now-unused shared data records.
3797  * They might still be useful, anyway.
3798  */
3799  }
3800 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3376
struct AfterTriggerEventChunk * next
Definition: trigger.c:3363
void pfree(void *pointer)
Definition: mcxt.c:1057
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3750

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

References AFTER_TRIGGER_1CTID, AFTER_TRIGGER_2CTID, AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_INITDEFERRED, afterTriggerAddEvent(), AfterTriggerEnlargeQueryState(), Assert, AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_modifiedcols, AfterTriggerSharedData::ats_relid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, TupleConversionMap::attrMap, cancel_prior_stmt_triggers(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, EState::es_tupleTable, AfterTriggersQueryData::events, ExecAllocTableSlot(), execute_attr_map_slot(), GetCurrentFDWTuplestore(), i, ItemPointerCopy, ItemPointerSetInvalid, list_member_oid(), AfterTriggersData::maxquerydepth, AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, TupleConversionMap::outdesc, AfterTriggersData::query_depth, AfterTriggersData::query_stack, RelationData::rd_rel, RelationGetRelid, ResultRelInfo::ri_ChildToRootMap, RI_FKey_fk_upd_check_required(), RI_FKey_pk_upd_check_required(), RI_FKey_trigger_type(), ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, RI_TRIGGER_FK, RI_TRIGGER_NONE, RI_TRIGGER_PK, AfterTriggersTableData::storeslot, TransitionCaptureState::tcs_delete_old_table, TransitionCaptureState::tcs_insert_new_table, TransitionCaptureState::tcs_original_insert_tuple, TransitionCaptureState::tcs_private, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, Trigger::tgconstrindid, Trigger::tgdeferrable, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgtype, TriggerDesc::trig_delete_after_row, TriggerDesc::trig_insert_after_row, TriggerDesc::trig_update_after_row, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_TRUNCATE, TRIGGER_EVENT_UPDATE, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tid, TTSOpsVirtual, TupIsNull, and tuplestore_puttupleslot().

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

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

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5028 of file trigger.c.

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

Referenced by standard_ProcessUtility().

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

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

5747 {
5748  bool result;
5749  AfterTriggersTableData *table;
5750 
5751  /* Check state, like AfterTriggerSaveEvent. */
5752  if (afterTriggers.query_depth < 0)
5753  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
5754 
5755  /* Be sure we have enough space to record events at this query depth. */
5758 
5759  /*
5760  * We keep this state in the AfterTriggersTableData that also holds
5761  * transition tables for the relation + operation. In this way, if we are
5762  * forced to make a new set of transition tables because more tuples get
5763  * entered after we've already fired triggers, we will allow a new set of
5764  * statement triggers to get queued.
5765  */
5766  table = GetAfterTriggersTableData(relid, cmdType);
5767  result = table->before_trig_done;
5768  table->before_trig_done = true;
5769  return result;
5770 }
#define ERROR
Definition: elog.h:45
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4307
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4906
#define elog(elevel,...)
Definition: elog.h:228
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

◆ cancel_prior_stmt_triggers()

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

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

5793 {
5794  AfterTriggersTableData *table;
5796 
5797  /*
5798  * We keep this state in the AfterTriggersTableData that also holds
5799  * transition tables for the relation + operation. In this way, if we are
5800  * forced to make a new set of transition tables because more tuples get
5801  * entered after we've already fired triggers, we will allow a new set of
5802  * statement triggers to get queued without canceling the old ones.
5803  */
5804  table = GetAfterTriggersTableData(relid, cmdType);
5805 
5806  if (table->after_trig_done)
5807  {
5808  /*
5809  * We want to start scanning from the tail location that existed just
5810  * before we inserted any statement triggers. But the events list
5811  * might've been entirely empty then, in which case scan from the
5812  * current head.
5813  */
5814  AfterTriggerEvent event;
5815  AfterTriggerEventChunk *chunk;
5816 
5817  if (table->after_trig_events.tail)
5818  {
5819  chunk = table->after_trig_events.tail;
5820  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
5821  }
5822  else
5823  {
5824  chunk = qs->events.head;
5825  event = NULL;
5826  }
5827 
5828  for_each_chunk_from(chunk)
5829  {
5830  if (event == NULL)
5831  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
5832  for_each_event_from(event, chunk)
5833  {
5834  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5835 
5836  /*
5837  * Exit loop when we reach events that aren't AS triggers for
5838  * the target relation.
5839  */
5840  if (evtshared->ats_relid != relid)
5841  goto done;
5842  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
5843  goto done;
5844  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
5845  goto done;
5846  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
5847  goto done;
5848  /* OK, mark it DONE */
5849  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
5850  event->ate_flags |= AFTER_TRIGGER_DONE;
5851  }
5852  /* signal we must reinitialize event ptr for next chunk */
5853  event = NULL;
5854  }
5855  }
5856 done:
5857 
5858  /* In any case, save current insertion point for next time */
5859  table->after_trig_done = true;
5860  table->after_trig_events = qs->events;
5861 }
TriggerEvent ats_event
Definition: trigger.c:3314
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3301
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3370
AfterTriggersQueryData * query_stack
Definition: trigger.c:3487
AfterTriggerEventChunk * tail
Definition: trigger.c:3376
#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:3351
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4307
AfterTriggerEventList after_trig_events
Definition: trigger.c:3520
#define for_each_chunk_from(cptr)
Definition: trigger.c:3392
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3394
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3322
AfterTriggerEventChunk * head
Definition: trigger.c:3375
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3302
AfterTriggerEventList events
Definition: trigger.c:3498
static AfterTriggersData afterTriggers
Definition: trigger.c:3526

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

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

1866 {
1867  TriggerDesc *newdesc;
1868  Trigger *trigger;
1869  int i;
1870 
1871  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
1872  return NULL;
1873 
1874  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
1875  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
1876 
1877  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
1878  memcpy(trigger, trigdesc->triggers,
1879  trigdesc->numtriggers * sizeof(Trigger));
1880  newdesc->triggers = trigger;
1881 
1882  for (i = 0; i < trigdesc->numtriggers; i++)
1883  {
1884  trigger->tgname = pstrdup(trigger->tgname);
1885  if (trigger->tgnattr > 0)
1886  {
1887  int16 *newattr;
1888 
1889  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
1890  memcpy(newattr, trigger->tgattr,
1891  trigger->tgnattr * sizeof(int16));
1892  trigger->tgattr = newattr;
1893  }
1894  if (trigger->tgnargs > 0)
1895  {
1896  char **newargs;
1897  int16 j;
1898 
1899  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
1900  for (j = 0; j < trigger->tgnargs; j++)
1901  newargs[j] = pstrdup(trigger->tgargs[j]);
1902  trigger->tgargs = newargs;
1903  }
1904  if (trigger->tgqual)
1905  trigger->tgqual = pstrdup(trigger->tgqual);
1906  if (trigger->tgoldtable)
1907  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
1908  if (trigger->tgnewtable)
1909  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
1910  trigger++;
1911  }
1912 
1913  return newdesc;
1914 }
signed short int16
Definition: c.h:428
char * pstrdup(const char *in)
Definition: mcxt.c:1187
char * tgqual
Definition: reltrigger.h:42
char * tgname
Definition: reltrigger.h:27
Trigger * triggers
Definition: reltrigger.h:49
int numtriggers
Definition: reltrigger.h:50
char ** tgargs
Definition: reltrigger.h:41
int16 * tgattr
Definition: reltrigger.h:40
char * tgnewtable
Definition: reltrigger.h:44
int16 tgnattr
Definition: reltrigger.h:39
void * palloc(Size size)
Definition: mcxt.c:950
int i
int16 tgnargs
Definition: reltrigger.h:38
char * tgoldtable
Definition: reltrigger.h:43

◆ CreateTrigger()

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

Definition at line 150 of file trigger.c.

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

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

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

◆ EnableDisableTrigger()

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

Definition at line 1534 of file trigger.c.

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

Referenced by ATExecEnableDisableTrigger().

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

◆ ExecARDeleteTriggers()

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

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

2546 {
2547  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2548  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2549 
2550  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2551  (transition_capture && transition_capture->tcs_delete_old_table))
2552  {
2553  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2554  if (fdw_trigtuple == NULL)
2555  GetTupleForTrigger(estate,
2556  NULL,
2557  relinfo,
2558  tupleid,
2560  slot,
2561  NULL);
2562  else
2563  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2564 
2566  true, slot, NULL, NIL, NULL,
2567  transition_capture);
2568  }
2569 }
#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:2992
#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:1439
bool trig_delete_after_row
Definition: reltrigger.h:67
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:427
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:5413
#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 2312 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().

2315 {
2316  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2317 
2318  if ((trigdesc && trigdesc->trig_insert_after_row) ||
2319  (transition_capture && transition_capture->tcs_insert_new_table))
2321  true, NULL, slot,
2322  recheckIndexes, NULL,
2323  transition_capture);
2324 }
bool trig_insert_after_row
Definition: reltrigger.h:57
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:427
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:5413
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:91

◆ ExecARUpdateTriggers()

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