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 TupleTableSlotGetAfterTriggersStoreSlot (AfterTriggersTableData *table, TupleDesc tupdesc)
 
static void AfterTriggerFreeQuery (AfterTriggersQueryData *qs)
 
static SetConstraintState SetConstraintStateCreate (int numalloc)
 
static SetConstraintState SetConstraintStateCopy (SetConstraintState state)
 
static SetConstraintState SetConstraintStateAddItem (SetConstraintState state, Oid tgoid, bool tgisdeferred)
 
static void cancel_prior_stmt_triggers (Oid relid, CmdType cmdType, int tgevent)
 
static TuplestorestateGetCurrentFDWTuplestore (void)
 
static bool afterTriggerCheckState (AfterTriggerShared evtshared)
 
static void afterTriggerAddEvent (AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
 
static void afterTriggerFreeEventList (AfterTriggerEventList *events)
 
static void afterTriggerRestoreEventList (AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
 
static void afterTriggerDeleteHeadEventChunk (AfterTriggersQueryData *qs)
 
static bool afterTriggerMarkEvents (AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
 
static bool afterTriggerInvokeEvents (AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
 
TransitionCaptureStateMakeTransitionCaptureState (TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
 
void AfterTriggerBeginXact (void)
 
void AfterTriggerBeginQuery (void)
 
void AfterTriggerEndQuery (EState *estate)
 
void AfterTriggerFireDeferred (void)
 
void AfterTriggerEndXact (bool isCommit)
 
void AfterTriggerBeginSubXact (void)
 
void AfterTriggerEndSubXact (bool isCommit)
 
void AfterTriggerSetState (ConstraintsSetStmt *stmt)
 
bool AfterTriggerPendingOnRel (Oid relid)
 
Datum pg_trigger_depth (PG_FUNCTION_ARGS)
 

Variables

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

Macro Definition Documentation

◆ AFTER_TRIGGER_1CTID

#define AFTER_TRIGGER_1CTID   0x40000000

Definition at line 3310 of file trigger.c.

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3312 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 3385 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 3396 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:3374
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3348
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3326

Definition at line 3387 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:3348
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3326

Definition at line 3398 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:3312
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3310
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3311

Definition at line 3348 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3326 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3314 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3479 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3481 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3480 of file trigger.c.

◆ SetConstraintState

Definition at line 3262 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3241 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3302 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

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

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4519 of file trigger.c.

References AfterTriggersData::query_depth.

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

4520 {
4521  /* Increase the query stack depth */
4523 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3530

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

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

4790 {
4791  int my_level = GetCurrentTransactionNestLevel();
4792 
4793  /*
4794  * Allocate more space in the trans_stack if needed. (Note: because the
4795  * minimum nest level of a subtransaction is 2, we waste the first couple
4796  * entries of the array; not worth the notational effort to avoid it.)
4797  */
4798  while (my_level >= afterTriggers.maxtransdepth)
4799  {
4800  if (afterTriggers.maxtransdepth == 0)
4801  {
4802  /* Arbitrarily initialize for max of 8 subtransaction levels */
4805  8 * sizeof(AfterTriggersTransData));
4807  }
4808  else
4809  {
4810  /* repalloc will keep the stack in the same context */
4811  int new_alloc = afterTriggers.maxtransdepth * 2;
4812 
4815  new_alloc * sizeof(AfterTriggersTransData));
4816  afterTriggers.maxtransdepth = new_alloc;
4817  }
4818  }
4819 
4820  /*
4821  * Push the current information into the stack. The SET CONSTRAINTS state
4822  * is not saved until/unless changed. Likewise, we don't make a
4823  * per-subtransaction event context until needed.
4824  */
4825  afterTriggers.trans_stack[my_level].state = NULL;
4829 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3496
MemoryContext TopTransactionContext
Definition: mcxt.c:53
AfterTriggerEventList events
Definition: trigger.c:3511
CommandId firing_counter
Definition: trigger.c:3513
CommandId firing_counter
Definition: trigger.c:3485
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:857
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1182
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
AfterTriggerEventList events
Definition: trigger.c:3487
static AfterTriggersData afterTriggers
Definition: trigger.c:3530
SetConstraintState state
Definition: trigger.c:3510

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

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

4488 {
4489  /*
4490  * Initialize after-trigger state structure to empty
4491  */
4492  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4494 
4495  /*
4496  * Verify that there is no leftover state remaining. If these assertions
4497  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4498  * up properly.
4499  */
4500  Assert(afterTriggers.state == NULL);
4501  Assert(afterTriggers.query_stack == NULL);
4503  Assert(afterTriggers.event_cxt == NULL);
4504  Assert(afterTriggers.events.head == NULL);
4505  Assert(afterTriggers.trans_stack == NULL);
4507 }
uint32 CommandId
Definition: c.h:601
AfterTriggersTransData * trans_stack
Definition: trigger.c:3496
AfterTriggersQueryData * query_stack
Definition: trigger.c:3491
SetConstraintState state
Definition: trigger.c:3486
CommandId firing_counter
Definition: trigger.c:3485
#define Assert(condition)
Definition: c.h:804
AfterTriggerEventChunk * head
Definition: trigger.c:3379
MemoryContext event_cxt
Definition: trigger.c:3488
AfterTriggerEventList events
Definition: trigger.c:3487
static AfterTriggersData afterTriggers
Definition: trigger.c:3530

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

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

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

3818 {
3819  AfterTriggerEventChunk *target = qs->events.head;
3820  ListCell *lc;
3821 
3822  Assert(target && target->next);
3823 
3824  /*
3825  * First, update any pointers in the per-table data, so that they won't be
3826  * dangling. Resetting obsoleted pointers to NULL will make
3827  * cancel_prior_stmt_triggers start from the list head, which is fine.
3828  */
3829  foreach(lc, qs->tables)
3830  {
3832 
3833  if (table->after_trig_done &&
3834  table->after_trig_events.tail == target)
3835  {
3836  table->after_trig_events.head = NULL;
3837  table->after_trig_events.tail = NULL;
3838  table->after_trig_events.tailfree = NULL;
3839  }
3840  }
3841 
3842  /* Now we can flush the head chunk */
3843  qs->events.head = target->next;
3844  pfree(target);
3845 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3380
struct AfterTriggerEventChunk * next
Definition: trigger.c:3367
void pfree(void *pointer)
Definition: mcxt.c:1169
AfterTriggerEventList after_trig_events
Definition: trigger.c:3524
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
AfterTriggerEventChunk * head
Definition: trigger.c:3379
AfterTriggerEventList events
Definition: trigger.c:3502

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 4539 of file trigger.c.

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

Referenced by CopyFrom(), ExecuteTruncateGuts(), finish_estate(), and standard_ExecutorFinish().

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

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

4838 {
4839  int my_level = GetCurrentTransactionNestLevel();
4841  AfterTriggerEvent event;
4842  AfterTriggerEventChunk *chunk;
4843  CommandId subxact_firing_id;
4844 
4845  /*
4846  * Pop the prior state if needed.
4847  */
4848  if (isCommit)
4849  {
4850  Assert(my_level < afterTriggers.maxtransdepth);
4851  /* If we saved a prior state, we don't need it anymore */
4852  state = afterTriggers.trans_stack[my_level].state;
4853  if (state != NULL)
4854  pfree(state);
4855  /* this avoids double pfree if error later: */
4856  afterTriggers.trans_stack[my_level].state = NULL;
4859  }
4860  else
4861  {
4862  /*
4863  * Aborting. It is possible subxact start failed before calling
4864  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
4865  * trans_stack levels that aren't there.
4866  */
4867  if (my_level >= afterTriggers.maxtransdepth)
4868  return;
4869 
4870  /*
4871  * Release query-level storage for queries being aborted, and restore
4872  * query_depth to its pre-subxact value. This assumes that a
4873  * subtransaction will not add events to query levels started in a
4874  * earlier transaction state.
4875  */
4877  {
4881  }
4884 
4885  /*
4886  * Restore the global deferred-event list to its former length,
4887  * discarding any events queued by the subxact.
4888  */
4890  &afterTriggers.trans_stack[my_level].events);
4891 
4892  /*
4893  * Restore the trigger state. If the saved state is NULL, then this
4894  * subxact didn't save it, so it doesn't need restoring.
4895  */
4896  state = afterTriggers.trans_stack[my_level].state;
4897  if (state != NULL)
4898  {
4900  afterTriggers.state = state;
4901  }
4902  /* this avoids double pfree if error later: */
4903  afterTriggers.trans_stack[my_level].state = NULL;
4904 
4905  /*
4906  * Scan for any remaining deferred events that were marked DONE or IN
4907  * PROGRESS by this subxact or a child, and un-mark them. We can
4908  * recognize such events because they have a firing ID greater than or
4909  * equal to the firing_counter value we saved at subtransaction start.
4910  * (This essentially assumes that the current subxact includes all
4911  * subxacts started after it.)
4912  */
4913  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
4915  {
4916  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4917 
4918  if (event->ate_flags &
4920  {
4921  if (evtshared->ats_firing_id >= subxact_firing_id)
4922  event->ate_flags &=
4924  }
4925  }
4926  }
4927 }
uint32 CommandId
Definition: c.h:601
AfterTriggersTransData * trans_stack
Definition: trigger.c:3496
TriggerFlags ate_flags
Definition: trigger.c:3330
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3305
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3392
AfterTriggerEventList events
Definition: trigger.c:3511
AfterTriggersQueryData * query_stack
Definition: trigger.c:3491
CommandId firing_counter
Definition: trigger.c:3513
#define GetTriggerSharedData(evt)
Definition: trigger.c:3355
void pfree(void *pointer)
Definition: mcxt.c:1169
SetConstraintState state
Definition: trigger.c:3486
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:3777
CommandId ats_firing_id
Definition: trigger.c:3321
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3306
AfterTriggerEventList events
Definition: trigger.c:3487
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4630
static AfterTriggersData afterTriggers
Definition: trigger.c:3530
SetConstraintState state
Definition: trigger.c:3510

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

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

4742 {
4743  /*
4744  * Forget the pending-events list.
4745  *
4746  * Since all the info is in TopTransactionContext or children thereof, we
4747  * don't really need to do anything to reclaim memory. However, the
4748  * pending-events list could be large, and so it's useful to discard it as
4749  * soon as possible --- especially if we are aborting because we ran out
4750  * of memory for the list!
4751  */
4753  {
4755  afterTriggers.event_cxt = NULL;
4756  afterTriggers.events.head = NULL;
4757  afterTriggers.events.tail = NULL;
4758  afterTriggers.events.tailfree = NULL;
4759  }
4760 
4761  /*
4762  * Forget any subtransaction state as well. Since this can't be very
4763  * large, we let the eventual reset of TopTransactionContext free the
4764  * memory instead of doing it here.
4765  */
4766  afterTriggers.trans_stack = NULL;
4768 
4769 
4770  /*
4771  * Forget the query stack and constraint-related state information. As
4772  * with the subtransaction state information, we don't bother freeing the
4773  * memory here.
4774  */
4775  afterTriggers.query_stack = NULL;
4777  afterTriggers.state = NULL;
4778 
4779  /* No more afterTriggers manipulation until next transaction starts. */
4781 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
AfterTriggersTransData * trans_stack
Definition: trigger.c:3496
AfterTriggersQueryData * query_stack
Definition: trigger.c:3491
AfterTriggerEventChunk * tail
Definition: trigger.c:3380
SetConstraintState state
Definition: trigger.c:3486
AfterTriggerEventChunk * head
Definition: trigger.c:3379
MemoryContext event_cxt
Definition: trigger.c:3488
AfterTriggerEventList events
Definition: trigger.c:3487
static AfterTriggersData afterTriggers
Definition: trigger.c:3530

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

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

4940 {
4941  int init_depth = afterTriggers.maxquerydepth;
4942 
4944 
4945  if (afterTriggers.maxquerydepth == 0)
4946  {
4947  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
4948 
4951  new_alloc * sizeof(AfterTriggersQueryData));
4952  afterTriggers.maxquerydepth = new_alloc;
4953  }
4954  else
4955  {
4956  /* repalloc will keep the stack in the same context */
4957  int old_alloc = afterTriggers.maxquerydepth;
4958  int new_alloc = Max(afterTriggers.query_depth + 1,
4959  old_alloc * 2);
4960 
4963  new_alloc * sizeof(AfterTriggersQueryData));
4964  afterTriggers.maxquerydepth = new_alloc;
4965  }
4966 
4967  /* Initialize new array entries to empty */
4968  while (init_depth < afterTriggers.maxquerydepth)
4969  {
4971 
4972  qs->events.head = NULL;
4973  qs->events.tail = NULL;
4974  qs->events.tailfree = NULL;
4975  qs->fdw_tuplestore = NULL;
4976  qs->tables = NIL;
4977 
4978  ++init_depth;
4979  }
4980 }
#define NIL
Definition: pg_list.h:65
MemoryContext TopTransactionContext
Definition: mcxt.c:53
AfterTriggersQueryData * query_stack
Definition: trigger.c:3491
AfterTriggerEventChunk * tail
Definition: trigger.c:3380
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3503
#define Max(x, y)
Definition: c.h:980
#define Assert(condition)
Definition: c.h:804
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1182
AfterTriggerEventChunk * head
Definition: trigger.c:3379
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
AfterTriggerEventList events
Definition: trigger.c:3502
static AfterTriggersData afterTriggers
Definition: trigger.c:3530

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

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

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

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

4686 {
4687  AfterTriggerEventList *events;
4688  bool snap_pushed = false;
4689 
4690  /* Must not be inside a query */
4692 
4693  /*
4694  * If there are any triggers to fire, make sure we have set a snapshot for
4695  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4696  * can't assume ActiveSnapshot is valid on entry.)
4697  */
4698  events = &afterTriggers.events;
4699  if (events->head != NULL)
4700  {
4702  snap_pushed = true;
4703  }
4704 
4705  /*
4706  * Run all the remaining triggers. Loop until they are all gone, in case
4707  * some trigger queues more for us to do.
4708  */
4709  while (afterTriggerMarkEvents(events, NULL, false))
4710  {
4711  CommandId firing_id = afterTriggers.firing_counter++;
4712 
4713  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4714  break; /* all fired */
4715  }
4716 
4717  /*
4718  * We don't bother freeing the event list, since it will go away anyway
4719  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4720  */
4721 
4722  if (snap_pushed)
4724 }
uint32 CommandId
Definition: c.h:601
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4169
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:3485
#define Assert(condition)
Definition: c.h:804
AfterTriggerEventChunk * head
Definition: trigger.c:3379
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4085
AfterTriggerEventList events
Definition: trigger.c:3487
static AfterTriggersData afterTriggers
Definition: trigger.c:3530

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 3756 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

3757 {
3758  AfterTriggerEventChunk *chunk;
3759 
3760  while ((chunk = events->head) != NULL)
3761  {
3762  events->head = chunk->next;
3763  pfree(chunk);
3764  }
3765  events->tail = NULL;
3766  events->tailfree = NULL;
3767 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3380
struct AfterTriggerEventChunk * next
Definition: trigger.c:3367
void pfree(void *pointer)
Definition: mcxt.c:1169
AfterTriggerEventChunk * head
Definition: trigger.c:3379

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 4630 of file trigger.c.

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

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

4631 {
4632  Tuplestorestate *ts;
4633  List *tables;
4634  ListCell *lc;
4635 
4636  /* Drop the trigger events */
4638 
4639  /* Drop FDW tuplestore if any */
4640  ts = qs->fdw_tuplestore;
4641  qs->fdw_tuplestore = NULL;
4642  if (ts)
4643  tuplestore_end(ts);
4644 
4645  /* Release per-table subsidiary storage */
4646  tables = qs->tables;
4647  foreach(lc, tables)
4648  {
4650 
4651  ts = table->old_tuplestore;
4652  table->old_tuplestore = NULL;
4653  if (ts)
4654  tuplestore_end(ts);
4655  ts = table->new_tuplestore;
4656  table->new_tuplestore = NULL;
4657  if (ts)
4658  tuplestore_end(ts);
4659  if (table->storeslot)
4661  }
4662 
4663  /*
4664  * Now free the AfterTriggersTableData structs and list cells. Reset list
4665  * pointer first; if list_free_deep somehow gets an error, better to leak
4666  * that storage than have an infinite loop.
4667  */
4668  qs->tables = NIL;
4669  list_free_deep(tables);
4670 }
#define NIL
Definition: pg_list.h:65
Tuplestorestate * old_tuplestore
Definition: trigger.c:3525
void list_free_deep(List *list)
Definition: list.c:1405
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3756
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
TupleTableSlot * storeslot
Definition: trigger.c:3527
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3503
Tuplestorestate * new_tuplestore
Definition: trigger.c:3526
#define lfirst(lc)
Definition: pg_list.h:169
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3502
Definition: pg_list.h:50

◆ afterTriggerInvokeEvents()

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

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

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

◆ afterTriggerMarkEvents()

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

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

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

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

5377 {
5378  AfterTriggerEvent event;
5379  AfterTriggerEventChunk *chunk;
5380  int depth;
5381 
5382  /* Scan queued events */
5384  {
5385  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5386 
5387  /*
5388  * We can ignore completed events. (Even if a DONE flag is rolled
5389  * back by subxact abort, it's OK because the effects of the TRUNCATE
5390  * or whatever must get rolled back too.)
5391  */
5392  if (event->ate_flags & AFTER_TRIGGER_DONE)
5393  continue;
5394 
5395  if (evtshared->ats_relid == relid)
5396  return true;
5397  }
5398 
5399  /*
5400  * Also scan events queued by incomplete queries. This could only matter
5401  * if TRUNCATE/etc is executed by a function or trigger within an updating
5402  * query on the same relation, which is pretty perverse, but let's check.
5403  */
5404  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5405  {
5407  {
5408  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5409 
5410  if (event->ate_flags & AFTER_TRIGGER_DONE)
5411  continue;
5412 
5413  if (evtshared->ats_relid == relid)
5414  return true;
5415  }
5416  }
5417 
5418  return false;
5419 }
TriggerFlags ate_flags
Definition: trigger.c:3330
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3305
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3392
AfterTriggersQueryData * query_stack
Definition: trigger.c:3491
#define GetTriggerSharedData(evt)
Definition: trigger.c:3355
AfterTriggerEventList events
Definition: trigger.c:3487
AfterTriggerEventList events
Definition: trigger.c:3502
static AfterTriggersData afterTriggers
Definition: trigger.c:3530

◆ afterTriggerRestoreEventList()

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

Definition at line 3777 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

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

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

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

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

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

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

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

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

◆ cancel_prior_stmt_triggers()

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

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

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

◆ 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:1299
char * tgqual
Definition: reltrigger.h:42
char * tgname
Definition: reltrigger.h:27
Trigger * triggers
Definition: reltrigger.h:49
int numtriggers
Definition: reltrigger.h:50
char ** tgargs
Definition: reltrigger.h:41
int16 * tgattr
Definition: reltrigger.h:40
char * tgnewtable
Definition: reltrigger.h:44
int16 tgnattr
Definition: reltrigger.h:39
void * palloc(Size size)
Definition: mcxt.c:1062
int i
int16 tgnargs
Definition: reltrigger.h:38
char * tgoldtable
Definition: reltrigger.h:43

◆ CreateTrigger()

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

Definition at line 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, true);
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:322
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3474
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
#define AllocSetContextCreate
Definition: memutils.h:173
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:48
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
int errhint(const char *fmt,...)
Definition: elog.c:1156
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:446
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:595
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
#define TriggerRelidNameIndexId
Definition: pg_trigger.h:87
bool IsSystemRelation(Relation relation)
Definition: catalog.c:73
#define RelationGetDescr(relation)
Definition: rel.h:495
Oid GetUserId(void)
Definition: miscinit.c:478
#define PointerGetDatum(X)
Definition: postgres.h:600
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:78
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
Node * whenClause
Definition: parsenodes.h:2529
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int conInhCount, bool conNoInherit, bool is_internal)
Definition: pg_constraint.c:50
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1974
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:205
int2vector * buildint2vector(const int16 *int2s, int n)
Definition: int.c:114
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:232
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define Int16GetDatum(X)
Definition: postgres.h:495
#define AccessShareLock
Definition: lockdefs.h:36
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Definition: nodes.h:539
#define strVal(v)
Definition: value.h:54
int errcode(int sqlerrcode)
Definition: elog.c:698
AttrNumber varattno
Definition: primnodes.h:191
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:43
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:109
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
Definition: primnodes.h:186
List * lappend_oid(List *list, Oid datum)
Definition: list.c:372
#define OidIsValid(objectId)
Definition: c.h:710
#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:2535
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:1169
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
Definition: parse_func.c:2117
ItemPointerData t_self
Definition: htup.h:65
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:378
Datum byteain(PG_FUNCTION_ARGS)
Definition: varlena.c:296
#define lfirst_node(type, lc)
Definition: pg_list.h:172
#define ACL_TRIGGER
Definition: parsenodes.h:88
int location
Definition: primnodes.h:201
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:72
int errdetail(const char *fmt,...)
Definition: elog.c:1042
#define CStringGetDatum(X)
Definition: postgres.h:622
void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior)
Definition: dependency.c:1578
#define RelationGetRelationName(relation)
Definition: rel.h:503
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
const char * p_sourcetext
Definition: parse_node.h:181
List * transitionRels
Definition: parsenodes.h:2531
Oid rd_id
Definition: rel.h:111
#define PRS2_OLD_VARNO
Definition: primnodes.h:183
Index varno
Definition: primnodes.h:189
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:411
void CommandCounterIncrement(void)
Definition: xact.c:1021
#define InvokeObjectPostCreateHookArg(classId, objectId, subId, is_internal)
Definition: objectaccess.h:153
TupleDesc rd_att
Definition: rel.h:110
#define BoolGetDatum(X)
Definition: postgres.h:446
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:102
bool allowSystemTableMods
Definition: globals.c:123
#define InvalidOid
Definition: postgres_ext.h:36
int16 attnum
Definition: pg_attribute.h:83
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition: pg_inherits.c:59
#define ereport(elevel,...)
Definition: elog.h:157
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
int attnameAttNum(Relation rd, const char *attname, bool sysColOK)
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
#define ShareRowExclusiveLock
Definition: lockdefs.h:42
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
static int list_length(const List *l)
Definition: pg_list.h:149
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
const char * name
Definition: encode.c:515
#define InvalidAttrNumber
Definition: attnum.h:23
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define CharGetDatum(X)
Definition: postgres.h:460
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4685
#define DatumGetPointer(X)
Definition: postgres.h:593
static Datum values[MAXATTR]
Definition: bootstrap.c:166
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:175
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:256
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define ACL_EXECUTE
Definition: parsenodes.h:89
void list_free(List *list)
Definition: list.c:1391
#define elog(elevel,...)
Definition: elog.h:232
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4723
int i
RangeVar * relation
Definition: parsenodes.h:2520
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:4526
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1314
#define copyObject(obj)
Definition: nodes.h:655
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:109
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:77
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
Definition: pg_list.h:50
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1899
#define snprintf
Definition: port.h:216
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:221
#define RelationGetRelid(relation)
Definition: rel.h:469
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:221
#define PRS2_NEW_VARNO
Definition: primnodes.h:184
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define lfirst_oid(lc)
Definition: pg_list.h:171
#define RelationGetNamespace(relation)
Definition: rel.h:510
List * p_rtable
Definition: parse_node.h:182

◆ EnableDisableTrigger()

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

Definition at line 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:654
#define TriggerRelidNameIndexId
Definition: pg_trigger.h:87
int errcode(int sqlerrcode)
Definition: elog.c:698
bool superuser(void)
Definition: superuser.c:46
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:383
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:502
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
#define RowExclusiveLock
Definition: lockdefs.h:38
#define CStringGetDatum(X)
Definition: postgres.h:622
#define RelationGetRelationName(relation)
Definition: rel.h:503
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:175
#define ereport(elevel,...)
Definition: elog.h:157
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1278
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define NameStr(name)
Definition: c.h:681
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define RelationGetRelid(relation)
Definition: rel.h:469
#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 
2549  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2550  (transition_capture && transition_capture->tcs_delete_old_table))
2551  {
2552  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2553 
2554  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2555  if (fdw_trigtuple == NULL)
2556  GetTupleForTrigger(estate,
2557  NULL,
2558  relinfo,
2559  tupleid,
2561  slot,
2562  NULL);
2563  else
2564  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2565 
2567  true, slot, NULL, NIL, NULL,
2568  transition_capture);
2569  }
2570 }
#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:2996
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:92
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1166
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1469
bool trig_delete_after_row
Definition: reltrigger.h:67
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:438
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:5446
#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:438
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:5446
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:91

◆ ExecARUpdateTriggers()