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_Result *tmresultp, 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, Oid tgparent, 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, TM_Result *tmresult, TM_FailureData *tmfd)
 
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_Result *tmresult, 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 3618 of file trigger.c.

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0x30000000

Definition at line 3619 of file trigger.c.

◆ AFTER_TRIGGER_CP_UPDATE

#define AFTER_TRIGGER_CP_UPDATE   0x08000000

Definition at line 3620 of file trigger.c.

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x80000000

Definition at line 3613 of file trigger.c.

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x20000000

Definition at line 3617 of file trigger.c.

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3616 of file trigger.c.

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x40000000

Definition at line 3614 of file trigger.c.

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3612 of file trigger.c.

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0x38000000

Definition at line 3621 of file trigger.c.

◆ CHUNK_DATA_START

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

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

◆ for_each_chunk_from

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

Definition at line 3722 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:3700
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3672
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3634

Definition at line 3713 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 3718 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 3724 of file trigger.c.

◆ GetTriggerSharedData

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

Definition at line 3681 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:3621
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3618
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3619
#define AFTER_TRIGGER_CP_UPDATE
Definition: trigger.c:3620

Definition at line 3672 of file trigger.c.

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3634 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataNoOids

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3622 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3634 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3634 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3634 of file trigger.c.

◆ SetConstraintState

Definition at line 3565 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3544 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3610 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 3993 of file trigger.c.

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

4981 {
4982  /* Increase the query stack depth */
4984 }

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5263 of file trigger.c.

5264 {
5265  int my_level = GetCurrentTransactionNestLevel();
5266 
5267  /*
5268  * Allocate more space in the trans_stack if needed. (Note: because the
5269  * minimum nest level of a subtransaction is 2, we waste the first couple
5270  * entries of the array; not worth the notational effort to avoid it.)
5271  */
5272  while (my_level >= afterTriggers.maxtransdepth)
5273  {
5274  if (afterTriggers.maxtransdepth == 0)
5275  {
5276  /* Arbitrarily initialize for max of 8 subtransaction levels */
5279  8 * sizeof(AfterTriggersTransData));
5281  }
5282  else
5283  {
5284  /* repalloc will keep the stack in the same context */
5285  int new_alloc = afterTriggers.maxtransdepth * 2;
5286 
5289  new_alloc * sizeof(AfterTriggersTransData));
5290  afterTriggers.maxtransdepth = new_alloc;
5291  }
5292  }
5293 
5294  /*
5295  * Push the current information into the stack. The SET CONSTRAINTS state
5296  * is not saved until/unless changed. Likewise, we don't make a
5297  * per-subtransaction event context until needed.
5298  */
5299  afterTriggers.trans_stack[my_level].state = NULL;
5303 }
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1456
CommandId firing_counter
Definition: trigger.c:3811
AfterTriggersTransData * trans_stack
Definition: trigger.c:3822
AfterTriggerEventList events
Definition: trigger.c:3813
AfterTriggerEventList events
Definition: trigger.c:3837
SetConstraintState state
Definition: trigger.c:3836
CommandId firing_counter
Definition: trigger.c:3839
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:914

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

4949 {
4950  /*
4951  * Initialize after-trigger state structure to empty
4952  */
4953  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4955 
4956  /*
4957  * Verify that there is no leftover state remaining. If these assertions
4958  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4959  * up properly.
4960  */
4961  Assert(afterTriggers.state == NULL);
4962  Assert(afterTriggers.query_stack == NULL);
4964  Assert(afterTriggers.event_cxt == NULL);
4965  Assert(afterTriggers.events.head == NULL);
4966  Assert(afterTriggers.trans_stack == NULL);
4968 }
uint32 CommandId
Definition: c.h:650
SetConstraintState state
Definition: trigger.c:3812
AfterTriggersQueryData * query_stack
Definition: trigger.c:3817

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

3948 {
3949  Oid tgoid = evtshared->ats_tgoid;
3951  int i;
3952 
3953  /*
3954  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3955  * constraints declared NOT DEFERRABLE), the state is always false.
3956  */
3957  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3958  return false;
3959 
3960  /*
3961  * If constraint state exists, SET CONSTRAINTS might have been executed
3962  * either for this trigger or for all triggers.
3963  */
3964  if (state != NULL)
3965  {
3966  /* Check for SET CONSTRAINTS for this specific trigger. */
3967  for (i = 0; i < state->numstates; i++)
3968  {
3969  if (state->trigstates[i].sct_tgoid == tgoid)
3970  return state->trigstates[i].sct_tgisdeferred;
3971  }
3972 
3973  /* Check for SET CONSTRAINTS ALL. */
3974  if (state->all_isset)
3975  return state->all_isdeferred;
3976  }
3977 
3978  /*
3979  * Otherwise return the default state for the trigger.
3980  */
3981  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3982 }
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 4170 of file trigger.c.

