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  AfterTriggerEventDataNoOids
 
struct  AfterTriggerEventDataOneCtid
 
struct  AfterTriggerEventDataZeroCtids
 
struct  AfterTriggerEventChunk
 
struct  AfterTriggerEventList
 
struct  AfterTriggersData
 
struct  AfterTriggersQueryData
 
struct  AfterTriggersTransData
 
struct  AfterTriggersTableData
 

Macros

#define AFTER_TRIGGER_OFFSET   0x07FFFFFF /* must be low-order bits */
 
#define AFTER_TRIGGER_DONE   0x80000000
 
#define AFTER_TRIGGER_IN_PROGRESS   0x40000000
 
#define AFTER_TRIGGER_FDW_REUSE   0x00000000
 
#define AFTER_TRIGGER_FDW_FETCH   0x20000000
 
#define AFTER_TRIGGER_1CTID   0x10000000
 
#define AFTER_TRIGGER_2CTID   0x30000000
 
#define AFTER_TRIGGER_CP_UPDATE   0x08000000
 
#define AFTER_TRIGGER_TUP_BITS   0x38000000
 
#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 AfterTriggerEventDataNoOids AfterTriggerEventDataNoOids
 
typedef struct AfterTriggerEventDataOneCtid AfterTriggerEventDataOneCtid
 
typedef struct AfterTriggerEventDataZeroCtids AfterTriggerEventDataZeroCtids
 
typedef struct AfterTriggerEventChunk AfterTriggerEventChunk
 
typedef struct AfterTriggerEventList AfterTriggerEventList
 
typedef struct AfterTriggersQueryData AfterTriggersQueryData
 
typedef struct AfterTriggersTransData AfterTriggersTransData
 
typedef struct AfterTriggersTableData AfterTriggersTableData
 
typedef struct AfterTriggersData AfterTriggersData
 

Functions

static void renametrig_internal (Relation tgrel, Relation targetrel, HeapTuple trigtup, const char *newname, const char *expected_name)
 
static void renametrig_partition (Relation tgrel, Oid partitionId, Oid parentTriggerOid, const char *newname, const char *expected_name)
 
static void SetTriggerFlags (TriggerDesc *trigdesc, Trigger *trigger)
 
static bool GetTupleForTrigger (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **newSlot, TM_FailureData *tmfpd)
 
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, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldtup, TupleTableSlot *newtup, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
static void AfterTriggerEnlargeQueryState (void)
 
static bool before_stmt_triggers_fired (Oid relid, CmdType cmdType)
 
ObjectAddress CreateTrigger (CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
 
ObjectAddress CreateTriggerFiringOn (CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
 
void TriggerSetParentTrigger (Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
 
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 is_crosspart_update)
 
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, TM_FailureData *tmfd)
 
void ExecARUpdateTriggers (EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
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, ResultRelInfo *src_relInfo, ResultRelInfo *dst_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 TuplestorestateGetAfterTriggersTransitionTable (int event, TupleTableSlot *oldslot, TupleTableSlot *newslot, TransitionCaptureState *transition_capture)
 
static void TransitionTableAddTuple (EState *estate, TransitionCaptureState *transition_capture, ResultRelInfo *relinfo, TupleTableSlot *slot, TupleTableSlot *original_insert_tuple, Tuplestorestate *tuplestore)
 
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   0x10000000

Definition at line 3601 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3602 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3603 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3596 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3600 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3599 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3597 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3595 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3604 of file trigger.c.

◆ CHUNK_DATA_START

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

Definition at line 3683 of file trigger.c.

◆ for_each_chunk

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

Definition at line 3694 of file trigger.c.

◆ for_each_chunk_from

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

Definition at line 3705 of file trigger.c.

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

Definition at line 3696 of file trigger.c.

◆ for_each_event_chunk

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

Definition at line 3701 of file trigger.c.

◆ for_each_event_from

#define for_each_event_from (   eptr,
  cptr 
)
Value:
for (; \
(char *) eptr < (cptr)->freeptr; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))

Definition at line 3707 of file trigger.c.

◆ GetTriggerSharedData

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

Definition at line 3664 of file trigger.c.

◆ MAX_CHUNK_SIZE

#define MAX_CHUNK_SIZE   (1024*1024)

◆ MIN_CHUNK_SIZE

#define MIN_CHUNK_SIZE   1024

◆ SizeofTriggerEvent

