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/guc_hooks.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/plancache.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 **epqslot, TM_FailureData *tmfdp)
 
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 *oldslot, TupleTableSlot *newslot, 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, bool recurse, 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 origstate)
 
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)
 
void assign_session_replication_role (int newval, void *extra)
 
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 3592 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3593 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3594 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3587 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3591 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3590 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3588 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3586 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3595 of file trigger.c.

◆ CHUNK_DATA_START

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

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

◆ for_each_chunk_from

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

Definition at line 3696 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:3674
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3646
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3608

Definition at line 3687 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 3692 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 3698 of file trigger.c.

◆ GetTriggerSharedData

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

Definition at line 3655 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:3595
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3592
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3593
#define AFTER_TRIGGER_CP_UPDATE
Definition: trigger.c:3594

Definition at line 3646 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3608 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3596 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3608 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3608 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3608 of file trigger.c.

◆ SetConstraintState

Definition at line 3539 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3518 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3584 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 3967 of file trigger.c.

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

4955 {
4956  /* Increase the query stack depth */
4958 }

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5237 of file trigger.c.

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

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

4923 {
4924  /*
4925  * Initialize after-trigger state structure to empty
4926  */
4927  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4929 
4930  /*
4931  * Verify that there is no leftover state remaining. If these assertions
4932  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4933  * up properly.
4934  */
4935  Assert(afterTriggers.state == NULL);
4936  Assert(afterTriggers.query_stack == NULL);
4938  Assert(afterTriggers.event_cxt == NULL);
4939  Assert(afterTriggers.events.head == NULL);
4940  Assert(afterTriggers.trans_stack == NULL);
4942 }
uint32 CommandId
Definition: c.h:602
SetConstraintState state
Definition: trigger.c:3786
AfterTriggersQueryData * query_stack
Definition: trigger.c:3791

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

3922 {
3923  Oid tgoid = evtshared->ats_tgoid;
3925  int i;
3926 
3927  /*
3928  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3929  * constraints declared NOT DEFERRABLE), the state is always false.
3930  */
3931  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3932  return false;
3933 
3934  /*
3935  * If constraint state exists, SET CONSTRAINTS might have been executed
3936  * either for this trigger or for all triggers.
3937  */
3938  if (state != NULL)
3939  {
3940  /* Check for SET CONSTRAINTS for this specific trigger. */
3941  for (i = 0; i < state->numstates; i++)
3942  {
3943  if (state->trigstates[i].sct_tgoid == tgoid)
3944  return state->trigstates[i].sct_tgisdeferred;
3945  }
3946 
3947  /* Check for SET CONSTRAINTS ALL. */
3948  if (state->all_isset)
3949  return state->all_isdeferred;
3950  }
3951 
3952  /*
3953  * Otherwise return the default state for the trigger.
3954  */
3955  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3956 }
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 4144 of file trigger.c.

4145 {
4146  AfterTriggerEventChunk *target = qs->events.head;
4147  ListCell *lc;
4148 
4149  Assert(target && target->next);
4150 
4151  /*
4152  * First, update any pointers in the per-table data, so that they won't be
4153  * dangling. Resetting obsoleted pointers to NULL will make
4154  * cancel_prior_stmt_triggers start from the list head, which is fine.
4155  */
4156  foreach(lc, qs->tables)
4157  {
4159 
4160  if (table->after_trig_done &&
4161  table->after_trig_events.tail == target)
4162  {
4163  table->after_trig_events.head = NULL;
4164  table->after_trig_events.tail = NULL;
4165  table->after_trig_events.tailfree = NULL;
4166  }
4167  }
4168 
4169  /* Now we can flush the head chunk */
4170  qs->events.head = target->next;
4171  pfree(target);
4172 }
void pfree(void *pointer)
Definition: mcxt.c:1306
#define lfirst(lc)
Definition: pg_list.h:170
AfterTriggerEventList events
Definition: trigger.c:3802
AfterTriggerEventList after_trig_events
Definition: trigger.c:3824

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

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

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

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

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

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

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

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

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