4171 {
4172  AfterTriggerEventChunk *target = qs->events.head;
4173  ListCell *lc;
4174 
4175  Assert(target && target->next);
4176 
4177  /*
4178  * First, update any pointers in the per-table data, so that they won't be
4179  * dangling. Resetting obsoleted pointers to NULL will make
4180  * cancel_prior_stmt_triggers start from the list head, which is fine.
4181  */
4182  foreach(lc, qs->tables)
4183  {
4185 
4186  if (table->after_trig_done &&
4187  table->after_trig_events.tail == target)
4188  {
4189  table->after_trig_events.head = NULL;
4190  table->after_trig_events.tail = NULL;
4191  table->after_trig_events.tailfree = NULL;
4192  }
4193  }
4194 
4195  /* Now we can flush the head chunk */
4196  qs->events.head = target->next;
4197  pfree(target);
4198 }
void pfree(void *pointer)
Definition: mcxt.c:1436
#define lfirst(lc)
Definition: pg_list.h:172
AfterTriggerEventList events
Definition: trigger.c:3828
AfterTriggerEventList after_trig_events
Definition: trigger.c:3850

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

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

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

5312 {
5313  int my_level = GetCurrentTransactionNestLevel();
5315  AfterTriggerEvent event;
5316  AfterTriggerEventChunk *chunk;
5317  CommandId subxact_firing_id;
5318 
5319  /*
5320  * Pop the prior state if needed.
5321  */
5322  if (isCommit)
5323  {
5324  Assert(my_level < afterTriggers.maxtransdepth);
5325  /* If we saved a prior state, we don't need it anymore */
5326  state = afterTriggers.trans_stack[my_level].state;
5327  if (state != NULL)
5328  pfree(state);
5329  /* this avoids double pfree if error later: */
5330  afterTriggers.trans_stack[my_level].state = NULL;
5333  }
5334  else
5335  {
5336  /*
5337  * Aborting. It is possible subxact start failed before calling
5338  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5339  * trans_stack levels that aren't there.
5340  */
5341  if (my_level >= afterTriggers.maxtransdepth)
5342  return;
5343 
5344  /*
5345  * Release query-level storage for queries being aborted, and restore
5346  * query_depth to its pre-subxact value. This assumes that a
5347  * subtransaction will not add events to query levels started in a
5348  * earlier transaction state.
5349  */
5351  {
5355  }
5358 
5359  /*
5360  * Restore the global deferred-event list to its former length,
5361  * discarding any events queued by the subxact.
5362  */
5364  &afterTriggers.trans_stack[my_level].events);
5365 
5366  /*
5367  * Restore the trigger state. If the saved state is NULL, then this
5368  * subxact didn't save it, so it doesn't need restoring.
5369  */
5370  state = afterTriggers.trans_stack[my_level].state;
5371  if (state != NULL)
5372  {
5375  }
5376  /* this avoids double pfree if error later: */
5377  afterTriggers.trans_stack[my_level].state = NULL;
5378 
5379  /*
5380  * Scan for any remaining deferred events that were marked DONE or IN
5381  * PROGRESS by this subxact or a child, and un-mark them. We can
5382  * recognize such events because they have a firing ID greater than or
5383  * equal to the firing_counter value we saved at subtransaction start.
5384  * (This essentially assumes that the current subxact includes all
5385  * subxacts started after it.)
5386  */
5387  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5389  {
5390  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5391 
5392  if (event->ate_flags &
5394  {
5395  if (evtshared->ats_firing_id >= subxact_firing_id)
5396  event->ate_flags &=
5398  }
5399  }
5400  }
5401 }
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3614
#define GetTriggerSharedData(evt)
Definition: trigger.c:3681
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:4130
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3613
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3718

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