#define SizeofTriggerEvent (   evt)
Value:
(((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_CP_UPDATE ? \
(((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:3604
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3601
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3602
#define AFTER_TRIGGER_CP_UPDATE
Definition: trigger.c:3603

Definition at line 3655 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3617 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3605 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3617 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3617 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3617 of file trigger.c.

◆ SetConstraintState

Definition at line 3548 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3527 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3593 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 3976 of file trigger.c.

3978 {
3979  Size eventsize = SizeofTriggerEvent(event);
3980  Size needed = eventsize + sizeof(AfterTriggerSharedData);
3981  AfterTriggerEventChunk *chunk;
3982  AfterTriggerShared newshared;
3983  AfterTriggerEvent newevent;
3984 
3985  /*
3986  * If empty list or not enough room in the tail chunk, make a new chunk.
3987  * We assume here that a new shared record will always be needed.
3988  */
3989  chunk = events->tail;
3990  if (chunk == NULL ||
3991  chunk->endfree - chunk->freeptr < needed)
3992  {
3993  Size chunksize;
3994 
3995  /* Create event context if we didn't already */
3996  if (afterTriggers.event_cxt == NULL)
3999  "AfterTriggerEvents",
4001 
4002  /*
4003  * Chunk size starts at 1KB and is allowed to increase up to 1MB.
4004  * These numbers are fairly arbitrary, though there is a hard limit at
4005  * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
4006  * shared records using the available space in ate_flags. Another
4007  * constraint is that if the chunk size gets too huge, the search loop
4008  * below would get slow given a (not too common) usage pattern with
4009  * many distinct event types in a chunk. Therefore, we double the
4010  * preceding chunk size only if there weren't too many shared records
4011  * in the preceding chunk; otherwise we halve it. This gives us some
4012  * ability to adapt to the actual usage pattern of the current query
4013  * while still having large chunk sizes in typical usage. All chunk
4014  * sizes used should be MAXALIGN multiples, to ensure that the shared
4015  * records will be aligned safely.
4016  */
4017 #define MIN_CHUNK_SIZE 1024
4018 #define MAX_CHUNK_SIZE (1024*1024)
4019 
4020 #if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
4021 #error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
4022 #endif
4023 
4024  if (chunk == NULL)
4025  chunksize = MIN_CHUNK_SIZE;
4026  else
4027  {
4028  /* preceding chunk size... */
4029  chunksize = chunk->endptr - (char *) chunk;
4030  /* check number of shared records in preceding chunk */
4031  if ((chunk->endptr - chunk->endfree) <=
4032  (100 * sizeof(AfterTriggerSharedData)))
4033  chunksize *= 2; /* okay, double it */
4034  else
4035  chunksize /= 2; /* too many shared records */
4036  chunksize = Min(chunksize, MAX_CHUNK_SIZE);
4037  }
4038  chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize);
4039  chunk->next = NULL;
4040  chunk->freeptr = CHUNK_DATA_START(chunk);
4041  chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
4042  Assert(chunk->endfree - chunk->freeptr >= needed);
4043 
4044  if (events->head == NULL)
4045  events->head = chunk;
4046  else
4047  events->tail->next = chunk;
4048  events->tail = chunk;
4049  /* events->tailfree is now out of sync, but we'll fix it below */
4050  }
4051 
4052  /*
4053  * Try to locate a matching shared-data record already in the chunk. If
4054  * none, make a new one.
4055  */
4056  for (newshared = ((AfterTriggerShared) chunk->endptr) - 1;
4057  (char *) newshared >= chunk->endfree;
4058  newshared--)
4059  {
4060  if (newshared->ats_tgoid == evtshared->ats_tgoid &&
4061  newshared->ats_relid == evtshared->ats_relid &&
4062  newshared->ats_event == evtshared->ats_event &&
4063  newshared->ats_table == evtshared->ats_table &&
4064  newshared->ats_firing_id == 0)
4065  break;
4066  }
4067  if ((char *) newshared < chunk->endfree)
4068  {
4069  *newshared = *evtshared;
4070  newshared->ats_firing_id = 0; /* just to be sure */
4071  chunk->endfree = (char *) newshared;
4072  }
4073 
4074  /* Insert the data */
4075  newevent = (AfterTriggerEvent) chunk->freeptr;
4076  memcpy(newevent, event, eventsize);
4077  /* ... and link the new event to its shared record */
4078  newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
4079  newevent->ate_flags |= (char *) newshared - (char *) newevent;
4080 
4081  chunk->freeptr += eventsize;
4082  events->tailfree = chunk->freeptr;
4083 }
#define Min(x, y)
Definition: c.h:986
size_t Size
Definition: c.h:540
Assert(fmt[strlen(fmt) - 1] !='\n')
MemoryContext TopTransactionContext
Definition: mcxt.c:53
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:863
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197
struct AfterTriggerEventChunk * next
Definition: trigger.c:3676
TriggerFlags ate_flags
Definition: trigger.c:3621
AfterTriggerEventChunk * head
Definition: trigger.c:3688
AfterTriggerEventChunk * tail
Definition: trigger.c:3689
TriggerEvent ats_event
Definition: trigger.c:3609
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3613
CommandId ats_firing_id
Definition: trigger.c:3612
MemoryContext event_cxt
Definition: trigger.c:3797
struct AfterTriggerSharedData AfterTriggerSharedData
static AfterTriggersData afterTriggers
Definition: trigger.c:3854
#define AFTER_TRIGGER_OFFSET
Definition: trigger.c:3595
#define MAX_CHUNK_SIZE
#define MIN_CHUNK_SIZE

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

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4961 of file trigger.c.

4962 {
4963  /* Increase the query stack depth */
4965 }

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5239 of file trigger.c.

5240 {
5241  int my_level = GetCurrentTransactionNestLevel();
5242 
5243  /*
5244  * Allocate more space in the trans_stack if needed. (Note: because the
5245  * minimum nest level of a subtransaction is 2, we waste the first couple
5246  * entries of the array; not worth the notational effort to avoid it.)
5247  */
5248  while (my_level >= afterTriggers.maxtransdepth)
5249  {
5250  if (afterTriggers.maxtransdepth == 0)
5251  {
5252  /* Arbitrarily initialize for max of 8 subtransaction levels */
5255  8 * sizeof(AfterTriggersTransData));
5257  }
5258  else
5259  {
5260  /* repalloc will keep the stack in the same context */
5261  int new_alloc = afterTriggers.maxtransdepth * 2;
5262 
5265  new_alloc * sizeof(AfterTriggersTransData));
5266  afterTriggers.maxtransdepth = new_alloc;
5267  }
5268  }
5269 
5270  /*
5271  * Push the current information into the stack. The SET CONSTRAINTS state
5272  * is not saved until/unless changed. Likewise, we don't make a
5273  * per-subtransaction event context until needed.
5274  */
5275  afterTriggers.trans_stack[my_level].state = NULL;
5279 }
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1188
CommandId firing_counter
Definition: trigger.c:3794
AfterTriggersTransData * trans_stack
Definition: trigger.c:3805
AfterTriggerEventList events
Definition: trigger.c:3796
AfterTriggerEventList events
Definition: trigger.c:3820
SetConstraintState state
Definition: trigger.c:3819
CommandId firing_counter
Definition: trigger.c:3822
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:910

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

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

Definition at line 4929 of file trigger.c.

4930 {
4931  /*
4932  * Initialize after-trigger state structure to empty
4933  */
4934  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4936 
4937  /*
4938  * Verify that there is no leftover state remaining. If these assertions
4939  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4940  * up properly.
4941  */
4942  Assert(afterTriggers.state == NULL);
4943  Assert(afterTriggers.query_stack == NULL);
4945  Assert(afterTriggers.event_cxt == NULL);
4946  Assert(afterTriggers.events.head == NULL);
4947  Assert(afterTriggers.trans_stack == NULL);
4949 }
uint32 CommandId
Definition: c.h:601
SetConstraintState state
Definition: trigger.c:3795
AfterTriggersQueryData * query_stack
Definition: trigger.c:3800

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

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

Definition at line 3930 of file trigger.c.

3931 {
3932  Oid tgoid = evtshared->ats_tgoid;
3934  int i;
3935 
3936  /*
3937  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3938  * constraints declared NOT DEFERRABLE), the state is always false.
3939  */
3940  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3941  return false;
3942 
3943  /*
3944  * If constraint state exists, SET CONSTRAINTS might have been executed
3945  * either for this trigger or for all triggers.
3946  */
3947  if (state != NULL)
3948  {
3949  /* Check for SET CONSTRAINTS for this specific trigger. */
3950  for (i = 0; i < state->numstates; i++)
3951  {
3952  if (state->trigstates[i].sct_tgoid == tgoid)
3953  return state->trigstates[i].sct_tgisdeferred;
3954  }
3955 
3956  /* Check for SET CONSTRAINTS ALL. */
3957  if (state->all_isset)
3958  return state->all_isdeferred;
3959  }
3960 
3961  /*
3962  * Otherwise return the default state for the trigger.
3963  */
3964  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3965 }
int i
Definition: isn.c:73
unsigned int Oid
Definition: postgres_ext.h:31
Definition: regguts.h:318
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:107
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:108

References AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_INITDEFERRED, afterTriggers, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_tgoid, i, and AfterTriggersData::state.

Referenced by afterTriggerMarkEvents().

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 4153 of file trigger.c.

4154 {
4155  AfterTriggerEventChunk *target = qs->events.head;
4156  ListCell *lc;
4157 
4158  Assert(target && target->next);
4159 
4160  /*
4161  * First, update any pointers in the per-table data, so that they won't be
4162  * dangling. Resetting obsoleted pointers to NULL will make
4163  * cancel_prior_stmt_triggers start from the list head, which is fine.
4164  */
4165  foreach(lc, qs->tables)
4166  {
4168 
4169  if (table->after_trig_done &&
4170  table->after_trig_events.tail == target)
4171  {
4172  table->after_trig_events.head = NULL;
4173  table->after_trig_events.tail = NULL;
4174  table->after_trig_events.tailfree = NULL;
4175  }
4176  }
4177 
4178  /* Now we can flush the head chunk */
4179  qs->events.head = target->next;
4180  pfree(target);
4181 }
void pfree(void *pointer)
Definition: mcxt.c:1175
#define lfirst(lc)
Definition: pg_list.h:169
AfterTriggerEventList events
Definition: trigger.c:3811
AfterTriggerEventList after_trig_events
Definition: trigger.c:3833

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

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 4981 of file trigger.c.

4982 {
4984 
4985  /* Must be inside a query, too */
4987 
4988  /*
4989  * If we never even got as far as initializing the event stack, there
4990  * certainly won't be any events, so exit quickly.
4991  */
4993  {
4995  return;
4996  }
4997 
4998  /*
4999  * Process all immediate-mode triggers queued by the query, and move the
5000  * deferred ones to the main list of deferred events.
5001  *
5002  * Notice that we decide which ones will be fired, and put the deferred
5003  * ones on the main list, before anything is actually fired. This ensures
5004  * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5005  * IMMEDIATE: all events we have decided to defer will be available for it
5006  * to fire.
5007  *
5008  * We loop in case a trigger queues more events at the same query level.
5009  * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5010  * will instead fire any triggers in a dedicated query level. Foreign key
5011  * enforcement triggers do add to the current query level, thanks to their
5012  * passing fire_triggers = false to SPI_execute_snapshot(). Other
5013  * C-language triggers might do likewise.
5014  *
5015  * If we find no firable events, we don't have to increment
5016  * firing_counter.
5017  */
5019 
5020  for (;;)
5021  {
5023  {
5024  CommandId firing_id = afterTriggers.firing_counter++;
5025  AfterTriggerEventChunk *oldtail = qs->events.tail;
5026 
5027  if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5028  break; /* all fired */
5029 
5030  /*
5031  * Firing a trigger could result in query_stack being repalloc'd,
5032  * so we must recalculate qs after each afterTriggerInvokeEvents
5033  * call. Furthermore, it's unsafe to pass delete_ok = true here,
5034  * because that could cause afterTriggerInvokeEvents to try to
5035  * access qs->events after the stack has been repalloc'd.
5036  */
5038 
5039  /*
5040  * We'll need to scan the events list again. To reduce the cost
5041  * of doing so, get rid of completely-fired chunks. We know that
5042  * all events were marked IN_PROGRESS or DONE at the conclusion of
5043  * afterTriggerMarkEvents, so any still-interesting events must
5044  * have been added after that, and so must be in the chunk that
5045  * was then the tail chunk, or in later chunks. So, zap all
5046  * chunks before oldtail. This is approximately the same set of
5047  * events we would have gotten rid of by passing delete_ok = true.
5048  */
5049  Assert(oldtail != NULL);
5050  while (qs->events.head != oldtail)
5052  }
5053  else
5054  break;
5055  }
5056 
5057  /* Release query-level-local storage, including tuplestores if any */
5059 
5061 }
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition: trigger.c:4153
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:5072
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4486
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4570

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

Definition at line 5287 of file trigger.c.

5288 {
5289  int my_level = GetCurrentTransactionNestLevel();
5291  AfterTriggerEvent event;
5292  AfterTriggerEventChunk *chunk;
5293  CommandId subxact_firing_id;
5294 
5295  /*
5296  * Pop the prior state if needed.
5297  */
5298  if (isCommit)
5299  {
5300  Assert(my_level < afterTriggers.maxtransdepth);
5301  /* If we saved a prior state, we don't need it anymore */
5302  state = afterTriggers.trans_stack[my_level].state;
5303  if (state != NULL)
5304  pfree(state);
5305  /* this avoids double pfree if error later: */
5306  afterTriggers.trans_stack[my_level].state = NULL;
5309  }
5310  else
5311  {
5312  /*
5313  * Aborting. It is possible subxact start failed before calling
5314  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5315  * trans_stack levels that aren't there.
5316  */
5317  if (my_level >= afterTriggers.maxtransdepth)
5318  return;
5319 
5320  /*
5321  * Release query-level storage for queries being aborted, and restore
5322  * query_depth to its pre-subxact value. This assumes that a
5323  * subtransaction will not add events to query levels started in a
5324  * earlier transaction state.
5325  */
5327  {
5331  }
5334 
5335  /*
5336  * Restore the global deferred-event list to its former length,
5337  * discarding any events queued by the subxact.
5338  */
5340  &afterTriggers.trans_stack[my_level].events);
5341 
5342  /*
5343  * Restore the trigger state. If the saved state is NULL, then this
5344  * subxact didn't save it, so it doesn't need restoring.
5345  */
5346  state = afterTriggers.trans_stack[my_level].state;
5347  if (state != NULL)
5348  {
5351  }
5352  /* this avoids double pfree if error later: */
5353  afterTriggers.trans_stack[my_level].state = NULL;
5354 
5355  /*
5356  * Scan for any remaining deferred events that were marked DONE or IN
5357  * PROGRESS by this subxact or a child, and un-mark them. We can
5358  * recognize such events because they have a firing ID greater than or
5359  * equal to the firing_counter value we saved at subtransaction start.
5360  * (This essentially assumes that the current subxact includes all
5361  * subxacts started after it.)
5362  */
5363  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5365  {
5366  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5367 
5368  if (event->ate_flags &
5370  {
5371  if (evtshared->ats_firing_id >= subxact_firing_id)
5372  event->ate_flags &=
5374  }
5375  }
5376  }
5377 }
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3597
#define GetTriggerSharedData(evt)
Definition: trigger.c:3664
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:4113
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3596
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3701

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AfterTriggerFreeQuery(), afterTriggerRestoreEventList(), afterTriggers, 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().

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

Definition at line 5191 of file trigger.c.

5192 {
5193  /*
5194  * Forget the pending-events list.
5195  *
5196  * Since all the info is in TopTransactionContext or children thereof, we
5197  * don't really need to do anything to reclaim memory. However, the
5198  * pending-events list could be large, and so it's useful to discard it as
5199  * soon as possible --- especially if we are aborting because we ran out
5200  * of memory for the list!
5201  */
5203  {
5205  afterTriggers.event_cxt = NULL;
5206  afterTriggers.events.head = NULL;
5207  afterTriggers.events.tail = NULL;
5208  afterTriggers.events.tailfree = NULL;
5209  }
5210 
5211  /*
5212  * Forget any subtransaction state as well. Since this can't be very
5213  * large, we let the eventual reset of TopTransactionContext free the
5214  * memory instead of doing it here.
5215  */
5216  afterTriggers.trans_stack = NULL;
5218 
5219 
5220  /*
5221  * Forget the query stack and constraint-related state information. As
5222  * with the subtransaction state information, we don't bother freeing the
5223  * memory here.
5224  */
5225  afterTriggers.query_stack = NULL;
5227  afterTriggers.state = NULL;
5228 
5229  /* No more afterTriggers manipulation until next transaction starts. */
5231 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218

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

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

Definition at line 5475 of file trigger.c.

5476 {
5477  int init_depth = afterTriggers.maxquerydepth;
5478 
5480 
5481  if (afterTriggers.maxquerydepth == 0)
5482  {
5483  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5484 
5487  new_alloc * sizeof(AfterTriggersQueryData));
5488  afterTriggers.maxquerydepth = new_alloc;
5489  }
5490  else
5491  {
5492  /* repalloc will keep the stack in the same context */
5493  int old_alloc = afterTriggers.maxquerydepth;
5494  int new_alloc = Max(afterTriggers.query_depth + 1,
5495  old_alloc * 2);
5496 
5499  new_alloc * sizeof(AfterTriggersQueryData));
5500  afterTriggers.maxquerydepth = new_alloc;
5501  }
5502 
5503  /* Initialize new array entries to empty */
5504  while (init_depth < afterTriggers.maxquerydepth)
5505  {
5507 
5508  qs->events.head = NULL;
5509  qs->events.tail = NULL;
5510  qs->events.tailfree = NULL;
5511  qs->fdw_tuplestore = NULL;
5512  qs->tables = NIL;
5513 
5514  ++init_depth;
5515  }
5516 }
#define Max(x, y)
Definition: c.h:980
#define NIL
Definition: pg_list.h:65
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3812

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

◆ AfterTriggerExecute()

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

Definition at line 4215 of file trigger.c.

4225 {
4226  Relation rel = relInfo->ri_RelationDesc;
4227  Relation src_rel = src_relInfo->ri_RelationDesc;
4228  Relation dst_rel = dst_relInfo->ri_RelationDesc;
4229  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4230  Oid tgoid = evtshared->ats_tgoid;
4231  TriggerData LocTriggerData = {0};
4232  HeapTuple rettuple;
4233  int tgindx;
4234  bool should_free_trig = false;
4235  bool should_free_new = false;
4236 
4237  /*
4238  * Locate trigger in trigdesc.
4239  */
4240  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4241  {
4242  if (trigdesc->triggers[tgindx].tgoid == tgoid)
4243  {
4244  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4245  break;
4246  }
4247  }
4248  if (LocTriggerData.tg_trigger == NULL)
4249  elog(ERROR, "could not find trigger %u", tgoid);
4250 
4251  /*
4252  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4253  * to include time spent re-fetching tuples in the trigger cost.
4254  */
4255  if (instr)
4256  InstrStartNode(instr + tgindx);
4257 
4258  /*
4259  * Fetch the required tuple(s).
4260  */
4261  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4262  {
4264  {
4265  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4266 
4267  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4268  trig_tuple_slot1))
4269  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4270 
4271  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4273  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4274  trig_tuple_slot2))
4275  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4276  }
4277  /* fall through */
4279 
4280  /*
4281  * Store tuple in the slot so that tg_trigtuple does not reference
4282  * tuplestore memory. (It is formally possible for the trigger
4283  * function to queue trigger events that add to the same
4284  * tuplestore, which can push other tuples out of memory.) The
4285  * distinction is academic, because we start with a minimal tuple
4286  * that is stored as a heap tuple, constructed in different memory
4287  * context, in the slot anyway.
4288  */
4289  LocTriggerData.tg_trigslot = trig_tuple_slot1;
4290  LocTriggerData.tg_trigtuple =
4291  ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
4292 
4293  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4295  {
4296  LocTriggerData.tg_newslot = trig_tuple_slot2;
4297  LocTriggerData.tg_newtuple =
4298  ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new);
4299  }
4300  else
4301  {
4302  LocTriggerData.tg_newtuple = NULL;
4303  }
4304  break;
4305 
4306  default:
4307  if (ItemPointerIsValid(&(event->ate_ctid1)))
4308  {
4309  TupleTableSlot *src_slot = ExecGetTriggerOldSlot(estate,
4310  src_relInfo);
4311 
4312  if (!table_tuple_fetch_row_version(src_rel,
4313  &(event->ate_ctid1),
4314  SnapshotAny,
4315  src_slot))
4316  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4317 
4318  /*
4319  * Store the tuple fetched from the source partition into the
4320  * target (root partitioned) table slot, converting if needed.
4321  */
4322  if (src_relInfo != relInfo)
4323  {
4324  TupleConversionMap *map = ExecGetChildToRootMap(src_relInfo);
4325 
4326  LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4327  if (map)
4328  {
4330  src_slot,
4331  LocTriggerData.tg_trigslot);
4332  }
4333  else
4334  ExecCopySlot(LocTriggerData.tg_trigslot, src_slot);
4335  }
4336  else
4337  LocTriggerData.tg_trigslot = src_slot;
4338  LocTriggerData.tg_trigtuple =
4339  ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, &should_free_trig);
4340  }
4341  else
4342  {
4343  LocTriggerData.tg_trigtuple = NULL;
4344  }
4345 
4346  /* don't touch ctid2 if not there */
4348  (event->ate_flags & AFTER_TRIGGER_CP_UPDATE)) &&
4349  ItemPointerIsValid(&(event->ate_ctid2)))
4350  {
4351  TupleTableSlot *dst_slot = ExecGetTriggerNewSlot(estate,
4352  dst_relInfo);
4353 
4354  if (!table_tuple_fetch_row_version(dst_rel,
4355  &(event->ate_ctid2),
4356  SnapshotAny,
4357  dst_slot))
4358  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4359 
4360  /*
4361  * Store the tuple fetched from the destination partition into
4362  * the target (root partitioned) table slot, converting if
4363  * needed.
4364  */
4365  if (dst_relInfo != relInfo)
4366  {
4367  TupleConversionMap *map = ExecGetChildToRootMap(dst_relInfo);
4368 
4369  LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4370  if (map)
4371  {
4373  dst_slot,
4374  LocTriggerData.tg_newslot);
4375  }
4376  else
4377  ExecCopySlot(LocTriggerData.tg_newslot, dst_slot);
4378  }
4379  else
4380  LocTriggerData.tg_newslot = dst_slot;
4381  LocTriggerData.tg_newtuple =
4382  ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, &should_free_new);
4383  }
4384  else
4385  {
4386  LocTriggerData.tg_newtuple = NULL;
4387  }
4388  }
4389 
4390  /*
4391  * Set up the tuplestore information to let the trigger have access to
4392  * transition tables. When we first make a transition table available to
4393  * a trigger, mark it "closed" so that it cannot change anymore. If any
4394  * additional events of the same type get queued in the current trigger
4395  * query level, they'll go into new transition tables.
4396  */
4397  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4398  if (evtshared->ats_table)
4399  {
4400  if (LocTriggerData.tg_trigger->tgoldtable)
4401  {
4402  if (TRIGGER_FIRED_BY_UPDATE(evtshared->ats_event))
4403  LocTriggerData.tg_oldtable = evtshared->ats_table->old_upd_tuplestore;
4404  else
4405  LocTriggerData.tg_oldtable = evtshared->ats_table->old_del_tuplestore;
4406  evtshared->ats_table->closed = true;
4407  }
4408 
4409  if (LocTriggerData.tg_trigger->tgnewtable)
4410  {
4411  if (TRIGGER_FIRED_BY_INSERT(evtshared->ats_event))
4412  LocTriggerData.tg_newtable = evtshared->ats_table->new_ins_tuplestore;
4413  else
4414  LocTriggerData.tg_newtable = evtshared->ats_table->new_upd_tuplestore;
4415  evtshared->ats_table->closed = true;
4416  }
4417  }
4418 
4419  /*
4420  * Setup the remaining trigger information
4421  */
4422  LocTriggerData.type = T_TriggerData;
4423  LocTriggerData.tg_event =
4425  LocTriggerData.tg_relation = rel;
4426  if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4427  LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4428 
4429  MemoryContextReset(per_tuple_context);
4430 
4431  /*
4432  * Call the trigger and throw away any possibly returned updated tuple.
4433  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4434  */
4435  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4436  tgindx,
4437  finfo,
4438  NULL,
4439  per_tuple_context);
4440  if (rettuple != NULL &&
4441  rettuple != LocTriggerData.tg_trigtuple &&
4442  rettuple != LocTriggerData.tg_newtuple)
4443  heap_freetuple(rettuple);
4444 
4445  /*
4446  * Release resources
4447  */
4448  if (should_free_trig)
4449  heap_freetuple(LocTriggerData.tg_trigtuple);
4450  if (should_free_new)
4451  heap_freetuple(LocTriggerData.tg_newtuple);
4452 
4453  /* don't clear slots' contents if foreign table */
4454  if (trig_tuple_slot1 == NULL)
4455  {
4456  if (LocTriggerData.tg_trigslot)
4457  ExecClearTuple(LocTriggerData.tg_trigslot);
4458  if (LocTriggerData.tg_newslot)
4459  ExecClearTuple(LocTriggerData.tg_newslot);
4460  }
4461 
4462  /*
4463  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4464  * one "tuple returned" (really the number of firings).
4465  */
4466  if (instr)
4467  InstrStopNode(instr + tgindx, 1);
4468 }
#define ERROR
Definition: elog.h:33
#define elog(elevel,...)
Definition: elog.h:218
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1644
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1166
TupleTableSlot * ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1188
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1234
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
void InstrStartNode(Instrumentation *instr)
Definition: instrument.c:68
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:84
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
@ T_TriggerData
Definition: nodes.h:547
#define SnapshotAny
Definition: snapmgr.h:67
ItemPointerData ate_ctid2
Definition: trigger.c:3623
ItemPointerData ate_ctid1
Definition: trigger.c:3622
Bitmapset * ats_modifiedcols
Definition: trigger.c:3614
Tuplestorestate * old_upd_tuplestore
Definition: trigger.c:3843
Tuplestorestate * new_upd_tuplestore
Definition: trigger.c:3845
Tuplestorestate * old_del_tuplestore
Definition: trigger.c:3847
Tuplestorestate * new_ins_tuplestore
Definition: trigger.c:3849
Relation ri_RelationDesc
Definition: execnodes.h:433
Tuplestorestate * tg_oldtable
Definition: trigger.h:41
NodeTag type
Definition: trigger.h:33
Relation tg_relation
Definition: trigger.h:35
const Bitmapset * tg_updatedcols
Definition: trigger.h:43
Tuplestorestate * tg_newtable
Definition: trigger.h:42
TriggerEvent tg_event
Definition: trigger.h:34
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
HeapTuple tg_newtuple
Definition: trigger.h:37
TupleTableSlot * tg_newslot
Definition: trigger.h:40
Trigger * tg_trigger
Definition: trigger.h:38
HeapTuple tg_trigtuple
Definition: trigger.h:36
int numtriggers
Definition: reltrigger.h:50
Trigger * triggers
Definition: reltrigger.h:49
Oid tgoid
Definition: reltrigger.h:25
char * tgoldtable
Definition: reltrigger.h:43
char * tgnewtable
Definition: reltrigger.h:44
int16 tgtype
Definition: reltrigger.h:29
AttrMap * attrMap
Definition: tupconvert.h:28
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1259
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3600
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3894
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition: trigger.c:2316
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3599
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:94
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:96
#define TRIGGER_EVENT_ROW
Definition: trigger.h:98
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:110
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:116
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:177
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:475