4216 {
4217  Relation rel = relInfo->ri_RelationDesc;
4218  Relation src_rel = src_relInfo->ri_RelationDesc;
4219  Relation dst_rel = dst_relInfo->ri_RelationDesc;
4220  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4221  Oid tgoid = evtshared->ats_tgoid;
4222  TriggerData LocTriggerData = {0};
4223  HeapTuple rettuple;
4224  int tgindx;
4225  bool should_free_trig = false;
4226  bool should_free_new = false;
4227 
4228  /*
4229  * Locate trigger in trigdesc.
4230  */
4231  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4232  {
4233  if (trigdesc->triggers[tgindx].tgoid == tgoid)
4234  {
4235  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4236  break;
4237  }
4238  }
4239  if (LocTriggerData.tg_trigger == NULL)
4240  elog(ERROR, "could not find trigger %u", tgoid);
4241 
4242  /*
4243  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4244  * to include time spent re-fetching tuples in the trigger cost.
4245  */
4246  if (instr)
4247  InstrStartNode(instr + tgindx);
4248 
4249  /*
4250  * Fetch the required tuple(s).
4251  */
4252  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4253  {
4255  {
4256  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4257 
4258  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4259  trig_tuple_slot1))
4260  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4261 
4262  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4264  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4265  trig_tuple_slot2))
4266  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4267  }
4268  /* fall through */
4270 
4271  /*
4272  * Store tuple in the slot so that tg_trigtuple does not reference
4273  * tuplestore memory. (It is formally possible for the trigger
4274  * function to queue trigger events that add to the same
4275  * tuplestore, which can push other tuples out of memory.) The
4276  * distinction is academic, because we start with a minimal tuple
4277  * that is stored as a heap tuple, constructed in different memory
4278  * context, in the slot anyway.
4279  */
4280  LocTriggerData.tg_trigslot = trig_tuple_slot1;
4281  LocTriggerData.tg_trigtuple =
4282  ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
4283 
4284  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4286  {
4287  LocTriggerData.tg_newslot = trig_tuple_slot2;
4288  LocTriggerData.tg_newtuple =
4289  ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new);
4290  }
4291  else
4292  {
4293  LocTriggerData.tg_newtuple = NULL;
4294  }
4295  break;
4296 
4297  default:
4298  if (ItemPointerIsValid(&(event->ate_ctid1)))
4299  {
4300  TupleTableSlot *src_slot = ExecGetTriggerOldSlot(estate,
4301  src_relInfo);
4302 
4303  if (!table_tuple_fetch_row_version(src_rel,
4304  &(event->ate_ctid1),
4305  SnapshotAny,
4306  src_slot))
4307  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4308 
4309  /*
4310  * Store the tuple fetched from the source partition into the
4311  * target (root partitioned) table slot, converting if needed.
4312  */
4313  if (src_relInfo != relInfo)
4314  {
4315  TupleConversionMap *map = ExecGetChildToRootMap(src_relInfo);
4316 
4317  LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4318  if (map)
4319  {
4321  src_slot,
4322  LocTriggerData.tg_trigslot);
4323  }
4324  else
4325  ExecCopySlot(LocTriggerData.tg_trigslot, src_slot);
4326  }
4327  else
4328  LocTriggerData.tg_trigslot = src_slot;
4329  LocTriggerData.tg_trigtuple =
4330  ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, &should_free_trig);
4331  }
4332  else
4333  {
4334  LocTriggerData.tg_trigtuple = NULL;
4335  }
4336 
4337  /* don't touch ctid2 if not there */
4339  (event->ate_flags & AFTER_TRIGGER_CP_UPDATE)) &&
4340  ItemPointerIsValid(&(event->ate_ctid2)))
4341  {
4342  TupleTableSlot *dst_slot = ExecGetTriggerNewSlot(estate,
4343  dst_relInfo);
4344 
4345  if (!table_tuple_fetch_row_version(dst_rel,
4346  &(event->ate_ctid2),
4347  SnapshotAny,
4348  dst_slot))
4349  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4350 
4351  /*
4352  * Store the tuple fetched from the destination partition into
4353  * the target (root partitioned) table slot, converting if
4354  * needed.
4355  */
4356  if (dst_relInfo != relInfo)
4357  {
4358  TupleConversionMap *map = ExecGetChildToRootMap(dst_relInfo);
4359 
4360  LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4361  if (map)
4362  {
4364  dst_slot,
4365  LocTriggerData.tg_newslot);
4366  }
4367  else
4368  ExecCopySlot(LocTriggerData.tg_newslot, dst_slot);
4369  }
4370  else
4371  LocTriggerData.tg_newslot = dst_slot;
4372  LocTriggerData.tg_newtuple =
4373  ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, &should_free_new);
4374  }
4375  else
4376  {
4377  LocTriggerData.tg_newtuple = NULL;
4378  }
4379  }
4380 
4381  /*
4382  * Set up the tuplestore information to let the trigger have access to
4383  * transition tables. When we first make a transition table available to
4384  * a trigger, mark it "closed" so that it cannot change anymore. If any
4385  * additional events of the same type get queued in the current trigger
4386  * query level, they'll go into new transition tables.
4387  */
4388  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4389  if (evtshared->ats_table)
4390  {
4391  if (LocTriggerData.tg_trigger->tgoldtable)
4392  {
4393  if (TRIGGER_FIRED_BY_UPDATE(evtshared->ats_event))
4394  LocTriggerData.tg_oldtable = evtshared->ats_table->old_upd_tuplestore;
4395  else
4396  LocTriggerData.tg_oldtable = evtshared->ats_table->old_del_tuplestore;
4397  evtshared->ats_table->closed = true;
4398  }
4399 
4400  if (LocTriggerData.tg_trigger->tgnewtable)
4401  {
4402  if (TRIGGER_FIRED_BY_INSERT(evtshared->ats_event))
4403  LocTriggerData.tg_newtable = evtshared->ats_table->new_ins_tuplestore;
4404  else
4405  LocTriggerData.tg_newtable = evtshared->ats_table->new_upd_tuplestore;
4406  evtshared->ats_table->closed = true;
4407  }
4408  }
4409 
4410  /*
4411  * Setup the remaining trigger information
4412  */
4413  LocTriggerData.type = T_TriggerData;
4414  LocTriggerData.tg_event =
4416  LocTriggerData.tg_relation = rel;
4417  if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
4418  LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
4419 
4420  MemoryContextReset(per_tuple_context);
4421 
4422  /*
4423  * Call the trigger and throw away any possibly returned updated tuple.
4424  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4425  */
4426  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4427  tgindx,
4428  finfo,
4429  NULL,
4430  per_tuple_context);
4431  if (rettuple != NULL &&
4432  rettuple != LocTriggerData.tg_trigtuple &&
4433  rettuple != LocTriggerData.tg_newtuple)
4434  heap_freetuple(rettuple);
4435 
4436  /*
4437  * Release resources
4438  */
4439  if (should_free_trig)
4440  heap_freetuple(LocTriggerData.tg_trigtuple);
4441  if (should_free_new)
4442  heap_freetuple(LocTriggerData.tg_newtuple);
4443 
4444  /* don't clear slots' contents if foreign table */
4445  if (trig_tuple_slot1 == NULL)
4446  {
4447  if (LocTriggerData.tg_trigslot)
4448  ExecClearTuple(LocTriggerData.tg_trigslot);
4449  if (LocTriggerData.tg_newslot)
4450  ExecClearTuple(LocTriggerData.tg_newslot);
4451  }
4452 
4453  /*
4454  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4455  * one "tuple returned" (really the number of firings).
4456  */
4457  if (instr)
4458  InstrStopNode(instr + tgindx, 1);
4459 }
#define ERROR
Definition: elog.h:35
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1644
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1167
TupleTableSlot * ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1189
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1235
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
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:303
#define SnapshotAny
Definition: snapmgr.h:67
ItemPointerData ate_ctid2
Definition: trigger.c:3614
ItemPointerData ate_ctid1
Definition: trigger.c:3613
Bitmapset * ats_modifiedcols
Definition: trigger.c:3605
Tuplestorestate * old_upd_tuplestore
Definition: trigger.c:3834
Tuplestorestate * new_upd_tuplestore
Definition: trigger.c:3836
Tuplestorestate * old_del_tuplestore
Definition: trigger.c:3838
Tuplestorestate * new_ins_tuplestore
Definition: trigger.c:3840
Relation ri_RelationDesc
Definition: execnodes.h:448
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:3591
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3885
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition: trigger.c:2307
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3590
#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:433
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:483

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, 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 5133 of file trigger.c.

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

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