5216 {
5217  /*
5218  * Forget the pending-events list.
5219  *
5220  * Since all the info is in TopTransactionContext or children thereof, we
5221  * don't really need to do anything to reclaim memory. However, the
5222  * pending-events list could be large, and so it's useful to discard it as
5223  * soon as possible --- especially if we are aborting because we ran out
5224  * of memory for the list!
5225  */
5227  {
5229  afterTriggers.event_cxt = NULL;
5230  afterTriggers.events.head = NULL;
5231  afterTriggers.events.tail = NULL;
5232  afterTriggers.events.tailfree = NULL;
5233  }
5234 
5235  /*
5236  * Forget any subtransaction state as well. Since this can't be very
5237  * large, we let the eventual reset of TopTransactionContext free the
5238  * memory instead of doing it here.
5239  */
5240  afterTriggers.trans_stack = NULL;
5242 
5243 
5244  /*
5245  * Forget the query stack and constraint-related state information. As
5246  * with the subtransaction state information, we don't bother freeing the
5247  * memory here.
5248  */
5249  afterTriggers.query_stack = NULL;
5251  afterTriggers.state = NULL;
5252 
5253  /* No more afterTriggers manipulation until next transaction starts. */
5255 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:387

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

5500 {
5501  int init_depth = afterTriggers.maxquerydepth;
5502 
5504 
5505  if (afterTriggers.maxquerydepth == 0)
5506  {
5507  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5508 
5511  new_alloc * sizeof(AfterTriggersQueryData));
5512  afterTriggers.maxquerydepth = new_alloc;
5513  }
5514  else
5515  {
5516  /* repalloc will keep the stack in the same context */
5517  int old_alloc = afterTriggers.maxquerydepth;
5518  int new_alloc = Max(afterTriggers.query_depth + 1,
5519  old_alloc * 2);
5520 
5523  new_alloc * sizeof(AfterTriggersQueryData));
5524  afterTriggers.maxquerydepth = new_alloc;
5525  }
5526 
5527  /* Initialize new array entries to empty */
5528  while (init_depth < afterTriggers.maxquerydepth)
5529  {
5531 
5532  qs->events.head = NULL;
5533  qs->events.tail = NULL;
5534  qs->events.tailfree = NULL;
5535  qs->fdw_tuplestore = NULL;
5536  qs->tables = NIL;
5537 
5538  ++init_depth;
5539  }
5540 }
#define Max(x, y)
Definition: c.h:982
#define NIL
Definition: pg_list.h:68
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3829

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

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

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

5160 {
5161  AfterTriggerEventList *events;
5162  bool snap_pushed = false;
5163 
5164  /* Must not be inside a query */
5166 
5167  /*
5168  * If there are any triggers to fire, make sure we have set a snapshot for
5169  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5170  * can't assume ActiveSnapshot is valid on entry.)
5171  */
5172  events = &afterTriggers.events;
5173  if (events->head != NULL)
5174  {
5176  snap_pushed = true;
5177  }
5178 
5179  /*
5180  * Run all the remaining triggers. Loop until they are all gone, in case
5181  * some trigger queues more for us to do.
5182  */
5183  while (afterTriggerMarkEvents(events, NULL, false))
5184  {
5185  CommandId firing_id = afterTriggers.firing_counter++;
5186 
5187  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5188  break; /* all fired */
5189  }
5190 
5191  /*
5192  * We don't bother freeing the event list, since it will go away anyway
5193  * (and more efficiently than via pfree) in AfterTriggerEndXact.
5194  */
5195 
5196  if (snap_pushed)
5198 }
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 4109 of file trigger.c.

4110 {
4111  AfterTriggerEventChunk *chunk;
4112 
4113  while ((chunk = events->head) != NULL)
4114  {
4115  events->head = chunk->next;
4116  pfree(chunk);
4117  }
4118  events->tail = NULL;
4119  events->tailfree = NULL;
4120 }

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