References AFTER_TRIGGER_2CTID, AFTER_TRIGGER_CP_UPDATE, 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, TupleConversionMap::attrMap, AfterTriggersTableData::closed, elog, ERROR, ExecCallTriggerFunc(), ExecClearTuple(), ExecCopySlot(), ExecFetchSlotHeapTuple(), ExecGetChildToRootMap(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), execute_attr_map_slot(), GetCurrentFDWTuplestore(), GetTriggerSharedData, heap_freetuple(), InstrStartNode(), InstrStopNode(), ItemPointerIsValid, MemoryContextReset(), AfterTriggersTableData::new_ins_tuplestore, AfterTriggersTableData::new_upd_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_del_tuplestore, AfterTriggersTableData::old_upd_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, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, TriggerDesc::triggers, tuplestore_gettupleslot(), and TriggerData::type.

Referenced by afterTriggerInvokeEvents().

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

Definition at line 5135 of file trigger.c.

5136 {
5137  AfterTriggerEventList *events;
5138  bool snap_pushed = false;
5139 
5140  /* Must not be inside a query */
5142 
5143  /*
5144  * If there are any triggers to fire, make sure we have set a snapshot for
5145  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5146  * can't assume ActiveSnapshot is valid on entry.)
5147  */
5148  events = &afterTriggers.events;
5149  if (events->head != NULL)
5150  {
5152  snap_pushed = true;
5153  }
5154 
5155  /*
5156  * Run all the remaining triggers. Loop until they are all gone, in case
5157  * some trigger queues more for us to do.
5158  */
5159  while (afterTriggerMarkEvents(events, NULL, false))
5160  {
5161  CommandId firing_id = afterTriggers.firing_counter++;
5162 
5163  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5164  break; /* all fired */
5165  }
5166 
5167  /*
5168  * We don't bother freeing the event list, since it will go away anyway
5169  * (and more efficiently than via pfree) in AfterTriggerEndXact.
5170  */
5171 
5172  if (snap_pushed)
5174 }
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
void PopActiveSnapshot(void)
Definition: snapmgr.c:776
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:682

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

Referenced by CommitTransaction(), and PrepareTransaction().

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4092 of file trigger.c.

4093 {
4094  AfterTriggerEventChunk *chunk;
4095 
4096  while ((chunk = events->head) != NULL)
4097  {
4098  events->head = chunk->next;
4099  pfree(chunk);
4100  }
4101  events->tail = NULL;
4102  events->tailfree = NULL;
4103 }

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 5072 of file trigger.c.

5073 {
5074  Tuplestorestate *ts;
5075  List *tables;
5076  ListCell *lc;
5077 
5078  /* Drop the trigger events */
5080 
5081  /* Drop FDW tuplestore if any */
5082  ts = qs->fdw_tuplestore;
5083  qs->fdw_tuplestore = NULL;
5084  if (ts)
5085  tuplestore_end(ts);
5086 
5087  /* Release per-table subsidiary storage */
5088  tables = qs->tables;
5089  foreach(lc, tables)
5090  {
5092 
5093  ts = table->old_upd_tuplestore;
5094  table->old_upd_tuplestore = NULL;
5095  if (ts)
5096  tuplestore_end(ts);
5097  ts = table->new_upd_tuplestore;
5098  table->new_upd_tuplestore = NULL;
5099  if (ts)
5100  tuplestore_end(ts);
5101  ts = table->old_del_tuplestore;
5102  table->old_del_tuplestore = NULL;
5103  if (ts)
5104  tuplestore_end(ts);
5105  ts = table->new_ins_tuplestore;
5106  table->new_ins_tuplestore = NULL;
5107  if (ts)
5108  tuplestore_end(ts);
5109  if (table->storeslot)
5111  }
5112 
5113  /*
5114  * Now free the AfterTriggersTableData structs and list cells. Reset list
5115  * pointer first; if list_free_deep somehow gets an error, better to leak
5116  * that storage than have an infinite loop.
5117  */
5118  qs->tables = NIL;
5119  list_free_deep(tables);
5120 }
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
void list_free_deep(List *list)
Definition: list.c:1519
TupleTableSlot * storeslot
Definition: trigger.c:3851
Definition: pg_list.h:51
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4092
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453