4084 {
4085  AfterTriggerEventChunk *chunk;
4086 
4087  while ((chunk = events->head) != NULL)
4088  {
4089  events->head = chunk->next;
4090  pfree(chunk);
4091  }
4092  events->tail = NULL;
4093  events->tailfree = NULL;
4094 }

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

5066 {
5067  Tuplestorestate *ts;
5068  List *tables;
5069  ListCell *lc;
5070 
5071  /* Drop the trigger events */
5073 
5074  /* Drop FDW tuplestore if any */
5075  ts = qs->fdw_tuplestore;
5076  qs->fdw_tuplestore = NULL;
5077  if (ts)
5078  tuplestore_end(ts);
5079 
5080  /* Release per-table subsidiary storage */
5081  tables = qs->tables;
5082  foreach(lc, tables)
5083  {
5085 
5086  ts = table->old_upd_tuplestore;
5087  table->old_upd_tuplestore = NULL;
5088  if (ts)
5089  tuplestore_end(ts);
5090  ts = table->new_upd_tuplestore;
5091  table->new_upd_tuplestore = NULL;
5092  if (ts)
5093  tuplestore_end(ts);
5094  ts = table->old_del_tuplestore;
5095  table->old_del_tuplestore = NULL;
5096  if (ts)
5097  tuplestore_end(ts);
5098  ts = table->new_ins_tuplestore;
5099  table->new_ins_tuplestore = NULL;
5100  if (ts)
5101  tuplestore_end(ts);
5102  if (table->storeslot)
5103  {
5104  TupleTableSlot *slot = table->storeslot;
5105 
5106  table->storeslot = NULL;
5108  }
5109  }
5110 
5111  /*
5112  * Now free the AfterTriggersTableData structs and list cells. Reset list
5113  * pointer first; if list_free_deep somehow gets an error, better to leak
5114  * that storage than have an infinite loop.
5115  */
5116  qs->tables = NIL;
5117  list_free_deep(tables);
5118 }
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
void list_free_deep(List *list)
Definition: list.c:1559
TupleTableSlot * storeslot
Definition: trigger.c:3842
Definition: pg_list.h:52
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4083
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 4561 of file trigger.c.