5092 {
5093  Tuplestorestate *ts;
5094  List *tables;
5095  ListCell *lc;
5096 
5097  /* Drop the trigger events */
5099 
5100  /* Drop FDW tuplestore if any */
5101  ts = qs->fdw_tuplestore;
5102  qs->fdw_tuplestore = NULL;
5103  if (ts)
5104  tuplestore_end(ts);
5105 
5106  /* Release per-table subsidiary storage */
5107  tables = qs->tables;
5108  foreach(lc, tables)
5109  {
5111 
5112  ts = table->old_upd_tuplestore;
5113  table->old_upd_tuplestore = NULL;
5114  if (ts)
5115  tuplestore_end(ts);
5116  ts = table->new_upd_tuplestore;
5117  table->new_upd_tuplestore = NULL;
5118  if (ts)
5119  tuplestore_end(ts);
5120  ts = table->old_del_tuplestore;
5121  table->old_del_tuplestore = NULL;
5122  if (ts)
5123  tuplestore_end(ts);
5124  ts = table->new_ins_tuplestore;
5125  table->new_ins_tuplestore = NULL;
5126  if (ts)
5127  tuplestore_end(ts);
5128  if (table->storeslot)
5129  {
5130  TupleTableSlot *slot = table->storeslot;
5131 
5132  table->storeslot = NULL;
5134  }
5135  }
5136 
5137  /*
5138  * Now free the AfterTriggersTableData structs and list cells. Reset list
5139  * pointer first; if list_free_deep somehow gets an error, better to leak
5140  * that storage than have an infinite loop.
5141  */
5142  qs->tables = NIL;
5143  list_free_deep(tables);
5144 }
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1255
void list_free_deep(List *list)
Definition: list.c:1559
TupleTableSlot * storeslot
Definition: trigger.c:3868
Definition: pg_list.h:54
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4109
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 4587 of file trigger.c.

4591 {
4592  bool all_fired = true;
4593  AfterTriggerEventChunk *chunk;
4594  MemoryContext per_tuple_context;
4595  bool local_estate = false;
4596  ResultRelInfo *rInfo = NULL;
4597  Relation rel = NULL;
4598  TriggerDesc *trigdesc = NULL;
4599  FmgrInfo *finfo = NULL;
4600  Instrumentation *instr = NULL;
4601  TupleTableSlot *slot1 = NULL,
4602  *slot2 = NULL;
4603 
4604  /* Make a local EState if need be */
4605  if (estate == NULL)
4606  {
4607  estate = CreateExecutorState();
4608  local_estate = true;
4609  }
4610 
4611  /* Make a per-tuple memory context for trigger function calls */
4612  per_tuple_context =
4614  "AfterTriggerTupleContext",
4616 
4617  for_each_chunk(chunk, *events)
4618  {
4619  AfterTriggerEvent event;
4620  bool all_fired_in_chunk = true;
4621 
4622  for_each_event(event, chunk)
4623  {
4624  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4625 
4626  /*
4627  * Is it one for me to fire?
4628  */
4629  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4630  evtshared->ats_firing_id == firing_id)
4631  {
4632  ResultRelInfo *src_rInfo,
4633  *dst_rInfo;
4634 
4635  /*
4636  * So let's fire it... but first, find the correct relation if
4637  * this is not the same relation as before.
4638  */
4639  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4640  {
4641  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid,
4642  NULL);
4643  rel = rInfo->ri_RelationDesc;
4644  /* Catch calls with insufficient relcache refcounting */
4646  trigdesc = rInfo->ri_TrigDesc;
4647  finfo = rInfo->ri_TrigFunctions;
4648  instr = rInfo->ri_TrigInstrument;
4649  if (slot1 != NULL)
4650  {
4653  slot1 = slot2 = NULL;
4654  }
4655  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4656  {
4657  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4659  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4661  }
4662  if (trigdesc == NULL) /* should not happen */
4663  elog(ERROR, "relation %u has no triggers",
4664  evtshared->ats_relid);
4665  }
4666 
4667  /*
4668  * Look up source and destination partition result rels of a
4669  * cross-partition update event.
4670  */
4671  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4673  {
4674  Assert(OidIsValid(event->ate_src_part) &&
4675  OidIsValid(event->ate_dst_part));
4676  src_rInfo = ExecGetTriggerResultRel(estate,
4677  event->ate_src_part,
4678  rInfo);
4679  dst_rInfo = ExecGetTriggerResultRel(estate,
4680  event->ate_dst_part,
4681  rInfo);
4682  }
4683  else
4684  src_rInfo = dst_rInfo = rInfo;
4685 
4686  /*
4687  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4688  * still set, so recursive examinations of the event list
4689  * won't try to re-fire it.
4690  */
4691  AfterTriggerExecute(estate, event, rInfo,
4692  src_rInfo, dst_rInfo,
4693  trigdesc, finfo, instr,
4694  per_tuple_context, slot1, slot2);
4695 
4696  /*
4697  * Mark the event as done.
4698  */
4699  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4700  event->ate_flags |= AFTER_TRIGGER_DONE;
4701  }
4702  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4703  {
4704  /* something remains to be done */
4705  all_fired = all_fired_in_chunk = false;
4706  }
4707  }
4708 
4709  /* Clear the chunk if delete_ok and nothing left of interest */
4710  if (delete_ok && all_fired_in_chunk)
4711  {
4712  chunk->freeptr = CHUNK_DATA_START(chunk);
4713  chunk->endfree = chunk->endptr;
4714 
4715  /*
4716  * If it's last chunk, must sync event list's tailfree too. Note
4717  * that delete_ok must NOT be passed as true if there could be
4718  * additional AfterTriggerEventList values pointing at this event
4719  * list, since we'd fail to fix their copies of tailfree.
4720  */
4721  if (chunk == events->tail)
4722  events->tailfree = chunk->freeptr;
4723  }
4724  }
4725  if (slot1 != NULL)
4726  {
4729  }
4730 
4731  /* Release working resources */
4732  MemoryContextDelete(per_tuple_context);
4733 
4734  if (local_estate)
4735  {
4736  ExecCloseResultRelations(estate);
4737  ExecResetTupleTable(estate->es_tupleTable, false);
4738  FreeExecutorState(estate);
4739  }
4740 
4741  return all_fired;
4742 }
#define OidIsValid(objectId)
Definition: c.h:759
void ExecCloseResultRelations(EState *estate)
Definition: execMain.c:1504
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid, ResultRelInfo *rootRelInfo)
Definition: execMain.c:1283
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1192
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1239
EState * CreateExecutorState(void)
Definition: execUtils.c:93
void FreeExecutorState(EState *estate)
Definition: execUtils.c:194
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
#define RelationHasReferenceCountZero(relation)
Definition: rel.h:487
#define RelationGetRelid(relation)
Definition: rel.h:503
List * es_tupleTable
Definition: execnodes.h:662
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:489
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:480
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:483
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:4232
#define for_each_event(eptr, cptr)
Definition: trigger.c:3713
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3711

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