References afterTriggerFreeEventList(), AfterTriggersQueryData::events, ExecDropSingleTupleTableSlot(), AfterTriggersQueryData::fdw_tuplestore, lfirst, list_free_deep(), AfterTriggersTableData::new_ins_tuplestore, AfterTriggersTableData::new_upd_tuplestore, NIL, AfterTriggersTableData::old_del_tuplestore, AfterTriggersTableData::old_upd_tuplestore, AfterTriggersTableData::storeslot, AfterTriggersQueryData::tables, and tuplestore_end().

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

◆ afterTriggerInvokeEvents()

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

Definition at line 4570 of file trigger.c.

4574 {
4575  bool all_fired = true;
4576  AfterTriggerEventChunk *chunk;
4577  MemoryContext per_tuple_context;
4578  bool local_estate = false;
4579  ResultRelInfo *rInfo = NULL;
4580  Relation rel = NULL;
4581  TriggerDesc *trigdesc = NULL;
4582  FmgrInfo *finfo = NULL;
4583  Instrumentation *instr = NULL;
4584  TupleTableSlot *slot1 = NULL,
4585  *slot2 = NULL;
4586 
4587  /* Make a local EState if need be */
4588  if (estate == NULL)
4589  {
4590  estate = CreateExecutorState();
4591  local_estate = true;
4592  }
4593 
4594  /* Make a per-tuple memory context for trigger function calls */
4595  per_tuple_context =
4597  "AfterTriggerTupleContext",
4599 
4600  for_each_chunk(chunk, *events)
4601  {
4602  AfterTriggerEvent event;
4603  bool all_fired_in_chunk = true;
4604 
4605  for_each_event(event, chunk)
4606  {
4607  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4608 
4609  /*
4610  * Is it one for me to fire?
4611  */
4612  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4613  evtshared->ats_firing_id == firing_id)
4614  {
4615  ResultRelInfo *src_rInfo,
4616  *dst_rInfo;
4617 
4618  /*
4619  * So let's fire it... but first, find the correct relation if
4620  * this is not the same relation as before.
4621  */
4622  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4623  {
4624  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4625  NULL);
4626  rel = rInfo->ri_RelationDesc;
4627  /* Catch calls with insufficient relcache refcounting */
4629  trigdesc = rInfo->ri_TrigDesc;
4630  finfo = rInfo->ri_TrigFunctions;
4631  instr = rInfo->ri_TrigInstrument;
4632  if (slot1 != NULL)
4633  {
4636  slot1 = slot2 = NULL;
4637  }
4638  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4639  {
4640  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4642  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4644  }
4645  if (trigdesc == NULL) /* should not happen */
4646  elog(ERROR, "relation %u has no triggers",
4647  evtshared->ats_relid);
4648  }
4649 
4650  /*
4651  * Look up source and destination partition result rels of a
4652  * cross-partition update event.
4653  */
4654  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4656  {
4657  Assert(OidIsValid(event->ate_src_part) &&
4658  OidIsValid(event->ate_dst_part));
4659  src_rInfo = ExecGetTriggerResultRel(estate,
4660  event->ate_src_part,
4661  rInfo);
4662  dst_rInfo = ExecGetTriggerResultRel(estate,
4663  event->ate_dst_part,
4664  rInfo);
4665  }
4666  else
4667  src_rInfo = dst_rInfo = rInfo;
4668 
4669  /*
4670  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4671  * still set, so recursive examinations of the event list
4672  * won't try to re-fire it.
4673  */
4674  AfterTriggerExecute(estate, event, rInfo,
4675  src_rInfo, dst_rInfo,
4676  trigdesc, finfo, instr,
4677  per_tuple_context, slot1, slot2);
4678 
4679  /*
4680  * Mark the event as done.
4681  */
4682  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4683  event->ate_flags |= AFTER_TRIGGER_DONE;
4684  }
4685  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4686  {
4687  /* something remains to be done */
4688  all_fired = all_fired_in_chunk = false;
4689  }
4690  }
4691 
4692  /* Clear the chunk if delete_ok and nothing left of interest */
4693  if (delete_ok && all_fired_in_chunk)
4694  {
4695  chunk->freeptr = CHUNK_DATA_START(chunk);
4696  chunk->endfree = chunk->endptr;
4697 
4698  /*
4699  * If it's last chunk, must sync event list's tailfree too. Note
4700  * that delete_ok must NOT be passed as true if there could be
4701  * additional AfterTriggerEventList values pointing at this event
4702  * list, since we'd fail to fix their copies of tailfree.
4703  */
4704  if (chunk == events->tail)
4705  events->tailfree = chunk->freeptr;
4706  }
4707  }
4708  if (slot1 != NULL)
4709  {
4712  }
4713 
4714  /* Release working resources */
4715  MemoryContextDelete(per_tuple_context);
4716 
4717  if (local_estate)
4718  {
4719  ExecCloseResultRelations(estate);
4720  ExecResetTupleTable(estate->es_tupleTable, false);
4721  FreeExecutorState(estate);
4722  }
4723 
4724  return all_fired;
4725 }
#define OidIsValid(objectId)
Definition: c.h:710
void ExecCloseResultRelations(EState *estate)
Definition: execMain.c:1507
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid, ResultRelInfo *rootRelInfo)
Definition: execMain.c:1286
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1191
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1238
EState * CreateExecutorState(void)
Definition: execUtils.c:90
void FreeExecutorState(EState *estate)
Definition: execUtils.c:186
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
#define RelationHasReferenceCountZero(relation)
Definition: rel.h:473
#define RelationGetRelid(relation)
Definition: rel.h:489
List * es_tupleTable
Definition: execnodes.h:634
Definition: fmgr.h:57
TupleDesc rd_att
Definition: rel.h:110
Form_pg_class rd_rel
Definition: rel.h:109
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:469
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:460
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:463
static void AfterTriggerExecute(EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, ResultRelInfo *src_relInfo, ResultRelInfo *dst_relInfo, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
Definition: trigger.c:4215
#define for_each_event(eptr, cptr)
Definition: trigger.c:3696
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3694

References AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AFTER_TRIGGER_TUP_BITS, AfterTriggerExecute(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), AfterTriggerEventData::ate_dst_part, AfterTriggerEventData::ate_flags, AfterTriggerEventData::ate_src_part, 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(), OidIsValid, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, RelationHasReferenceCountZero, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TTSOpsMinimalTuple.

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

◆ afterTriggerMarkEvents()

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

Definition at line 4486 of file trigger.c.

4489 {
4490  bool found = false;
4491  bool deferred_found = false;
4492  AfterTriggerEvent event;
4493  AfterTriggerEventChunk *chunk;
4494 
4495  for_each_event_chunk(event, chunk, *events)
4496  {
4497  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4498  bool defer_it = false;
4499 
4500  if (!(event->ate_flags &
4502  {
4503  /*
4504  * This trigger hasn't been called or scheduled yet. Check if we
4505  * should call it now.
4506  */
4507  if (immediate_only && afterTriggerCheckState(evtshared))
4508  {
4509  defer_it = true;
4510  }
4511  else
4512  {
4513  /*
4514  * Mark it as to be fired in this firing cycle.
4515  */
4517  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4518  found = true;
4519  }
4520  }
4521 
4522  /*
4523  * If it's deferred, move it to move_list, if requested.
4524  */
4525  if (defer_it && move_list != NULL)
4526  {
4527  deferred_found = true;
4528  /* add it to move_list */
4529  afterTriggerAddEvent(move_list, event, evtshared);
4530  /* mark original copy "done" so we don't do it again */
4531  event->ate_flags |= AFTER_TRIGGER_DONE;
4532  }
4533  }
4534 
4535  /*
4536  * We could allow deferred triggers if, before the end of the
4537  * security-restricted operation, we were to verify that a SET CONSTRAINTS
4538  * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4539  */
4540  if (deferred_found && InSecurityRestrictedOperation())
4541  ereport(ERROR,
4542  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4543  errmsg("cannot fire deferred trigger within security-restricted operation")));
4544 
4545  return found;
4546 }
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ereport(elevel,...)
Definition: elog.h:143
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:630
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3976
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3930

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, afterTriggerAddEvent(), afterTriggerCheckState(), afterTriggers, 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().

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

Definition at line 5912 of file trigger.c.

5913 {
5914  AfterTriggerEvent event;
5915  AfterTriggerEventChunk *chunk;
5916  int depth;
5917 
5918  /* Scan queued events */
5920  {
5921  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5922 
5923  /*
5924  * We can ignore completed events. (Even if a DONE flag is rolled
5925  * back by subxact abort, it's OK because the effects of the TRUNCATE
5926  * or whatever must get rolled back too.)
5927  */
5928  if (event->ate_flags & AFTER_TRIGGER_DONE)
5929  continue;
5930 
5931  if (evtshared->ats_relid == relid)
5932  return true;
5933  }
5934 
5935  /*
5936  * Also scan events queued by incomplete queries. This could only matter
5937  * if TRUNCATE/etc is executed by a function or trigger within an updating
5938  * query on the same relation, which is pretty perverse, but let's check.
5939  */
5940  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5941  {
5943  {
5944  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5945 
5946  if (event->ate_flags & AFTER_TRIGGER_DONE)
5947  continue;
5948 
5949  if (evtshared->ats_relid == relid)
5950  return true;
5951  }
5952  }
5953 
5954  return false;
5955 }

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

◆ afterTriggerRestoreEventList()

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

Definition at line 4113 of file trigger.c.

4115 {
4116  AfterTriggerEventChunk *chunk;
4117  AfterTriggerEventChunk *next_chunk;
4118 
4119  if (old_events->tail == NULL)
4120  {
4121  /* restoring to a completely empty state, so free everything */
4122  afterTriggerFreeEventList(events);
4123  }
4124  else
4125  {
4126  *events = *old_events;
4127  /* free any chunks after the last one we want to keep */
4128  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4129  {
4130  next_chunk = chunk->next;
4131  pfree(chunk);
4132  }
4133  /* and clean up the tail chunk to be the right length */
4134  events->tail->next = NULL;
4135  events->tail->freeptr = events->tailfree;
4136 
4137  /*
4138  * We don't make any effort to remove now-unused shared data records.
4139  * They might still be useful, anyway.
4140  */
4141  }
4142 }

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

Referenced by AfterTriggerEndSubXact().

◆ AfterTriggerSaveEvent()

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

Definition at line 5999 of file trigger.c.

6007 {
6008  Relation rel = relinfo->ri_RelationDesc;
6009  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
6010  AfterTriggerEventData new_event;
6011  AfterTriggerSharedData new_shared;
6012  char relkind = rel->rd_rel->relkind;
6013  int tgtype_event;
6014  int tgtype_level;
6015  int i;
6016  Tuplestorestate *fdw_tuplestore = NULL;
6017 
6018  /*
6019  * Check state. We use a normal test not Assert because it is possible to
6020  * reach here in the wrong state given misconfigured RI triggers, in
6021  * particular deferring a cascade action trigger.
6022  */
6023  if (afterTriggers.query_depth < 0)
6024  elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
6025 
6026  /* Be sure we have enough space to record events at this query depth. */
6029 
6030  /*
6031  * If the directly named relation has any triggers with transition tables,
6032  * then we need to capture transition tuples.
6033  */
6034  if (row_trigger && transition_capture != NULL)
6035  {
6036  TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
6037 
6038  /*
6039  * Capture the old tuple in the appropriate transition table based on
6040  * the event.
6041  */
6042  if (!TupIsNull(oldslot))
6043  {
6044  Tuplestorestate *old_tuplestore;
6045 
6046  old_tuplestore = GetAfterTriggersTransitionTable(event,
6047  oldslot,
6048  NULL,
6049  transition_capture);
6050  TransitionTableAddTuple(estate, transition_capture, relinfo,
6051  oldslot, NULL, old_tuplestore);
6052  }
6053 
6054  /*
6055  * Capture the new tuple in the appropriate transition table based on
6056  * the event.
6057  */
6058  if (!TupIsNull(newslot))
6059  {
6060  Tuplestorestate *new_tuplestore;
6061 
6062  new_tuplestore = GetAfterTriggersTransitionTable(event,
6063  NULL,
6064  newslot,
6065  transition_capture);
6066  TransitionTableAddTuple(estate, transition_capture, relinfo,
6067  newslot, original_insert_tuple, new_tuplestore);
6068  }
6069 
6070  /*
6071  * If transition tables are the only reason we're here, return. As
6072  * mentioned above, we can also be here during update tuple routing in
6073  * presence of transition tables, in which case this function is
6074  * called separately for OLD and NEW, so we expect exactly one of them
6075  * to be NULL.
6076  */
6077  if (trigdesc == NULL ||
6078  (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
6079  (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
6080  (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
6081  (event == TRIGGER_EVENT_UPDATE && (TupIsNull(oldslot) ^ TupIsNull(newslot))))
6082  return;
6083  }
6084 
6085  /*
6086  * We normally don't see partitioned tables here for row level triggers
6087  * except in the special case of a cross-partition update. In that case,
6088  * nodeModifyTable.c:ExecCrossPartitionUpdateForeignKey() calls here to
6089  * queue an update event on the root target partitioned table, also
6090  * passing the source and destination partitions and their tuples.
6091  */
6092  Assert(!row_trigger ||
6093  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
6094  (is_crosspart_update &&
6095  TRIGGER_FIRED_BY_UPDATE(event) &&
6096  src_partinfo != NULL && dst_partinfo != NULL));
6097 
6098  /*
6099  * Validate the event code and collect the associated tuple CTIDs.
6100  *
6101  * The event code will be used both as a bitmask and an array offset, so
6102  * validation is important to make sure we don't walk off the edge of our
6103  * arrays.
6104  *
6105  * Also, if we're considering statement-level triggers, check whether we
6106  * already queued a set of them for this event, and cancel the prior set
6107  * if so. This preserves the behavior that statement-level triggers fire
6108  * just once per statement and fire after row-level triggers.
6109  */
6110  switch (event)
6111  {
6112  case TRIGGER_EVENT_INSERT:
6113  tgtype_event = TRIGGER_TYPE_INSERT;
6114  if (row_trigger)
6115  {
6116  Assert(oldslot == NULL);
6117  Assert(newslot != NULL);
6118  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
6119  ItemPointerSetInvalid(&(new_event.ate_ctid2));
6120  }
6121  else
6122  {
6123  Assert(oldslot == NULL);
6124  Assert(newslot == NULL);
6125  ItemPointerSetInvalid(&(new_event.ate_ctid1));
6126  ItemPointerSetInvalid(&(new_event.ate_ctid2));
6128  CMD_INSERT, event);
6129  }
6130  break;
6131  case TRIGGER_EVENT_DELETE:
6132  tgtype_event = TRIGGER_TYPE_DELETE;
6133  if (row_trigger)
6134  {
6135  Assert(oldslot != NULL);
6136  Assert(newslot == NULL);
6137  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6138  ItemPointerSetInvalid(&(new_event.ate_ctid2));
6139  }
6140  else
6141  {
6142  Assert(oldslot == NULL);
6143  Assert(newslot == NULL);
6144  ItemPointerSetInvalid(&(new_event.ate_ctid1));
6145  ItemPointerSetInvalid(&(new_event.ate_ctid2));
6147  CMD_DELETE, event);
6148  }
6149  break;
6150  case TRIGGER_EVENT_UPDATE:
6151  tgtype_event = TRIGGER_TYPE_UPDATE;
6152  if (row_trigger)
6153  {
6154  Assert(oldslot != NULL);
6155  Assert(newslot != NULL);
6156  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
6157  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
6158 
6159  /*
6160  * Also remember the OIDs of partitions to fetch these tuples
6161  * out of later in AfterTriggerExecute().
6162  */
6163  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6164  {
6165  Assert(src_partinfo != NULL && dst_partinfo != NULL);
6166  new_event.ate_src_part =
6167  RelationGetRelid(src_partinfo->ri_RelationDesc);
6168  new_event.ate_dst_part =
6169  RelationGetRelid(dst_partinfo->ri_RelationDesc);
6170  }
6171  }
6172  else
6173  {
6174  Assert(oldslot == NULL);
6175  Assert(newslot == NULL);
6176  ItemPointerSetInvalid(&(new_event.ate_ctid1));
6177  ItemPointerSetInvalid(&(new_event.ate_ctid2));
6179  CMD_UPDATE, event);
6180  }
6181  break;
6183  tgtype_event = TRIGGER_TYPE_TRUNCATE;
6184  Assert(oldslot == NULL);
6185  Assert(newslot == NULL);
6186  ItemPointerSetInvalid(&(new_event.ate_ctid1));
6187  ItemPointerSetInvalid(&(new_event.ate_ctid2));
6188  break;
6189  default:
6190  elog(ERROR, "invalid after-trigger event code: %d", event);
6191  tgtype_event = 0; /* keep compiler quiet */
6192  break;
6193  }
6194 
6195  /* Determine flags */
6196  if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
6197  {
6198  if (row_trigger && event == TRIGGER_EVENT_UPDATE)
6199  {
6200  if (relkind == RELKIND_PARTITIONED_TABLE)
6201  new_event.ate_flags = AFTER_TRIGGER_CP_UPDATE;
6202  else
6203  new_event.ate_flags = AFTER_TRIGGER_2CTID;
6204  }
6205  else
6206  new_event.ate_flags = AFTER_TRIGGER_1CTID;
6207  }
6208 
6209  /* else, we'll initialize ate_flags for each trigger */
6210 
6211  tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
6212 
6213  /*
6214  * Must convert/copy the source and destination partition tuples into the
6215  * root partitioned table's format/slot, because the processing in the
6216  * loop below expects both oldslot and newslot tuples to be in that form.
6217  */
6218  if (row_trigger && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6219  {
6220  TupleTableSlot *rootslot;
6221  TupleConversionMap *map;
6222 
6223  rootslot = ExecGetTriggerOldSlot(estate, relinfo);
6224  map = ExecGetChildToRootMap(src_partinfo);
6225  if (map)
6226  oldslot = execute_attr_map_slot(map->attrMap,
6227  oldslot,
6228  rootslot);
6229  else
6230  oldslot = ExecCopySlot(rootslot, oldslot);
6231 
6232  rootslot = ExecGetTriggerNewSlot(estate, relinfo);
6233  map = ExecGetChildToRootMap(dst_partinfo);
6234  if (map)
6235  newslot = execute_attr_map_slot(map->attrMap,
6236  newslot,
6237  rootslot);
6238  else
6239  newslot = ExecCopySlot(rootslot, newslot);
6240  }
6241 
6242  for (i = 0; i < trigdesc->numtriggers; i++)
6243  {
6244  Trigger *trigger = &trigdesc->triggers[i];
6245 
6246  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
6247  tgtype_level,
6248  TRIGGER_TYPE_AFTER,
6249  tgtype_event))
6250  continue;
6251  if (!TriggerEnabled(estate, relinfo, trigger, event,
6252  modifiedCols, oldslot, newslot))
6253  continue;
6254 
6255  if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
6256  {
6257  if (fdw_tuplestore == NULL)
6258  {
6259  fdw_tuplestore = GetCurrentFDWTuplestore();
6260  new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH;
6261  }
6262  else
6263  /* subsequent event for the same tuple */
6264  new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE;
6265  }
6266 
6267  /*
6268  * If the trigger is a foreign key enforcement trigger, there are
6269  * certain cases where we can skip queueing the event because we can
6270  * tell by inspection that the FK constraint will still pass. There
6271  * are also some cases during cross-partition updates of a partitioned
6272  * table where queuing the event can be skipped.
6273  */
6274  if (TRIGGER_FIRED_BY_UPDATE(event) || TRIGGER_FIRED_BY_DELETE(event))
6275  {
6276  switch (RI_FKey_trigger_type(trigger->tgfoid))
6277  {
6278  case RI_TRIGGER_PK:
6279 
6280  /*
6281  * For cross-partitioned updates of partitioned PK table,
6282  * skip the event fired by the component delete on the
6283  * source leaf partition unless the constraint originates
6284  * in the partition itself (!tgisclone), because the
6285  * update event that will be fired on the root
6286  * (partitioned) target table will be used to perform the
6287  * necessary foreign key enforcement action.
6288  */
6289  if (is_crosspart_update &&
6290  TRIGGER_FIRED_BY_DELETE(event) &&
6291  trigger->tgisclone)
6292  continue;
6293 
6294  /* Update or delete on trigger's PK table */
6295  if (!RI_FKey_pk_upd_check_required(trigger, rel,
6296  oldslot, newslot))
6297  {
6298  /* skip queuing this event */
6299  continue;
6300  }
6301  break;
6302 
6303  case RI_TRIGGER_FK:
6304 
6305  /*
6306  * Update on trigger's FK table. We can skip the update
6307  * event fired on a partitioned table during a
6308  * cross-partition of that table, because the insert event
6309  * that is fired on the destination leaf partition would
6310  * suffice to perform the necessary foreign key check.
6311  * Moreover, RI_FKey_fk_upd_check_required() expects to be
6312  * passed a tuple that contains system attributes, most of
6313  * which are not present in the virtual slot belonging to
6314  * a partitioned table.
6315  */
6316  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
6317  !RI_FKey_fk_upd_check_required(trigger, rel,
6318  oldslot, newslot))
6319  {
6320  /* skip queuing this event */
6321  continue;
6322  }
6323  break;
6324 
6325  case RI_TRIGGER_NONE:
6326 
6327  /*
6328  * Not an FK trigger. No need to queue the update event
6329  * fired during a cross-partitioned update of a
6330  * partitioned table, because the same row trigger must be
6331  * present in the leaf partition(s) that are affected as
6332  * part of this update and the events fired on them are
6333  * queued instead.
6334  */
6335  if (row_trigger &&
6336  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6337  continue;
6338  break;
6339  }
6340  }
6341 
6342  /*
6343  * If the trigger is a deferred unique constraint check trigger, only
6344  * queue it if the unique constraint was potentially violated, which
6345  * we know from index insertion time.
6346  */
6347  if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
6348  {
6349  if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
6350  continue; /* Uniqueness definitely not violated */
6351  }
6352 
6353  /*
6354  * Fill in event structure and add it to the current query's queue.
6355  * Note we set ats_table to NULL whenever this trigger doesn't use
6356  * transition tables, to improve sharability of the shared event data.
6357  */
6358  new_shared.ats_event =
6359  (event & TRIGGER_EVENT_OPMASK) |
6360  (row_trigger ? TRIGGER_EVENT_ROW : 0) |
6361  (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
6362  (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
6363  new_shared.ats_tgoid = trigger->tgoid;
6364  new_shared.ats_relid = RelationGetRelid(rel);
6365  new_shared.ats_firing_id = 0;
6366  if ((trigger->tgoldtable || trigger->tgnewtable) &&
6367  transition_capture != NULL)
6368  new_shared.ats_table = transition_capture->tcs_private;
6369  else
6370  new_shared.ats_table = NULL;
6371  new_shared.ats_modifiedcols = modifiedCols;
6372 
6374  &new_event, &new_shared);
6375  }
6376 
6377  /*
6378  * Finally, spool any foreign tuple(s). The tuplestore squashes them to
6379  * minimal tuples, so this loses any system columns. The executor lost
6380  * those columns before us, for an unrelated reason, so this is fine.
6381  */
6382  if (fdw_tuplestore)
6383  {
6384  if (oldslot != NULL)
6385  tuplestore_puttupleslot(fdw_tuplestore, oldslot);
6386  if (newslot != NULL)
6387  tuplestore_puttupleslot(fdw_tuplestore, newslot);
6388  }
6389 }
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:161
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:701
@ CMD_INSERT
Definition: nodes.h:723
@ CMD_DELETE
Definition: nodes.h:724
@ CMD_UPDATE
Definition: nodes.h:722
bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: ri_triggers.c:1230
bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: ri_triggers.c:1262
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:2998
struct AfterTriggersTableData * tcs_private
Definition: trigger.h:81
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:76
bool trig_update_after_row
Definition: reltrigger.h:62
bool trig_insert_after_row
Definition: reltrigger.h:57
bool trig_delete_after_row
Definition: reltrigger.h:67
Oid tgconstrindid
Definition: reltrigger.h:34
Oid tgfoid
Definition: reltrigger.h:28
bool tgdeferrable
Definition: reltrigger.h:36
bool tginitdeferred
Definition: reltrigger.h:37
bool tgisclone
Definition: reltrigger.h:32
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:6442
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: trigger.c:3398
static Tuplestorestate * GetAfterTriggersTransitionTable(int event, TupleTableSlot *oldslot, TupleTableSlot *newslot, TransitionCaptureState *transition_capture)
Definition: trigger.c:5384
static void TransitionTableAddTuple(EState *estate, TransitionCaptureState *transition_capture, ResultRelInfo *relinfo, TupleTableSlot *slot, TupleTableSlot *original_insert_tuple, Tuplestorestate *tuplestore)
Definition: trigger.c:5435
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:5475
#define RI_TRIGGER_FK
Definition: trigger.h:279
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:113
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:93
#define RI_TRIGGER_NONE
Definition: trigger.h:280
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:92
#define TRIGGER_EVENT_TRUNCATE
Definition: trigger.h:95
#define RI_TRIGGER_PK
Definition: trigger.h:278
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:708
#define TupIsNull(slot)
Definition: tuptable.h:292

References AFTER_TRIGGER_1CTID, AFTER_TRIGGER_2CTID, AFTER_TRIGGER_CP_UPDATE, AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_INITDEFERRED, afterTriggerAddEvent(), AfterTriggerEnlargeQueryState(), afterTriggers, Assert(), AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_dst_part, AfterTriggerEventData::ate_flags, AfterTriggerEventData::ate_src_part, 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, ExecCopySlot(), ExecGetChildToRootMap(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), execute_attr_map_slot(), GetAfterTriggersTransitionTable(), GetCurrentFDWTuplestore(), i, ItemPointerCopy, ItemPointerSetInvalid, list_member_oid(), AfterTriggersData::maxquerydepth, TriggerDesc::numtriggers, 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_original_insert_tuple, TransitionCaptureState::tcs_private, Trigger::tgconstrindid, Trigger::tgdeferrable, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgisclone, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgtype, TransitionTableAddTuple(), 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().

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5597 of file trigger.c.

5598 {
5599  int my_level = GetCurrentTransactionNestLevel();
5600 
5601  /* If we haven't already done so, initialize our state. */
5602  if (afterTriggers.state == NULL)
5604 
5605  /*
5606  * If in a subtransaction, and we didn't save the current state already,
5607  * save it so it can be restored if the subtransaction aborts.
5608  */
5609  if (my_level > 1 &&
5610  afterTriggers.trans_stack[my_level].state == NULL)
5611  {
5612  afterTriggers.trans_stack[my_level].state =
5614  }
5615 
5616  /*
5617  * Handle SET CONSTRAINTS ALL ...
5618  */
5619  if (stmt->constraints == NIL)
5620  {
5621  /*
5622  * Forget any previous SET CONSTRAINTS commands in this transaction.
5623  */
5625 
5626  /*
5627  * Set the per-transaction ALL state to known.
5628  */
5629  afterTriggers.state->all_isset = true;
5631  }
5632  else
5633  {
5634  Relation conrel;
5635  Relation tgrel;
5636  List *conoidlist = NIL;
5637  List *tgoidlist = NIL;
5638  ListCell *lc;
5639 
5640  /*
5641  * Handle SET CONSTRAINTS constraint-name [, ...]
5642  *
5643  * First, identify all the named constraints and make a list of their
5644  * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5645  * the same name within a schema, the specifications are not
5646  * necessarily unique. Our strategy is to target all matching
5647  * constraints within the first search-path schema that has any
5648  * matches, but disregard matches in schemas beyond the first match.
5649  * (This is a bit odd but it's the historical behavior.)
5650  *
5651  * A constraint in a partitioned table may have corresponding
5652  * constraints in the partitions. Grab those too.
5653  */
5654  conrel = table_open(ConstraintRelationId, AccessShareLock);
5655 
5656  foreach(lc, stmt->constraints)
5657  {
5658  RangeVar *constraint = lfirst(lc);
5659  bool found;
5660  List *namespacelist;
5661  ListCell *nslc;
5662 
5663  if (constraint->catalogname)
5664  {
5665  if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5666  ereport(ERROR,
5667  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5668  errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5669  constraint->catalogname, constraint->schemaname,
5670  constraint->relname)));
5671  }
5672 
5673  /*
5674  * If we're given the schema name with the constraint, look only
5675  * in that schema. If given a bare constraint name, use the
5676  * search path to find the first matching constraint.
5677  */
5678  if (constraint->schemaname)
5679  {
5680  Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5681  false);
5682 
5683  namespacelist = list_make1_oid(namespaceId);
5684  }
5685  else
5686  {
5687  namespacelist = fetch_search_path(true);
5688  }
5689 
5690  found = false;
5691  foreach(nslc, namespacelist)
5692  {
5693  Oid namespaceId = lfirst_oid(nslc);
5694  SysScanDesc conscan;
5695  ScanKeyData skey[2];
5696  HeapTuple tup;
5697 
5698  ScanKeyInit(&skey[0],
5699  Anum_pg_constraint_conname,
5700  BTEqualStrategyNumber, F_NAMEEQ,
5701  CStringGetDatum(constraint->relname));
5702  ScanKeyInit(&skey[1],
5703  Anum_pg_constraint_connamespace,
5704  BTEqualStrategyNumber, F_OIDEQ,
5705  ObjectIdGetDatum(namespaceId));
5706 
5707  conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5708  true, NULL, 2, skey);
5709 
5710  while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5711  {
5713 
5714  if (con->condeferrable)
5715  conoidlist = lappend_oid(conoidlist, con->oid);
5716  else if (stmt->deferred)
5717  ereport(ERROR,
5718  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5719  errmsg("constraint \"%s\" is not deferrable",
5720  constraint->relname)));
5721  found = true;
5722  }
5723 
5724  systable_endscan(conscan);
5725 
5726  /*
5727  * Once we've found a matching constraint we do not search
5728  * later parts of the search path.
5729  */
5730  if (found)
5731  break;
5732  }
5733 
5734  list_free(namespacelist);
5735 
5736  /*
5737  * Not found ?
5738  */
5739  if (!found)
5740  ereport(ERROR,
5741  (errcode(ERRCODE_UNDEFINED_OBJECT),
5742  errmsg("constraint \"%s\" does not exist",
5743  constraint->relname)));
5744  }
5745 
5746  /*
5747  * Scan for any possible descendants of the constraints. We append
5748  * whatever we find to the same list that we're scanning; this has the
5749  * effect that we create new scans for those, too, so if there are
5750  * further descendents, we'll also catch them.
5751  */
5752  foreach(lc, conoidlist)
5753  {
5754  Oid parent = lfirst_oid(lc);
5755  ScanKeyData key;
5756  SysScanDesc scan;
5757  HeapTuple tuple;
5758 
5759  ScanKeyInit(&key,
5760  Anum_pg_constraint_conparentid,
5761  BTEqualStrategyNumber, F_OIDEQ,
5762  ObjectIdGetDatum(parent));
5763 
5764  scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5765 
5766  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5767  {
5769 
5770  conoidlist = lappend_oid(conoidlist, con->oid);
5771  }
5772 
5773  systable_endscan(scan);
5774  }
5775 
5776  table_close(conrel, AccessShareLock);
5777 
5778  /*
5779  * Now, locate the trigger(s) implementing each of these constraints,
5780  * and make a list of their OIDs.
5781  */
5782  tgrel = table_open(TriggerRelationId, AccessShareLock);
5783 
5784  foreach(lc, conoidlist)
5785  {
5786  Oid conoid = lfirst_oid(lc);
5787  ScanKeyData skey;
5788  SysScanDesc tgscan;
5789  HeapTuple htup;
5790 
5791  ScanKeyInit(&skey,
5792  Anum_pg_trigger_tgconstraint,
5793  BTEqualStrategyNumber, F_OIDEQ,
5794  ObjectIdGetDatum(conoid));
5795 
5796  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5797  NULL, 1, &skey);
5798 
5799  while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5800  {
5801  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5802 
5803  /*
5804  * Silently skip triggers that are marked as non-deferrable in
5805  * pg_trigger. This is not an error condition, since a
5806  * deferrable RI constraint may have some non-deferrable
5807  * actions.
5808  */
5809  if (pg_trigger->tgdeferrable)
5810  tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid);
5811  }
5812 
5813  systable_endscan(tgscan);
5814  }
5815 
5816  table_close(tgrel, AccessShareLock);
5817 
5818  /*
5819  * Now we can set the trigger states of individual triggers for this
5820  * xact.
5821  */
5822  foreach(lc, tgoidlist)
5823  {
5824  Oid tgoid = lfirst_oid(lc);
5826  bool found = false;
5827  int i;
5828 
5829  for (i = 0; i < state->numstates; i++)
5830  {
5831  if (state->trigstates[i].sct_tgoid == tgoid)
5832  {
5833  state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5834  found = true;
5835  break;
5836  }
5837  }
5838  if (!found)
5839  {
5841  SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5842  }
5843  }
5844  }
5845 
5846  /*
5847  * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5848  * checks against that constraint must be made when the SET CONSTRAINTS
5849  * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5850  * apply retroactively. We've updated the constraints state, so scan the
5851  * list of previously deferred events to fire any that have now become
5852  * immediate.
5853  *
5854  * Obviously, if this was SET ... DEFERRED then it can't have converted
5855  * any unfired events to immediate, so we need do nothing in that case.
5856  */
5857  if (!stmt->deferred)
5858  {
5860  bool snapshot_set = false;
5861 
5862  while (afterTriggerMarkEvents(events, NULL, true))
5863  {
5864  CommandId firing_id = afterTriggers.firing_counter++;
5865 
5866  /*
5867  * Make sure a snapshot has been established in case trigger
5868  * functions need one. Note that we avoid setting a snapshot if
5869  * we don't find at least one trigger that has to be fired now.
5870  * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
5871  * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
5872  * at the start of a transaction it's not possible for any trigger
5873  * events to be queued yet.)
5874  */
5875  if (!snapshot_set)
5876  {
5878  snapshot_set = true;
5879  }
5880 
5881  /*
5882  * We can delete fired events if we are at top transaction level,
5883  * but we'd better not if inside a subtransaction, since the
5884  * subtransaction could later get rolled back.
5885  */
5886  if (afterTriggerInvokeEvents(events, firing_id, NULL,
5887  !IsSubTransaction()))
5888  break; /* all fired */
5889  }
5890 
5891  if (snapshot_set)
5893  }
5894 }
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2994
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:598
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:505
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:386
Oid MyDatabaseId
Definition: globals.c:89
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:649
List * lappend_oid(List *list, Oid datum)
Definition: list.c:372
void list_free(List *list)
Definition: list.c:1505
#define AccessShareLock
Definition: lockdefs.h:36
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2939
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4431
FormData_pg_constraint * Form_pg_constraint
#define list_make1_oid(x1)
Definition: pg_list.h:236
#define lfirst_oid(lc)
Definition: pg_list.h:171
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
#define CStringGetDatum(X)
Definition: postgres.h:622
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
char * relname
Definition: primnodes.h:68
char * catalogname
Definition: primnodes.h:66
char * schemaname
Definition: primnodes.h:67
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition: trigger.c:5567
static SetConstraintState SetConstraintStateCopy(SetConstraintState state)
Definition: trigger.c:5547
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:5522
bool IsSubTransaction(void)
Definition: xact.c:4839

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