4565 {
4566  bool all_fired = true;
4567  AfterTriggerEventChunk *chunk;
4568  MemoryContext per_tuple_context;
4569  bool local_estate = false;
4570  ResultRelInfo *rInfo = NULL;
4571  Relation rel = NULL;
4572  TriggerDesc *trigdesc = NULL;
4573  FmgrInfo *finfo = NULL;
4574  Instrumentation *instr = NULL;
4575  TupleTableSlot *slot1 = NULL,
4576  *slot2 = NULL;
4577 
4578  /* Make a local EState if need be */
4579  if (estate == NULL)
4580  {
4581  estate = CreateExecutorState();
4582  local_estate = true;
4583  }
4584 
4585  /* Make a per-tuple memory context for trigger function calls */
4586  per_tuple_context =
4588  "AfterTriggerTupleContext",
4590 
4591  for_each_chunk(chunk, *events)
4592  {
4593  AfterTriggerEvent event;
4594  bool all_fired_in_chunk = true;
4595 
4596  for_each_event(event, chunk)
4597  {
4598  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4599 
4600  /*
4601  * Is it one for me to fire?
4602  */
4603  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4604  evtshared->ats_firing_id == firing_id)
4605  {
4606  ResultRelInfo *src_rInfo,
4607  *dst_rInfo;
4608 
4609  /*
4610  * So let's fire it... but first, find the correct relation if
4611  * this is not the same relation as before.
4612  */
4613  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4614  {
4615  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4616  NULL);
4617  rel = rInfo->ri_RelationDesc;
4618  /* Catch calls with insufficient relcache refcounting */
4620  trigdesc = rInfo->ri_TrigDesc;
4621  finfo = rInfo->ri_TrigFunctions;
4622  instr = rInfo->ri_TrigInstrument;
4623  if (slot1 != NULL)
4624  {
4627  slot1 = slot2 = NULL;
4628  }
4629  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4630  {
4631  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4633  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4635  }
4636  if (trigdesc == NULL) /* should not happen */
4637  elog(ERROR, "relation %u has no triggers",
4638  evtshared->ats_relid);
4639  }
4640 
4641  /*
4642  * Look up source and destination partition result rels of a
4643  * cross-partition update event.
4644  */
4645  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4647  {
4648  Assert(OidIsValid(event->ate_src_part) &&
4649  OidIsValid(event->ate_dst_part));
4650  src_rInfo = ExecGetTriggerResultRel(estate,
4651  event->ate_src_part,
4652  rInfo);
4653  dst_rInfo = ExecGetTriggerResultRel(estate,
4654  event->ate_dst_part,
4655  rInfo);
4656  }
4657  else
4658  src_rInfo = dst_rInfo = rInfo;
4659 
4660  /*
4661  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4662  * still set, so recursive examinations of the event list
4663  * won't try to re-fire it.
4664  */
4665  AfterTriggerExecute(estate, event, rInfo,
4666  src_rInfo, dst_rInfo,
4667  trigdesc, finfo, instr,
4668  per_tuple_context, slot1, slot2);
4669 
4670  /*
4671  * Mark the event as done.
4672  */
4673  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4674  event->ate_flags |= AFTER_TRIGGER_DONE;
4675  }
4676  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4677  {
4678  /* something remains to be done */
4679  all_fired = all_fired_in_chunk = false;
4680  }
4681  }
4682 
4683  /* Clear the chunk if delete_ok and nothing left of interest */
4684  if (delete_ok && all_fired_in_chunk)
4685  {
4686  chunk->freeptr = CHUNK_DATA_START(chunk);
4687  chunk->endfree = chunk->endptr;
4688 
4689  /*
4690  * If it's last chunk, must sync event list's tailfree too. Note
4691  * that delete_ok must NOT be passed as true if there could be
4692  * additional AfterTriggerEventList values pointing at this event
4693  * list, since we'd fail to fix their copies of tailfree.
4694  */
4695  if (chunk == events->tail)
4696  events->tailfree = chunk->freeptr;
4697  }
4698  }
4699  if (slot1 != NULL)
4700  {
4703  }
4704 
4705  /* Release working resources */
4706  MemoryContextDelete(per_tuple_context);
4707 
4708  if (local_estate)
4709  {
4710  ExecCloseResultRelations(estate);
4711  ExecResetTupleTable(estate->es_tupleTable, false);
4712  FreeExecutorState(estate);
4713  }
4714 
4715  return all_fired;
4716 }
#define OidIsValid(objectId)
Definition: c.h:711
void ExecCloseResultRelations(EState *estate)
Definition: execMain.c:1508
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid, ResultRelInfo *rootRelInfo)
Definition: execMain.c:1287
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:187
MemoryContext CurrentMemoryContext
Definition: mcxt.c:124
#define RelationHasReferenceCountZero(relation)
Definition: rel.h:485
#define RelationGetRelid(relation)
Definition: rel.h:501
List * es_tupleTable
Definition: execnodes.h:652
Definition: fmgr.h:57
TupleDesc rd_att
Definition: rel.h:111
Form_pg_class rd_rel
Definition: rel.h:110
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:484
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:475
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:478
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:4206
#define for_each_event(eptr, cptr)
Definition: trigger.c:3687
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3685

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

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

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

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

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

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

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 oldslot,
TupleTableSlot newslot,
List recheckIndexes,
Bitmapset modifiedCols,
TransitionCaptureState transition_capture,
bool  is_crosspart_update 
)
static