4506 {
4507  bool found = false;
4508  bool deferred_found = false;
4509  AfterTriggerEvent event;
4510  AfterTriggerEventChunk *chunk;
4511 
4512  for_each_event_chunk(event, chunk, *events)
4513  {
4514  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4515  bool defer_it = false;
4516 
4517  if (!(event->ate_flags &
4519  {
4520  /*
4521  * This trigger hasn't been called or scheduled yet. Check if we
4522  * should call it now.
4523  */
4524  if (immediate_only && afterTriggerCheckState(evtshared))
4525  {
4526  defer_it = true;
4527  }
4528  else
4529  {
4530  /*
4531  * Mark it as to be fired in this firing cycle.
4532  */
4534  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4535  found = true;
4536  }
4537  }
4538 
4539  /*
4540  * If it's deferred, move it to move_list, if requested.
4541  */
4542  if (defer_it && move_list != NULL)
4543  {
4544  deferred_found = true;
4545  /* add it to move_list */
4546  afterTriggerAddEvent(move_list, event, evtshared);
4547  /* mark original copy "done" so we don't do it again */
4548  event->ate_flags |= AFTER_TRIGGER_DONE;
4549  }
4550  }
4551 
4552  /*
4553  * We could allow deferred triggers if, before the end of the
4554  * security-restricted operation, we were to verify that a SET CONSTRAINTS
4555  * ... IMMEDIATE has fired all such triggers. For now, don't bother.
4556  */
4557  if (deferred_found && InSecurityRestrictedOperation())
4558  ereport(ERROR,
4559  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4560  errmsg("cannot fire deferred trigger within security-restricted operation")));
4561 
4562  return found;
4563 }
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ereport(elevel,...)
Definition: elog.h:149
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:658
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3993
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3947

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

5937 {
5938  AfterTriggerEvent event;
5939  AfterTriggerEventChunk *chunk;
5940  int depth;
5941 
5942  /* Scan queued events */
5944  {
5945  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5946 
5947  /*
5948  * We can ignore completed events. (Even if a DONE flag is rolled
5949  * back by subxact abort, it's OK because the effects of the TRUNCATE
5950  * or whatever must get rolled back too.)
5951  */
5952  if (event->ate_flags & AFTER_TRIGGER_DONE)
5953  continue;
5954 
5955  if (evtshared->ats_relid == relid)
5956  return true;
5957  }
5958 
5959  /*
5960  * Also scan events queued by incomplete queries. This could only matter
5961  * if TRUNCATE/etc is executed by a function or trigger within an updating
5962  * query on the same relation, which is pretty perverse, but let's check.
5963  */
5964  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5965  {
5967  {
5968  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5969 
5970  if (event->ate_flags & AFTER_TRIGGER_DONE)
5971  continue;
5972 
5973  if (evtshared->ats_relid == relid)
5974  return true;
5975  }
5976  }
5977 
5978  return false;
5979 }

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

4132 {
4133  AfterTriggerEventChunk *chunk;
4134  AfterTriggerEventChunk *next_chunk;
4135 
4136  if (old_events->tail == NULL)
4137  {
4138  /* restoring to a completely empty state, so free everything */
4139  afterTriggerFreeEventList(events);
4140  }
4141  else
4142  {
4143  *events = *old_events;
4144  /* free any chunks after the last one we want to keep */
4145  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4146  {
4147  next_chunk = chunk->next;
4148  pfree(chunk);
4149  }
4150  /* and clean up the tail chunk to be the right length */
4151  events->tail->next = NULL;
4152  events->tail->freeptr = events->tailfree;
4153 
4154  /*
4155  * We don't make any effort to remove now-unused shared data records.
4156  * They might still be useful, anyway.
4157  */
4158  }
4159 }

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

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

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

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, BTEqualStrategyNumber, RangeVar::catalogname, CStringGetDatum(), 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, stmt, 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 6541 of file trigger.c.

6542 {
6543  /*
6544  * Must flush the plan cache when changing replication role; but don't
6545  * flush unnecessarily.
6546  */
6548  ResetPlanCache();
6549 }
#define newval
void ResetPlanCache(void)
Definition: plancache.c:2150
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 6420 of file trigger.c.

6421 {
6422  bool result;
6423  AfterTriggersTableData *table;
6424 
6425  /* Check state, like AfterTriggerSaveEvent. */
6426  if (afterTriggers.query_depth < 0)
6427  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6428 
6429  /* Be sure we have enough space to record events at this query depth. */
6432 
6433  /*
6434  * We keep this state in the AfterTriggersTableData that also holds
6435  * transition tables for the relation + operation. In this way, if we are
6436  * forced to make a new set of transition tables because more tuples get
6437  * entered after we've already fired triggers, we will allow a new set of
6438  * statement triggers to get queued.
6439  */
6440  table = GetAfterTriggersTableData(relid, cmdType);
6441  result = table->before_trig_done;
6442  table->before_trig_done = true;
6443  return result;
6444 }
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4758

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

6467 {
6468  AfterTriggersTableData *table;
6470 
6471  /*
6472  * We keep this state in the AfterTriggersTableData that also holds
6473  * transition tables for the relation + operation. In this way, if we are
6474  * forced to make a new set of transition tables because more tuples get
6475  * entered after we've already fired triggers, we will allow a new set of
6476  * statement triggers to get queued without canceling the old ones.
6477  */
6478  table = GetAfterTriggersTableData(relid, cmdType);
6479 
6480  if (table->after_trig_done)
6481  {
6482  /*
6483  * We want to start scanning from the tail location that existed just
6484  * before we inserted any statement triggers. But the events list
6485  * might've been entirely empty then, in which case scan from the
6486  * current head.
6487  */
6488  AfterTriggerEvent event;
6489  AfterTriggerEventChunk *chunk;
6490 
6491  if (table->after_trig_events.tail)
6492  {
6493  chunk = table->after_trig_events.tail;
6494  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6495  }
6496  else
6497  {
6498  chunk = qs->events.head;
6499  event = NULL;
6500  }
6501 
6502  for_each_chunk_from(chunk)
6503  {
6504  if (event == NULL)
6505  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6506  for_each_event_from(event, chunk)
6507  {
6508  AfterTriggerShared evtshared = GetTriggerSharedData(event);
6509 
6510  /*
6511  * Exit loop when we reach events that aren't AS triggers for
6512  * the target relation.
6513  */
6514  if (evtshared->ats_relid != relid)
6515  goto done;
6516  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6517  goto done;
6518  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6519  goto done;
6520  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6521  goto done;
6522  /* OK, mark it DONE */
6523  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6524  event->ate_flags |= AFTER_TRIGGER_DONE;
6525  }
6526  /* signal we must reinitialize event ptr for next chunk */
6527  event = NULL;
6528  }
6529  }
6530 done:
6531 
6532  /* In any case, save current insertion point for next time */
6533  table->after_trig_done = true;
6534  table->after_trig_events = qs->events;
6535 }
#define for_each_chunk_from(cptr)
Definition: trigger.c:3722
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3724
#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 2096 of file trigger.c.

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

169 {
170  return
171  CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
172  constraintOid, indexOid, funcoid,
173  parentTriggerOid, whenClause, isInternal,
174  in_partition, TRIGGER_FIRES_ON_ORIGIN);
175 }
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:182
#define TRIGGER_FIRES_ON_ORIGIN
Definition: trigger.h:149