Referenced by standard_ProcessUtility().

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6396 of file trigger.c.

6397 {
6398  bool result;
6399  AfterTriggersTableData *table;
6400 
6401  /* Check state, like AfterTriggerSaveEvent. */
6402  if (afterTriggers.query_depth < 0)
6403  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6404 
6405  /* Be sure we have enough space to record events at this query depth. */
6408 
6409  /*
6410  * We keep this state in the AfterTriggersTableData that also holds
6411  * transition tables for the relation + operation. In this way, if we are
6412  * forced to make a new set of transition tables because more tuples get
6413  * entered after we've already fired triggers, we will allow a new set of
6414  * statement triggers to get queued.
6415  */
6416  table = GetAfterTriggersTableData(relid, cmdType);
6417  result = table->before_trig_done;
6418  table->before_trig_done = true;
6419  return result;
6420 }
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4741

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

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

◆ cancel_prior_stmt_triggers()

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

Definition at line 6442 of file trigger.c.

6443 {
6444  AfterTriggersTableData *table;
6446 
6447  /*
6448  * We keep this state in the AfterTriggersTableData that also holds
6449  * transition tables for the relation + operation. In this way, if we are
6450  * forced to make a new set of transition tables because more tuples get
6451  * entered after we've already fired triggers, we will allow a new set of
6452  * statement triggers to get queued without canceling the old ones.
6453  */
6454  table = GetAfterTriggersTableData(relid, cmdType);
6455 
6456  if (table->after_trig_done)
6457  {
6458  /*
6459  * We want to start scanning from the tail location that existed just
6460  * before we inserted any statement triggers. But the events list
6461  * might've been entirely empty then, in which case scan from the
6462  * current head.
6463  */
6464  AfterTriggerEvent event;
6465  AfterTriggerEventChunk *chunk;
6466 
6467  if (table->after_trig_events.tail)
6468  {
6469  chunk = table->after_trig_events.tail;
6470  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6471  }
6472  else
6473  {
6474  chunk = qs->events.head;
6475  event = NULL;
6476  }
6477 
6478  for_each_chunk_from(chunk)
6479  {
6480  if (event == NULL)
6481  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6482  for_each_event_from(event, chunk)
6483  {
6484  AfterTriggerShared evtshared = GetTriggerSharedData(event);
6485 
6486  /*
6487  * Exit loop when we reach events that aren't AS triggers for
6488  * the target relation.
6489  */
6490  if (evtshared->ats_relid != relid)
6491  goto done;
6492  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6493  goto done;
6494  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6495  goto done;
6496  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6497  goto done;
6498  /* OK, mark it DONE */
6499  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6500  event->ate_flags |= AFTER_TRIGGER_DONE;
6501  }
6502  /* signal we must reinitialize event ptr for next chunk */
6503  event = NULL;
6504  }
6505  }
6506 done:
6507 
6508  /* In any case, save current insertion point for next time */
6509  table->after_trig_done = true;
6510  table->after_trig_events = qs->events;
6511 }
#define for_each_chunk_from(cptr)
Definition: trigger.c:3705
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3707
#define TRIGGER_FIRED_FOR_STATEMENT(event)
Definition: trigger.h:125
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:131