Definition at line 5997 of file trigger.c.

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

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

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

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

◆ assign_session_replication_role()

void assign_session_replication_role ( int  newval,
void *  extra 
)

Definition at line 6515 of file trigger.c.

6516 {
6517  /*
6518  * Must flush the plan cache when changing replication role; but don't
6519  * flush unnecessarily.
6520  */
6522  ResetPlanCache();
6523 }
#define newval
void ResetPlanCache(void)
Definition: plancache.c:2149
int SessionReplicationRole
Definition: trigger.c:70

References newval, ResetPlanCache(), and SessionReplicationRole.

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 6394 of file trigger.c.

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

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

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

2091 {
2092  TriggerDesc *newdesc;
2093  Trigger *trigger;
2094  int i;
2095 
2096  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2097  return NULL;
2098 
2099  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
2100  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2101 
2102  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2103  memcpy(trigger, trigdesc->triggers,
2104  trigdesc->numtriggers * sizeof(Trigger));
2105  newdesc->triggers = trigger;
2106 
2107  for (i = 0; i < trigdesc->numtriggers; i++)
2108  {
2109  trigger->tgname = pstrdup(trigger->tgname);
2110  if (trigger->tgnattr > 0)
2111  {
2112  int16 *newattr;
2113 
2114  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2115  memcpy(newattr, trigger->tgattr,
2116  trigger->tgnattr * sizeof(int16));
2117  trigger->tgattr = newattr;
2118  }
2119  if (trigger->tgnargs > 0)
2120  {
2121  char **newargs;
2122  int16 j;
2123 
2124  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2125  for (j = 0; j < trigger->tgnargs; j++)
2126  newargs[j] = pstrdup(trigger->tgargs[j]);
2127  trigger->tgargs = newargs;
2128  }
2129  if (trigger->tgqual)
2130  trigger->tgqual = pstrdup(trigger->tgqual);
2131  if (trigger->tgoldtable)
2132  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2133  if (trigger->tgnewtable)
2134  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2135  trigger++;
2136  }
2137 
2138  return newdesc;
2139 }
signed short int16
Definition: c.h:429
int j
Definition: isn.c:74
char * pstrdup(const char *in)
Definition: mcxt.c:1483
void * palloc(Size size)
Definition: mcxt.c:1199
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 164 of file trigger.c.