References CreateTriggerFiringOn(), stmt, 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 182 of file trigger.c.

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

References AccessShareLock, ACL_EXECUTE, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addNSItemToQuery(), addRangeTableEntryForRelation(), ALLOCSET_SMALL_SIZES, AllocSetContextCreate, allowSystemTableMods, generate_unaccent_rules::args, Assert(), assign_expr_collations(), attnameAttNum(), attnum, BoolGetDatum(), BTEqualStrategyNumber, buildint2vector(), byteain(), CacheInvalidateRelcacheByTuple(), CatalogTupleInsert(), CatalogTupleUpdate(), ObjectAddress::classId, CommandCounterIncrement(), copyObject, CreateConstraintEntry(), CStringGetDatum(), CStringGetTextDatum, CurrentMemoryContext, DatumGetPointer(), 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, 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, Int16GetDatum(), InvalidAttrNumber, InvalidOid, InvokeObjectPostCreateHookArg, 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(), RelationGetDescr, RelationGetNamespace, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RELOID, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, ShareRowExclusiveLock, snprintf, stmt, strVal, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), table_openrv(), transformWhereClause(), TupleDescAttr, values, Var::varattno, Var::varno, and CreateTrigStmt::whenClause.

Referenced by CloneRowTriggersToPartition(), and CreateTrigger().