References AfterTriggersTableData::after_trig_done, AfterTriggersTableData::after_trig_events, AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, afterTriggers, 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().

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

Definition at line 2099 of file trigger.c.

2100 {
2101  TriggerDesc *newdesc;
2102  Trigger *trigger;
2103  int i;
2104 
2105  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2106  return NULL;
2107 
2108  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
2109  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2110 
2111  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2112  memcpy(trigger, trigdesc->triggers,
2113  trigdesc->numtriggers * sizeof(Trigger));
2114  newdesc->triggers = trigger;
2115 
2116  for (i = 0; i < trigdesc->numtriggers; i++)
2117  {
2118  trigger->tgname = pstrdup(trigger->tgname);
2119  if (trigger->tgnattr > 0)
2120  {
2121  int16 *newattr;
2122 
2123  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2124  memcpy(newattr, trigger->tgattr,
2125  trigger->tgnattr * sizeof(int16));
2126  trigger->tgattr = newattr;
2127  }
2128  if (trigger->tgnargs > 0)
2129  {
2130  char **newargs;
2131  int16 j;
2132 
2133  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2134  for (j = 0; j < trigger->tgnargs; j++)
2135  newargs[j] = pstrdup(trigger->tgargs[j]);
2136  trigger->tgargs = newargs;
2137  }
2138  if (trigger->tgqual)
2139  trigger->tgqual = pstrdup(trigger->tgqual);
2140  if (trigger->tgoldtable)
2141  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2142  if (trigger->tgnewtable)
2143  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2144  trigger++;
2145  }
2146 
2147  return newdesc;
2148 }
signed short int16
Definition: c.h:428
int j
Definition: isn.c:74
char * pstrdup(const char *in)
Definition: mcxt.c:1305
void * palloc(Size size)
Definition: mcxt.c:1068
char * tgname
Definition: reltrigger.h:27
int16 tgnargs
Definition: reltrigger.h:38
char * tgqual
Definition: reltrigger.h:42
int16 tgnattr
Definition: reltrigger.h:39
char ** tgargs
Definition: reltrigger.h:41
int16 * tgattr
Definition: reltrigger.h:40

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

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