168 {
169  return
170  CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
171  constraintOid, indexOid, funcoid,
172  parentTriggerOid, whenClause, isInternal,
173  in_partition, TRIGGER_FIRES_ON_ORIGIN);
174 }
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:181
#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 181 of file trigger.c.

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

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(), 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, CreateTrigStmt::initdeferred, Int16GetDatum(), InvalidAttrNumber, InvalidOid, InvokeObjectPostCreateHookArg, CreateTrigStmt::isconstraint, TriggerTransition::isNew, IsSystemRelation(), TriggerTransition::isTable, j, len, lfirst, lfirst_node, 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_aclcheck(), OBJECT_FUNCTION, ObjectAddressSet, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, OidIsValid, PartitionDescData::oids, ParseState::p_rtable, ParseState::p_sourcetext, palloc(), parser_errposition(), pfree(), pg_class_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,
bool  recurse,
LOCKMODE  lockmode 
)

Definition at line 1730 of file trigger.c.

1733 {
1734  Relation tgrel;
1735  int nkeys;
1736  ScanKeyData keys[2];
1737  SysScanDesc tgscan;
1738  HeapTuple tuple;
1739  bool found;
1740  bool changed;
1741 
1742  /* Scan the relevant entries in pg_triggers */
1743  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
1744 
1745  ScanKeyInit(&keys[0],
1746  Anum_pg_trigger_tgrelid,
1747  BTEqualStrategyNumber, F_OIDEQ,
1749  if (tgname)
1750  {
1751  ScanKeyInit(&keys[1],
1752  Anum_pg_trigger_tgname,
1753  BTEqualStrategyNumber, F_NAMEEQ,
1754  CStringGetDatum(tgname));
1755  nkeys = 2;
1756  }
1757  else
1758  nkeys = 1;
1759 
1760  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1761  NULL, nkeys, keys);
1762 
1763  found = changed = false;
1764 
1765  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1766  {
1767  Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
1768 
1769  if (oldtrig->tgisinternal)
1770  {
1771  /* system trigger ... ok to process? */
1772  if (skip_system)
1773  continue;
1774  if (!superuser())
1775  ereport(ERROR,
1776  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1777  errmsg("permission denied: \"%s\" is a system trigger",
1778  NameStr(oldtrig->tgname))));
1779  }
1780 
1781  found = true;
1782 
1783  if (oldtrig->tgenabled != fires_when)
1784  {
1785  /* need to change this one ... make a copy to scribble on */
1786  HeapTuple newtup = heap_copytuple(tuple);
1787  Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
1788 
1789  newtrig->tgenabled = fires_when;
1790 
1791  CatalogTupleUpdate(tgrel, &newtup->t_self, newtup);
1792 
1793  heap_freetuple(newtup);
1794 
1795  changed = true;
1796  }
1797 
1798  /*
1799  * When altering FOR EACH ROW triggers on a partitioned table, do the
1800  * same on the partitions as well, unless ONLY is specified.
1801  *
1802  * Note that we recurse even if we didn't change the trigger above,
1803  * because the partitions' copy of the trigger may have a different
1804  * value of tgenabled than the parent's trigger and thus might need to
1805  * be changed.
1806  */
1807  if (recurse &&
1808  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
1809  (TRIGGER_FOR_ROW(oldtrig->tgtype)))
1810  {
1811  PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1812  int i;
1813 
1814  for (i = 0; i < partdesc->nparts; i++)
1815  {
1816  Relation part;
1817 
1818  part = relation_open(partdesc->oids[i], lockmode);
1819  EnableDisableTrigger(part, NameStr(oldtrig->tgname),
1820  fires_when, skip_system, recurse,
1821  lockmode);
1822  table_close(part, NoLock); /* keep lock till commit */
1823  }
1824  }
1825 
1826  InvokeObjectPostAlterHook(TriggerRelationId,
1827  oldtrig->oid, 0);
1828  }
1829 
1830  systable_endscan(tgscan);
1831 
1832  table_close(tgrel, RowExclusiveLock);
1833 
1834  if (tgname && !found)
1835  ereport(ERROR,
1836  (errcode(ERRCODE_UNDEFINED_OBJECT),
1837  errmsg("trigger \"%s\" for table \"%s\" does not exist",
1838  tgname, RelationGetRelationName(rel))));
1839 
1840  /*
1841  * If we changed anything, broadcast a SI inval message to force each
1842  * backend (including our own!) to rebuild relation's relcache entry.
1843  * Otherwise they will fail to apply the change promptly.
1844  */
1845  if (changed)
1847 }
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1363
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
bool superuser(void)
Definition: superuser.c:46
void EnableDisableTrigger(Relation rel, const char *tgname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: trigger.c:1730

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, i, InvokeObjectPostAlterHook, NameStr, NoLock, PartitionDescData::nparts, ObjectIdGetDatum(), PartitionDescData::oids, RelationData::rd_rel, relation_open(), RelationGetPartitionDesc(), 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 2777 of file trigger.c.

2783 {
2784  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2785 
2786  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2787  (transition_capture && transition_capture->tcs_delete_old_table))
2788  {
2789  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2790 
2791  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2792  if (fdw_trigtuple == NULL)
2793  GetTupleForTrigger(estate,
2794  NULL,
2795  relinfo,
2796  tupleid,
2798  slot,
2799  NULL,
2800  NULL);
2801  else
2802  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2803 
2804  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2806  true, slot, NULL, NIL, NULL,
2807  transition_capture,
2808  is_crosspart_update);
2809  }
2810 }
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1469
@ LockTupleExclusive
Definition: lockoptions.h:58
static bool GetTupleForTrigger(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **epqslot, TM_FailureData *tmfdp)
Definition: trigger.c:3268
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldslot, TupleTableSlot *newslot, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition: trigger.c:5997

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