◆ EnableDisableTrigger()

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

Definition at line 1732 of file trigger.c.

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

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

2791 {
2792  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2793 
2794  if ((trigdesc && trigdesc->trig_delete_after_row) ||
2795  (transition_capture && transition_capture->tcs_delete_old_table))
2796  {
2797  TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
2798 
2799  Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
2800  if (fdw_trigtuple == NULL)
2801  GetTupleForTrigger(estate,
2802  NULL,
2803  relinfo,
2804  tupleid,
2806  slot,
2807  NULL,
2808  NULL,
2809  NULL);
2810  else
2811  ExecForceStoreHeapTuple(fdw_trigtuple, slot, false);
2812 
2813  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2815  true, slot, NULL, NIL, NULL,
2816  transition_capture,
2817  is_crosspart_update);
2818  }
2819 }
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1470
@ LockTupleExclusive
Definition: lockoptions.h:58
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:6023
static bool GetTupleForTrigger(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **epqslot, TM_Result *tmresultp, TM_FailureData *tmfdp)
Definition: trigger.c:3279

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

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

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

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

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

2676 {
2677  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2678 
2679  if (trigdesc && trigdesc->trig_delete_after_statement)
2680  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2682  false, NULL, NULL, NIL, NULL, transition_capture,
2683  false);
2684 }
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 2456 of file trigger.c.

2458 {
2459  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2460 
2461  if (trigdesc && trigdesc->trig_insert_after_statement)
2462  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2464  false, NULL, NULL, NIL, NULL, transition_capture,
2465  false);
2466 }
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 3262 of file trigger.c.

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

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

Referenced by ExecuteTruncateGuts().

◆ ExecASUpdateTriggers()

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

Definition at line 2927 of file trigger.c.

2929 {
2930  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2931 
2932  /* statement-level triggers operate on the parent table */
2933  Assert(relinfo->ri_RootResultRelInfo == NULL);
2934 
2935  if (trigdesc && trigdesc->trig_update_after_statement)
2936  AfterTriggerSaveEvent(estate, relinfo, NULL, NULL,
2938  false, NULL, NULL, NIL,
2939  ExecGetAllUpdatedCols(relinfo, estate),
2940  transition_capture,
2941  false);
2942 }
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:574
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,
TM_Result tmresult,
TM_FailureData tmfd 
)

Definition at line 2694 of file trigger.c.

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

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

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_Result tmresult,
TM_FailureData tmfd 
)

Definition at line 2945 of file trigger.c.

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