166 {
167  return
168  CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
169  constraintOid, indexOid, funcoid,
170  parentTriggerOid, whenClause, isInternal,
171  in_partition, TRIGGER_FIRES_ON_ORIGIN);
172 }
ObjectAddress CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition: trigger.c:179
#define TRIGGER_FIRES_ON_ORIGIN
Definition: trigger.h:149

References CreateTriggerFiringOn(), and TRIGGER_FIRES_ON_ORIGIN.

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

◆ CreateTriggerFiringOn()

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

Definition at line 179 of file trigger.c.

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

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

Referenced by CloneRowTriggersToPartition(), and CreateTrigger().

◆ EnableDisableTrigger()

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

Definition at line 1768 of file trigger.c.

1770 {
1771  Relation tgrel;
1772  int nkeys;
1773  ScanKeyData keys[2];
1774  SysScanDesc tgscan;
1775  HeapTuple tuple;
1776  bool found;
1777  bool changed;
1778 
1779  /* Scan the relevant entries in pg_triggers */
1780  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1781 
1782  ScanKeyInit(&keys[0],
1783  Anum_pg_trigger_tgrelid,
1784  BTEqualStrategyNumber, F_OIDEQ,
1786  if (tgname)
1787  {
1788  ScanKeyInit(&keys[1],
1789  Anum_pg_trigger_tgname,
1790  BTEqualStrategyNumber, F_NAMEEQ,
1791  CStringGetDatum(tgname));
1792  nkeys = 2;
1793  }
1794  else
1795  nkeys = 1;
1796 
1797  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1798  NULL, nkeys, keys);
1799 
1800  found = changed = false;
1801 
1802  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1803  {
1804  Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
1805 
1806  if (oldtrig->tgisinternal)
1807  {
1808  /* system trigger ... ok to process? */
1809  if (skip_system)
1810  continue;
1811  if (!superuser())
1812  ereport(ERROR,
1813  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1814  errmsg("permission denied: \"%s\" is a system trigger",
1815  NameStr(oldtrig->tgname))));
1816  }
1817 
1818  found = true;
1819 
1820  if (oldtrig->tgenabled != fires_when)
1821  {
1822  /* need to change this one ... make a copy to scribble on */
1823  HeapTuple newtup = heap_copytuple(tuple);
1824  Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
1825 
1826  newtrig->tgenabled = fires_when;
1827 
1828  CatalogTupleUpdate(tgrel, &newtup->t_self, newtup);
1829 
1830  heap_freetuple(newtup);
1831 
1832  changed = true;
1833  }
1834 
1835  InvokeObjectPostAlterHook(TriggerRelationId,
1836  oldtrig->oid, 0);
1837  }
1838 
1839  systable_endscan(tgscan);
1840 
1841  table_close(tgrel, RowExclusiveLock);
1842 
1843  if (tgname && !found)
1844  ereport(ERROR,
1845  (errcode(ERRCODE_UNDEFINED_OBJECT),
1846  errmsg("trigger \"%s\" for table \"%s\" does not exist",
1847  tgname, RelationGetRelationName(rel))));
1848 
1849  /*
1850  * If we changed anything, broadcast a SI inval message to force each
1851  * backend (including our own!) to rebuild relation's relcache entry.
1852  * Otherwise they will fail to apply the change promptly.
1853  */
1854  if (changed)
1856 }
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1363
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:195
bool superuser(void)
Definition: superuser.c:46

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

Referenced by ATExecEnableDisableTrigger().

◆ ExecARDeleteTriggers()

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

Definition at line 2786 of file trigger.c.

2792 {
2793  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2794 
2795  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2796  (transition_capture && transition_capture->tcs_delete_old_table))
2797  {
2798  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2799 
2800  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2801  if (fdw_trigtuple == NULL)
2802  GetTupleForTrigger(estate,
2803  NULL,
2804  relinfo,
2805  tupleid,
2807  slot,
2808  NULL,
2809  NULL);
2810  else
2811  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2812 
2813  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2815  true, slot, NULL, NIL, NULL,
2816  transition_capture,
2817  is_crosspart_update);
2818  }
2819 }
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1469
@ LockTupleExclusive
Definition: lockoptions.h:58
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldtup, TupleTableSlot *newtup, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition: trigger.c:5999
static bool GetTupleForTrigger(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **newSlot, TM_FailureData *tmfpd)
Definition: trigger.c:3277

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

◆ ExecARInsertTriggers()

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

Definition at line 2548 of file trigger.c.

2551 {
2552  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2553 
2554  if ((trigdesc && trigdesc->trig_insert_after_row) ||
2555  (transition_capture && transition_capture->tcs_insert_new_table))
2556  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2558  true, NULL, slot,
2559  recheckIndexes, NULL,
2560  transition_capture,
2561  false);
2562 }

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

◆ ExecARUpdateTriggers()

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

Definition at line 3089 of file trigger.c.

3098 {
3099  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3100 
3101  if ((trigdesc && trigdesc->trig_update_after_row) ||
3102  (transition_capture &&
3103  (transition_capture->tcs_update_old_table ||
3104  transition_capture->tcs_update_new_table)))
3105  {
3106  /*
3107  * Note: if the UPDATE is converted into a DELETE+INSERT as part of
3108  * update-partition-key operation, then this function is also called
3109  * separately for DELETE and INSERT to capture transition table rows.
3110  * In such case, either old tuple or new tuple can be NULL.
3111  */
3112  TupleTableSlot *oldslot;
3113  ResultRelInfo *tupsrc;
3114 
3115  Assert((src_partinfo != NULL && dst_partinfo != NULL) ||
3116  !is_crosspart_update);
3117 
3118  tupsrc = src_partinfo ? src_partinfo : relinfo;
3119  oldslot = ExecGetTriggerOldSlot(estate, tupsrc);
3120 
3121  if (fdw_trigtuple == NULL && ItemPointerIsValid(tupleid))
3122  GetTupleForTrigger(estate,
3123  NULL,
3124  tupsrc,
3125  tupleid,
3127  oldslot,
3128  NULL,
3129  NULL);
3130  else if (fdw_trigtuple != NULL)
3131  ExecForceStoreHeapTuple(fdw_trigtuple, oldslot, false);
3132  else
3133  ExecClearTuple(oldslot);
3134 
3135  AfterTriggerSaveEvent(estate, relinfo,
3136  src_partinfo, dst_partinfo,
3138  true,
3139  oldslot, newslot, recheckIndexes,
3140  ExecGetAllUpdatedCols(relinfo, estate),
3141  transition_capture,
3142  is_crosspart_update);
3143  }
3144 }
Bitmapset * ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition: execUtils.c:1347