2542 {
2543  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2544 
2545  if ((trigdesc && trigdesc->trig_insert_after_row) ||
2546  (transition_capture && transition_capture->tcs_insert_new_table))
2547  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2549  true, NULL, slot,
2550  recheckIndexes, NULL,
2551  transition_capture,
2552  false);
2553 }

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

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

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

2670 {
2671  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2672 
2673  if (trigdesc && trigdesc->trig_delete_after_statement)
2674  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2676  false, NULL, NULL, NIL, NULL, transition_capture,
2677  false);
2678 }
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 2450 of file trigger.c.

2452 {
2453  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2454 
2455  if (trigdesc && trigdesc->trig_insert_after_statement)
2456  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2458  false, NULL, NULL, NIL, NULL, transition_capture,
2459  false);
2460 }
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 3251 of file trigger.c.

3252 {
3253  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3254 
3255  if (trigdesc && trigdesc->trig_truncate_after_statement)
3256  AfterTriggerSaveEvent(estate, relinfo,
3257  NULL, NULL,
3259  false, NULL, NULL, NIL, NULL, NULL,
3260  false);
3261 }
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 2918 of file trigger.c.

2920 {
2921  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2922 
2923  /* statement-level triggers operate on the parent table */
2924  Assert(relinfo->ri_RootResultRelInfo == NULL);
2925 
2926  if (trigdesc && trigdesc->trig_update_after_statement)
2927  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2929  false, NULL, NULL, NIL,
2930  ExecGetAllUpdatedCols(relinfo, estate),
2931  transition_capture,
2932  false);
2933 }
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:553
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 2688 of file trigger.c.

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

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

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, 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 2936 of file trigger.c.

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