References AfterTriggerSaveEvent(), Assert(), ExecClearTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), GetTupleForTrigger(), ItemPointerIsValid, LockTupleExclusive, ResultRelInfo::ri_TrigDesc, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, TriggerDesc::trig_update_after_row, and TRIGGER_EVENT_UPDATE.

Referenced by ExecCrossPartitionUpdateForeignKey(), ExecDeleteEpilogue(), ExecInsert(), ExecSimpleRelationUpdate(), and ExecUpdateEpilogue().

◆ ExecASDeleteTriggers()

void ExecASDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)

Definition at line 2677 of file trigger.c.

2679 {
2680  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2681 
2682  if (trigdesc && trigdesc->trig_delete_after_statement)
2683  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2685  false, NULL, NULL, NIL, NULL, transition_capture,
2686  false);
2687 }
bool trig_delete_after_statement
Definition: reltrigger.h:70

References AfterTriggerSaveEvent(), NIL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_delete_after_statement, and TRIGGER_EVENT_DELETE.

Referenced by fireASTriggers().

◆ ExecASInsertTriggers()

void ExecASInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)

Definition at line 2459 of file trigger.c.

2461 {
2462  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2463 
2464  if (trigdesc && trigdesc->trig_insert_after_statement)
2465  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2467  false, NULL, NULL, NIL, NULL, transition_capture,
2468  false);
2469 }
bool trig_insert_after_statement
Definition: reltrigger.h:60

References AfterTriggerSaveEvent(), NIL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_insert_after_statement, and TRIGGER_EVENT_INSERT.

Referenced by CopyFrom(), and fireASTriggers().

◆ ExecASTruncateTriggers()

void ExecASTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)

Definition at line 3260 of file trigger.c.

3261 {
3262  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3263 
3264  if (trigdesc && trigdesc->trig_truncate_after_statement)
3265  AfterTriggerSaveEvent(estate, relinfo,
3266  NULL, NULL,
3268  false, NULL, NULL, NIL, NULL, NULL,
3269  false);
3270 }
bool trig_truncate_after_statement
Definition: reltrigger.h:73

References AfterTriggerSaveEvent(), NIL, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_truncate_after_statement, and TRIGGER_EVENT_TRUNCATE.

Referenced by ExecuteTruncateGuts().

◆ ExecASUpdateTriggers()

void ExecASUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)

Definition at line 2927 of file trigger.c.

2929 {
2930  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2931 
2932  /* statement-level triggers operate on the parent table */
2933  Assert(relinfo->ri_RootResultRelInfo == NULL);
2934 
2935  if (trigdesc && trigdesc->trig_update_after_statement)
2936  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2938  false, NULL, NULL, NIL,
2939  ExecGetAllUpdatedCols(relinfo, estate),
2940  transition_capture,
2941  false);
2942 }
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:538
bool trig_update_after_statement
Definition: reltrigger.h:65

References AfterTriggerSaveEvent(), Assert(), ExecGetAllUpdatedCols(), NIL, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, TriggerDesc::trig_update_after_statement, and TRIGGER_EVENT_UPDATE.

Referenced by fireASTriggers().

◆ ExecBRDeleteTriggers()

bool ExecBRDeleteTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot **  epqslot 
)

Definition at line 2697 of file trigger.c.

2702 {
2703  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2704  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2705  bool result = true;
2706  TriggerData LocTriggerData = {0};
2707  HeapTuple trigtuple;
2708  bool should_free = false;
2709  int i;
2710 
2711  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2712  if (fdw_trigtuple == NULL)
2713  {
2714  TupleTableSlot *epqslot_candidate = NULL;
2715 
2716  if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2717  LockTupleExclusive, slot, &epqslot_candidate,
2718  NULL))
2719  return false;
2720 
2721  /*
2722  * If the tuple was concurrently updated and the caller of this
2723  * function requested for the updated tuple, skip the trigger
2724  * execution.
2725  */
2726  if (epqslot_candidate != NULL && epqslot != NULL)
2727  {
2728  *epqslot = epqslot_candidate;
2729  return false;
2730  }
2731 
2732  trigtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2733  }
2734  else
2735  {
2736  trigtuple = fdw_trigtuple;
2737  ExecForceStoreHeapTuple(trigtuple, slot, false);
2738  }
2739 
2740  LocTriggerData.type = T_TriggerData;
2741  LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
2744  LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2745  for (i = 0; i < trigdesc->numtriggers; i++)
2746  {
2747  HeapTuple newtuple;
2748  Trigger *trigger = &trigdesc->triggers[i];
2749 
2750  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2751  TRIGGER_TYPE_ROW,
2752  TRIGGER_TYPE_BEFORE,
2753  TRIGGER_TYPE_DELETE))
2754  continue;
2755  if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2756  NULL, slot, NULL))
2757  continue;
2758 
2759  LocTriggerData.tg_trigslot = slot;
2760  LocTriggerData.tg_trigtuple = trigtuple;
2761  LocTriggerData.tg_trigger = trigger;
2762  newtuple = ExecCallTriggerFunc(&LocTriggerData,
2763  i,
2764  relinfo->ri_TrigFunctions,
2765  relinfo->ri_TrigInstrument,
2766  GetPerTupleMemoryContext(estate));
2767  if (newtuple == NULL)
2768  {
2769  result = false; /* tell caller to suppress delete */
2770  break;
2771  }
2772  if (newtuple != trigtuple)
2773  heap_freetuple(newtuple);
2774  }
2775  if (should_free)
2776  heap_freetuple(trigtuple);
2777 
2778  return result;
2779 }
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:542
#define TRIGGER_EVENT_BEFORE
Definition: trigger.h:100

References Assert(), ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), HeapTupleIsValid, i, ItemPointerIsValid, LockTupleExclusive, TriggerDesc::numtriggers, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, T_TriggerData, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgtype, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by ExecDeletePrologue(), and ExecSimpleRelationDelete().

◆ ExecBRInsertTriggers()

bool ExecBRInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot 
)

Definition at line 2472 of file trigger.c.

2474 {
2475  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2476  HeapTuple newtuple = NULL;
2477  bool should_free;
2478  TriggerData LocTriggerData = {0};
2479  int i;
2480 
2481  LocTriggerData.type = T_TriggerData;
2482  LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
2485  LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2486  for (i = 0; i < trigdesc->numtriggers; i++)
2487  {
2488  Trigger *trigger = &trigdesc->triggers[i];
2489  HeapTuple oldtuple;
2490 
2491  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2492  TRIGGER_TYPE_ROW,
2493  TRIGGER_TYPE_BEFORE,
2494  TRIGGER_TYPE_INSERT))
2495  continue;
2496  if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2497  NULL, NULL, slot))
2498  continue;
2499 
2500  if (!newtuple)
2501  newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2502 
2503  LocTriggerData.tg_trigslot = slot;
2504  LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2505  LocTriggerData.tg_trigger = trigger;
2506  newtuple = ExecCallTriggerFunc(&LocTriggerData,
2507  i,
2508  relinfo->ri_TrigFunctions,
2509  relinfo->ri_TrigInstrument,
2510  GetPerTupleMemoryContext(estate));
2511  if (newtuple == NULL)
2512  {
2513  if (should_free)
2514  heap_freetuple(oldtuple);
2515  return false; /* "do nothing" */
2516  }
2517  else if (newtuple != oldtuple)
2518  {
2519  ExecForceStoreHeapTuple(newtuple, slot, false);
2520 
2521  /*
2522  * After a tuple in a partition goes through a trigger, the user
2523  * could have changed the partition key enough that the tuple no
2524  * longer fits the partition. Verify that.
2525  */
2526  if (trigger->tgisclone &&
2527  !ExecPartitionCheck(relinfo, slot, estate, false))
2528  ereport(ERROR,
2529  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2530  errmsg("moving row to another partition during a BEFORE FOR EACH ROW trigger is not supported"),
2531  errdetail("Before executing trigger \"%s\", the row was to be in partition \"%s.%s\".",
2532  trigger->tgname,
2535 
2536  if (should_free)
2537  heap_freetuple(oldtuple);
2538 
2539  /* signal tuple should be re-fetched if used */
2540  newtuple = NULL;
2541  }
2542  }
2543 
2544  return true;
2545 }
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1782
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3326

References ereport, errcode(), errdetail(), errmsg(), ERROR, ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecPartitionCheck(), get_namespace_name(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, RelationGetNamespace, RelationGetRelationName, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, T_TriggerData, TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgisclone, Trigger::tgname, Trigger::tgtype, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

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

◆ ExecBRUpdateTriggers()

bool ExecBRUpdateTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot newslot,
TM_FailureData tmfd 
)

Definition at line 2945 of file trigger.c.

2951 {
2952  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2953  TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
2954  HeapTuple newtuple = NULL;
2955  HeapTuple trigtuple;
2956  bool should_free_trig = false;
2957  bool should_free_new = false;
2958  TriggerData LocTriggerData = {0};
2959  int i;
2960  Bitmapset *updatedCols;
2961  LockTupleMode lockmode;
2962 
2963  /* Determine lock mode to use */
2964  lockmode = ExecUpdateLockMode(estate, relinfo);
2965 
2966  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2967  if (fdw_trigtuple == NULL)
2968  {
2969  TupleTableSlot *epqslot_candidate = NULL;
2970 
2971  /* get a copy of the on-disk tuple we are planning to update */
2972  if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2973  lockmode, oldslot, &epqslot_candidate,
2974  tmfd))
2975  return false; /* cancel the update action */
2976 
2977  /*
2978  * In READ COMMITTED isolation level it's possible that target tuple
2979  * was changed due to concurrent update. In that case we have a raw
2980  * subplan output tuple in epqslot_candidate, and need to form a new
2981  * insertable tuple using ExecGetUpdateNewTuple to replace the one we
2982  * received in newslot. Neither we nor our callers have any further
2983  * interest in the passed-in tuple, so it's okay to overwrite newslot
2984  * with the newer data.
2985  *
2986  * (Typically, newslot was also generated by ExecGetUpdateNewTuple, so
2987  * that epqslot_clean will be that same slot and the copy step below
2988  * is not needed.)
2989  */
2990  if (epqslot_candidate != NULL)
2991  {
2992  TupleTableSlot *epqslot_clean;
2993 
2994  epqslot_clean = ExecGetUpdateNewTuple(relinfo, epqslot_candidate,
2995  oldslot);
2996 
2997  if (newslot != epqslot_clean)
2998  ExecCopySlot(newslot, epqslot_clean);
2999  }
3000 
3001  trigtuple = ExecFetchSlotHeapTuple(oldslot, true, &should_free_trig);
3002  }
3003  else
3004  {
3005  ExecForceStoreHeapTuple(fdw_trigtuple, oldslot, false);
3006  trigtuple = fdw_trigtuple;
3007  }
3008 
3009  LocTriggerData.type = T_TriggerData;
3010  LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
3013  LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3014  updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
3015  LocTriggerData.tg_updatedcols = updatedCols;
3016  for (i = 0; i < trigdesc->numtriggers; i++)
3017  {
3018  Trigger *trigger = &trigdesc->triggers[i];
3019  HeapTuple oldtuple;
3020 
3021  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3022  TRIGGER_TYPE_ROW,
3023  TRIGGER_TYPE_BEFORE,
3024  TRIGGER_TYPE_UPDATE))
3025  continue;
3026  if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.