PostgreSQL Source Code  git master
trigger.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/sysattr.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_inherits_fn.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 "miscadmin.h"
#include "nodes/bitmapset.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/var.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 "pgstat.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
#include "utils/tuplestore.h"
Include dependency graph for trigger.c:

Go to the source code of this file.

Data Structures

struct  OldTriggerInfo
 
struct  SetConstraintTriggerData
 
struct  SetConstraintStateData
 
struct  AfterTriggerSharedData
 
struct  AfterTriggerEventData
 
struct  AfterTriggerEventDataOneCtid
 
struct  AfterTriggerEventDataZeroCtids
 
struct  AfterTriggerEventChunk
 
struct  AfterTriggerEventList
 
struct  AfterTriggersData
 
struct  AfterTriggersQueryData
 
struct  AfterTriggersTransData
 
struct  AfterTriggersTableData
 

Macros

#define GetUpdatedColumns(relinfo, estate)   (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
 
#define AFTER_TRIGGER_OFFSET   0x0FFFFFFF /* must be low-order bits */
 
#define AFTER_TRIGGER_DONE   0x10000000
 
#define AFTER_TRIGGER_IN_PROGRESS   0x20000000
 
#define AFTER_TRIGGER_FDW_REUSE   0x00000000
 
#define AFTER_TRIGGER_FDW_FETCH   0x80000000
 
#define AFTER_TRIGGER_1CTID   0x40000000
 
#define AFTER_TRIGGER_2CTID   0xC0000000
 
#define AFTER_TRIGGER_TUP_BITS   0xC0000000
 
#define SizeofTriggerEvent(evt)
 
#define GetTriggerSharedData(evt)   ((AfterTriggerShared) ((char *) (evt) + ((evt)->ate_flags & AFTER_TRIGGER_OFFSET)))
 
#define CHUNK_DATA_START(cptr)   ((char *) (cptr) + MAXALIGN(sizeof(AfterTriggerEventChunk)))
 
#define for_each_chunk(cptr, evtlist)   for (cptr = (evtlist).head; cptr != NULL; cptr = cptr->next)
 
#define for_each_event(eptr, cptr)
 
#define for_each_event_chunk(eptr, cptr, evtlist)   for_each_chunk(cptr, evtlist) for_each_event(eptr, cptr)
 
#define for_each_chunk_from(cptr)   for (; cptr != NULL; cptr = cptr->next)
 
#define for_each_event_from(eptr, cptr)
 
#define MIN_CHUNK_SIZE   1024
 
#define MAX_CHUNK_SIZE   (1024*1024)
 

Typedefs

typedef struct SetConstraintTriggerData SetConstraintTriggerData
 
typedef struct SetConstraintTriggerDataSetConstraintTrigger
 
typedef struct SetConstraintStateData SetConstraintStateData
 
typedef SetConstraintStateDataSetConstraintState
 
typedef uint32 TriggerFlags
 
typedef struct AfterTriggerSharedDataAfterTriggerShared
 
typedef struct AfterTriggerSharedData AfterTriggerSharedData
 
typedef struct AfterTriggerEventDataAfterTriggerEvent
 
typedef struct AfterTriggerEventData AfterTriggerEventData
 
typedef struct AfterTriggerEventDataOneCtid AfterTriggerEventDataOneCtid
 
typedef struct AfterTriggerEventDataZeroCtids AfterTriggerEventDataZeroCtids
 
typedef struct AfterTriggerEventChunk AfterTriggerEventChunk
 
typedef struct AfterTriggerEventList AfterTriggerEventList
 
typedef struct AfterTriggersQueryData AfterTriggersQueryData
 
typedef struct AfterTriggersTransData AfterTriggersTransData
 
typedef struct AfterTriggersTableData AfterTriggersTableData
 
typedef struct AfterTriggersData AfterTriggersData
 

Functions

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

Variables

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

Macro Definition Documentation

◆ AFTER_TRIGGER_1CTID

#define AFTER_TRIGGER_1CTID   0x40000000

Definition at line 3384 of file trigger.c.

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

Definition at line 3385 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x10000000

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x80000000

Definition at line 3383 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3382 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x20000000

◆ AFTER_TRIGGER_OFFSET

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

Definition at line 3378 of file trigger.c.

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3386 of file trigger.c.

Referenced by AfterTriggerExecute().

◆ CHUNK_DATA_START

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

◆ for_each_chunk

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

Definition at line 3458 of file trigger.c.

Referenced by afterTriggerInvokeEvents().

◆ for_each_chunk_from

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

Definition at line 3469 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

◆ for_each_event

#define for_each_event (   eptr,
  cptr 
)
Value:
for (eptr = (AfterTriggerEvent) CHUNK_DATA_START(cptr); \
(char *) eptr < (cptr)->freeptr; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3447
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3421
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3399

Definition at line 3460 of file trigger.c.

Referenced by afterTriggerInvokeEvents().

◆ for_each_event_chunk

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

◆ for_each_event_from

#define for_each_event_from (   eptr,
  cptr 
)
Value:
for (; \
(char *) eptr < (cptr)->freeptr; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3421
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3399

Definition at line 3471 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

◆ GetTriggerSharedData

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

◆ GetUpdatedColumns

#define GetUpdatedColumns (   relinfo,
  estate 
)    (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)

◆ MAX_CHUNK_SIZE

#define MAX_CHUNK_SIZE   (1024*1024)

Referenced by afterTriggerAddEvent().

◆ MIN_CHUNK_SIZE

#define MIN_CHUNK_SIZE   1024

Referenced by afterTriggerAddEvent().

◆ SizeofTriggerEvent

#define SizeofTriggerEvent (   evt)
Value:
(((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_2CTID ? \
((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_1CTID ? \
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3386
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3384
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3385

Definition at line 3421 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3399 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3388 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

◆ AfterTriggersTableData

◆ AfterTriggersTransData

◆ SetConstraintState

◆ SetConstraintStateData

◆ SetConstraintTrigger

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3376 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

Definition at line 3708 of file trigger.c.

References AFTER_TRIGGER_OFFSET, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate(), Assert, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_relid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, CHUNK_DATA_START, AfterTriggersData::event_cxt, AfterTriggerEventList::head, MAX_CHUNK_SIZE, MemoryContextAlloc(), Min, MIN_CHUNK_SIZE, AfterTriggerEventChunk::next, SizeofTriggerEvent, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TopTransactionContext.

Referenced by afterTriggerMarkEvents(), and AfterTriggerSaveEvent().

3710 {
3711  Size eventsize = SizeofTriggerEvent(event);
3712  Size needed = eventsize + sizeof(AfterTriggerSharedData);
3713  AfterTriggerEventChunk *chunk;
3714  AfterTriggerShared newshared;
3715  AfterTriggerEvent newevent;
3716 
3717  /*
3718  * If empty list or not enough room in the tail chunk, make a new chunk.
3719  * We assume here that a new shared record will always be needed.
3720  */
3721  chunk = events->tail;
3722  if (chunk == NULL ||
3723  chunk->endfree - chunk->freeptr < needed)
3724  {
3725  Size chunksize;
3726 
3727  /* Create event context if we didn't already */
3728  if (afterTriggers.event_cxt == NULL)
3731  "AfterTriggerEvents",
3733 
3734  /*
3735  * Chunk size starts at 1KB and is allowed to increase up to 1MB.
3736  * These numbers are fairly arbitrary, though there is a hard limit at
3737  * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
3738  * shared records using the available space in ate_flags. Another
3739  * constraint is that if the chunk size gets too huge, the search loop
3740  * below would get slow given a (not too common) usage pattern with
3741  * many distinct event types in a chunk. Therefore, we double the
3742  * preceding chunk size only if there weren't too many shared records
3743  * in the preceding chunk; otherwise we halve it. This gives us some
3744  * ability to adapt to the actual usage pattern of the current query
3745  * while still having large chunk sizes in typical usage. All chunk
3746  * sizes used should be MAXALIGN multiples, to ensure that the shared
3747  * records will be aligned safely.
3748  */
3749 #define MIN_CHUNK_SIZE 1024
3750 #define MAX_CHUNK_SIZE (1024*1024)
3751 
3752 #if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
3753 #error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
3754 #endif
3755 
3756  if (chunk == NULL)
3757  chunksize = MIN_CHUNK_SIZE;
3758  else
3759  {
3760  /* preceding chunk size... */
3761  chunksize = chunk->endptr - (char *) chunk;
3762  /* check number of shared records in preceding chunk */
3763  if ((chunk->endptr - chunk->endfree) <=
3764  (100 * sizeof(AfterTriggerSharedData)))
3765  chunksize *= 2; /* okay, double it */
3766  else
3767  chunksize /= 2; /* too many shared records */
3768  chunksize = Min(chunksize, MAX_CHUNK_SIZE);
3769  }
3770  chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize);
3771  chunk->next = NULL;
3772  chunk->freeptr = CHUNK_DATA_START(chunk);
3773  chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
3774  Assert(chunk->endfree - chunk->freeptr >= needed);
3775 
3776  if (events->head == NULL)
3777  events->head = chunk;
3778  else
3779  events->tail->next = chunk;
3780  events->tail = chunk;
3781  /* events->tailfree is now out of sync, but we'll fix it below */
3782  }
3783 
3784  /*
3785  * Try to locate a matching shared-data record already in the chunk. If
3786  * none, make a new one.
3787  */
3788  for (newshared = ((AfterTriggerShared) chunk->endptr) - 1;
3789  (char *) newshared >= chunk->endfree;
3790  newshared--)
3791  {
3792  if (newshared->ats_tgoid == evtshared->ats_tgoid &&
3793  newshared->ats_relid == evtshared->ats_relid &&
3794  newshared->ats_event == evtshared->ats_event &&
3795  newshared->ats_table == evtshared->ats_table &&
3796  newshared->ats_firing_id == 0)
3797  break;
3798  }
3799  if ((char *) newshared < chunk->endfree)
3800  {
3801  *newshared = *evtshared;
3802  newshared->ats_firing_id = 0; /* just to be sure */
3803  chunk->endfree = (char *) newshared;
3804  }
3805 
3806  /* Insert the data */
3807  newevent = (AfterTriggerEvent) chunk->freeptr;
3808  memcpy(newevent, event, eventsize);
3809  /* ... and link the new event to its shared record */
3810  newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
3811  newevent->ate_flags |= (char *) newshared - (char *) newevent;
3812 
3813  chunk->freeptr += eventsize;
3814  events->tailfree = chunk->freeptr;
3815 }
TriggerEvent ats_event
Definition: trigger.c:3392
MemoryContext TopTransactionContext
Definition: mcxt.c:48
#define MIN_CHUNK_SIZE
TriggerFlags ate_flags
Definition: trigger.c:3403
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3447
struct AfterTriggerSharedData AfterTriggerSharedData
#define Min(x, y)
Definition: c.h:802
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
struct AfterTriggerEventChunk * next
Definition: trigger.c:3440
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:170
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3421
#define AFTER_TRIGGER_OFFSET
Definition: trigger.c:3378
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:342
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3396
#define Assert(condition)
Definition: c.h:670
size_t Size
Definition: c.h:404
CommandId ats_firing_id
Definition: trigger.c:3395
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3399
#define MAX_CHUNK_SIZE
AfterTriggerEventChunk * head
Definition: trigger.c:3452
MemoryContext event_cxt
Definition: trigger.c:3561
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:706
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4526 of file trigger.c.

References AfterTriggersData::query_depth.

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

4527 {
4528  /* Increase the query stack depth */
4530 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 4794 of file trigger.c.

References AfterTriggersData::events, AfterTriggersTransData::events, AfterTriggersData::firing_counter, AfterTriggersTransData::firing_counter, GetCurrentTransactionNestLevel(), AfterTriggersData::maxtransdepth, MemoryContextAlloc(), AfterTriggersData::query_depth, AfterTriggersTransData::query_depth, repalloc(), AfterTriggersTransData::state, TopTransactionContext, and AfterTriggersData::trans_stack.

Referenced by StartSubTransaction().

4795 {
4796  int my_level = GetCurrentTransactionNestLevel();
4797 
4798  /*
4799  * Allocate more space in the trans_stack if needed. (Note: because the
4800  * minimum nest level of a subtransaction is 2, we waste the first couple
4801  * entries of the array; not worth the notational effort to avoid it.)
4802  */
4803  while (my_level >= afterTriggers.maxtransdepth)
4804  {
4805  if (afterTriggers.maxtransdepth == 0)
4806  {
4807  /* Arbitrarily initialize for max of 8 subtransaction levels */
4810  8 * sizeof(AfterTriggersTransData));
4812  }
4813  else
4814  {
4815  /* repalloc will keep the stack in the same context */
4816  int new_alloc = afterTriggers.maxtransdepth * 2;
4817 
4820  new_alloc * sizeof(AfterTriggersTransData));
4821  afterTriggers.maxtransdepth = new_alloc;
4822  }
4823  }
4824 
4825  /*
4826  * Push the current information into the stack. The SET CONSTRAINTS state
4827  * is not saved until/unless changed. Likewise, we don't make a
4828  * per-subtransaction event context until needed.
4829  */
4830  afterTriggers.trans_stack[my_level].state = NULL;
4834 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
MemoryContext TopTransactionContext
Definition: mcxt.c:48
AfterTriggerEventList events
Definition: trigger.c:3584
CommandId firing_counter
Definition: trigger.c:3586
CommandId firing_counter
Definition: trigger.c:3558
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:754
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:962
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:706
AfterTriggerEventList events
Definition: trigger.c:3560
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
SetConstraintState state
Definition: trigger.c:3583

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

Definition at line 4494 of file trigger.c.

References Assert, AfterTriggersData::event_cxt, AfterTriggersData::events, AfterTriggersData::firing_counter, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, and AfterTriggersData::trans_stack.

Referenced by StartTransaction().

4495 {
4496  /*
4497  * Initialize after-trigger state structure to empty
4498  */
4499  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4501 
4502  /*
4503  * Verify that there is no leftover state remaining. If these assertions
4504  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4505  * up properly.
4506  */
4507  Assert(afterTriggers.state == NULL);
4508  Assert(afterTriggers.query_stack == NULL);
4510  Assert(afterTriggers.event_cxt == NULL);
4511  Assert(afterTriggers.events.head == NULL);
4512  Assert(afterTriggers.trans_stack == NULL);
4514 }
uint32 CommandId
Definition: c.h:459
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
SetConstraintState state
Definition: trigger.c:3559
CommandId firing_counter
Definition: trigger.c:3558
#define Assert(condition)
Definition: c.h:670
AfterTriggerEventChunk * head
Definition: trigger.c:3452
MemoryContext event_cxt
Definition: trigger.c:3561
AfterTriggerEventList events
Definition: trigger.c:3560
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

Definition at line 3662 of file trigger.c.

References AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_INITDEFERRED, SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_tgoid, i, SetConstraintStateData::numstates, SetConstraintTriggerData::sct_tgisdeferred, SetConstraintTriggerData::sct_tgoid, AfterTriggersData::state, and SetConstraintStateData::trigstates.

Referenced by afterTriggerMarkEvents().

3663 {
3664  Oid tgoid = evtshared->ats_tgoid;
3666  int i;
3667 
3668  /*
3669  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3670  * constraints declared NOT DEFERRABLE), the state is always false.
3671  */
3672  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3673  return false;
3674 
3675  /*
3676  * If constraint state exists, SET CONSTRAINTS might have been executed
3677  * either for this trigger or for all triggers.
3678  */
3679  if (state != NULL)
3680  {
3681  /* Check for SET CONSTRAINTS for this specific trigger. */
3682  for (i = 0; i < state->numstates; i++)
3683  {
3684  if (state->trigstates[i].sct_tgoid == tgoid)
3685  return state->trigstates[i].sct_tgisdeferred;
3686  }
3687 
3688  /* Check for SET CONSTRAINTS ALL. */
3689  if (state->all_isset)
3690  return state->all_isdeferred;
3691  }
3692 
3693  /*
3694  * Otherwise return the default state for the trigger.
3695  */
3696  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3697 }
TriggerEvent ats_event
Definition: trigger.c:3392
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:114
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:113
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3333
unsigned int Oid
Definition: postgres_ext.h:31
SetConstraintState state
Definition: trigger.c:3559
Definition: regguts.h:298
int i
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

Definition at line 3885 of file trigger.c.

References AfterTriggersTableData::after_trig_done, AfterTriggersTableData::after_trig_events, Assert, AfterTriggersQueryData::events, AfterTriggerEventList::head, lfirst, AfterTriggerEventChunk::next, pfree(), AfterTriggersQueryData::tables, AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.

Referenced by AfterTriggerEndQuery().

3886 {
3887  AfterTriggerEventChunk *target = qs->events.head;
3888  ListCell *lc;
3889 
3890  Assert(target && target->next);
3891 
3892  /*
3893  * First, update any pointers in the per-table data, so that they won't be
3894  * dangling. Resetting obsoleted pointers to NULL will make
3895  * cancel_prior_stmt_triggers start from the list head, which is fine.
3896  */
3897  foreach(lc, qs->tables)
3898  {
3900 
3901  if (table->after_trig_done &&
3902  table->after_trig_events.tail == target)
3903  {
3904  table->after_trig_events.head = NULL;
3905  table->after_trig_events.tail = NULL;
3906  table->after_trig_events.tailfree = NULL;
3907  }
3908  }
3909 
3910  /* Now we can flush the head chunk */
3911  qs->events.head = target->next;
3912  pfree(target);
3913 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
struct AfterTriggerEventChunk * next
Definition: trigger.c:3440
void pfree(void *pointer)
Definition: mcxt.c:949
AfterTriggerEventList after_trig_events
Definition: trigger.c:3597
#define Assert(condition)
Definition: c.h:670
#define lfirst(lc)
Definition: pg_list.h:106
AfterTriggerEventChunk * head
Definition: trigger.c:3452
AfterTriggerEventList events
Definition: trigger.c:3575

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 4546 of file trigger.c.

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

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

Definition at line 4842 of file trigger.c.

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AfterTriggerFreeQuery(), afterTriggerRestoreEventList(), Assert, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_firing_id, AfterTriggersData::events, AfterTriggersTransData::events, AfterTriggersTransData::firing_counter, for_each_event_chunk, GetCurrentTransactionNestLevel(), GetTriggerSharedData, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, pfree(), AfterTriggersData::query_depth, AfterTriggersTransData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, AfterTriggersTransData::state, and AfterTriggersData::trans_stack.

Referenced by AbortSubTransaction(), and CommitSubTransaction().

4843 {
4844  int my_level = GetCurrentTransactionNestLevel();
4846  AfterTriggerEvent event;
4847  AfterTriggerEventChunk *chunk;
4848  CommandId subxact_firing_id;
4849 
4850  /*
4851  * Pop the prior state if needed.
4852  */
4853  if (isCommit)
4854  {
4855  Assert(my_level < afterTriggers.maxtransdepth);
4856  /* If we saved a prior state, we don't need it anymore */
4857  state = afterTriggers.trans_stack[my_level].state;
4858  if (state != NULL)
4859  pfree(state);
4860  /* this avoids double pfree if error later: */
4861  afterTriggers.trans_stack[my_level].state = NULL;
4864  }
4865  else
4866  {
4867  /*
4868  * Aborting. It is possible subxact start failed before calling
4869  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
4870  * trans_stack levels that aren't there.
4871  */
4872  if (my_level >= afterTriggers.maxtransdepth)
4873  return;
4874 
4875  /*
4876  * Release query-level storage for queries being aborted, and restore
4877  * query_depth to its pre-subxact value. This assumes that a
4878  * subtransaction will not add events to query levels started in a
4879  * earlier transaction state.
4880  */
4882  {
4886  }
4889 
4890  /*
4891  * Restore the global deferred-event list to its former length,
4892  * discarding any events queued by the subxact.
4893  */
4895  &afterTriggers.trans_stack[my_level].events);
4896 
4897  /*
4898  * Restore the trigger state. If the saved state is NULL, then this
4899  * subxact didn't save it, so it doesn't need restoring.
4900  */
4901  state = afterTriggers.trans_stack[my_level].state;
4902  if (state != NULL)
4903  {
4905  afterTriggers.state = state;
4906  }
4907  /* this avoids double pfree if error later: */
4908  afterTriggers.trans_stack[my_level].state = NULL;
4909 
4910  /*
4911  * Scan for any remaining deferred events that were marked DONE or IN
4912  * PROGRESS by this subxact or a child, and un-mark them. We can
4913  * recognize such events because they have a firing ID greater than or
4914  * equal to the firing_counter value we saved at subtransaction start.
4915  * (This essentially assumes that the current subxact includes all
4916  * subxacts started after it.)
4917  */
4918  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
4920  {
4921  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4922 
4923  if (event->ate_flags &
4925  {
4926  if (evtshared->ats_firing_id >= subxact_firing_id)
4927  event->ate_flags &=
4929  }
4930  }
4931  }
4932 }
uint32 CommandId
Definition: c.h:459
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
TriggerFlags ate_flags
Definition: trigger.c:3403
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3465
AfterTriggerEventList events
Definition: trigger.c:3584
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
CommandId firing_counter
Definition: trigger.c:3586
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
void pfree(void *pointer)
Definition: mcxt.c:949
SetConstraintState state
Definition: trigger.c:3559
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:754
#define Assert(condition)
Definition: c.h:670
Definition: regguts.h:298
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:3845
CommandId ats_firing_id
Definition: trigger.c:3395
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3380
AfterTriggerEventList events
Definition: trigger.c:3560
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4637
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
SetConstraintState state
Definition: trigger.c:3583

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

Definition at line 4746 of file trigger.c.

References AfterTriggersData::event_cxt, AfterTriggersData::events, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, MemoryContextDelete(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and AfterTriggersData::trans_stack.

Referenced by AbortTransaction(), CommitTransaction(), and PrepareTransaction().

4747 {
4748  /*
4749  * Forget the pending-events list.
4750  *
4751  * Since all the info is in TopTransactionContext or children thereof, we
4752  * don't really need to do anything to reclaim memory. However, the
4753  * pending-events list could be large, and so it's useful to discard it as
4754  * soon as possible --- especially if we are aborting because we ran out
4755  * of memory for the list!
4756  */
4758  {
4760  afterTriggers.event_cxt = NULL;
4761  afterTriggers.events.head = NULL;
4762  afterTriggers.events.tail = NULL;
4763  afterTriggers.events.tailfree = NULL;
4764  }
4765 
4766  /*
4767  * Forget any subtransaction state as well. Since this can't be very
4768  * large, we let the eventual reset of TopTransactionContext free the
4769  * memory instead of doing it here.
4770  */
4771  afterTriggers.trans_stack = NULL;
4773 
4774 
4775  /*
4776  * Forget the query stack and constraint-related state information. As
4777  * with the subtransaction state information, we don't bother freeing the
4778  * memory here.
4779  */
4780  afterTriggers.query_stack = NULL;
4782  afterTriggers.state = NULL;
4783 
4784  /* No more afterTriggers manipulation until next transaction starts. */
4786 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
SetConstraintState state
Definition: trigger.c:3559
AfterTriggerEventChunk * head
Definition: trigger.c:3452
MemoryContext event_cxt
Definition: trigger.c:3561
AfterTriggerEventList events
Definition: trigger.c:3560
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

Definition at line 4944 of file trigger.c.

References Assert, AfterTriggersQueryData::events, AfterTriggersQueryData::fdw_tuplestore, AfterTriggerEventList::head, Max, AfterTriggersData::maxquerydepth, MemoryContextAlloc(), NIL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, repalloc(), AfterTriggersQueryData::tables, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TopTransactionContext.

Referenced by AfterTriggerSaveEvent(), before_stmt_triggers_fired(), and MakeTransitionCaptureState().

4945 {
4946  int init_depth = afterTriggers.maxquerydepth;
4947 
4949 
4950  if (afterTriggers.maxquerydepth == 0)
4951  {
4952  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
4953 
4956  new_alloc * sizeof(AfterTriggersQueryData));
4957  afterTriggers.maxquerydepth = new_alloc;
4958  }
4959  else
4960  {
4961  /* repalloc will keep the stack in the same context */
4962  int old_alloc = afterTriggers.maxquerydepth;
4963  int new_alloc = Max(afterTriggers.query_depth + 1,
4964  old_alloc * 2);
4965 
4968  new_alloc * sizeof(AfterTriggersQueryData));
4969  afterTriggers.maxquerydepth = new_alloc;
4970  }
4971 
4972  /* Initialize new array entries to empty */
4973  while (init_depth < afterTriggers.maxquerydepth)
4974  {
4976 
4977  qs->events.head = NULL;
4978  qs->events.tail = NULL;
4979  qs->events.tailfree = NULL;
4980  qs->fdw_tuplestore = NULL;
4981  qs->tables = NIL;
4982 
4983  ++init_depth;
4984  }
4985 }
#define NIL
Definition: pg_list.h:69
MemoryContext TopTransactionContext
Definition: mcxt.c:48
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3576
#define Max(x, y)
Definition: c.h:796
#define Assert(condition)
Definition: c.h:670
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:962
AfterTriggerEventChunk * head
Definition: trigger.c:3452
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:706
AfterTriggerEventList events
Definition: trigger.c:3575
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ AfterTriggerExecute()

static void AfterTriggerExecute ( AfterTriggerEvent  event,
Relation  rel,
TriggerDesc trigdesc,
FmgrInfo finfo,
Instrumentation instr,
MemoryContext  per_tuple_context,
TupleTableSlot trig_tuple_slot1,
TupleTableSlot trig_tuple_slot2 
)
static

Definition at line 3939 of file trigger.c.

References AFTER_TRIGGER_2CTID, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_TUP_BITS, AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, AfterTriggersTableData::closed, elog, ERROR, ExecCallTriggerFunc(), ExecMaterializeSlot(), GetCurrentFDWTuplestore(), GetTriggerSharedData, heap_fetch(), heap_freetuple(), InstrStartNode(), InstrStopNode(), InvalidBuffer, ItemPointerCopy, ItemPointerIsValid, MemoryContextReset(), AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, ReleaseBuffer(), SnapshotAny, HeapTupleData::t_self, T_TriggerData, TriggerData::tg_event, TriggerData::tg_newtable, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_oldtable, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerDesc::triggers, tuplestore_gettupleslot(), and TriggerData::type.

Referenced by afterTriggerInvokeEvents().

3945 {
3946  AfterTriggerShared evtshared = GetTriggerSharedData(event);
3947  Oid tgoid = evtshared->ats_tgoid;
3948  TriggerData LocTriggerData;
3949  HeapTupleData tuple1;
3950  HeapTupleData tuple2;
3951  HeapTuple rettuple;
3952  Buffer buffer1 = InvalidBuffer;
3953  Buffer buffer2 = InvalidBuffer;
3954  int tgindx;
3955 
3956  /*
3957  * Locate trigger in trigdesc.
3958  */
3959  LocTriggerData.tg_trigger = NULL;
3960  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
3961  {
3962  if (trigdesc->triggers[tgindx].tgoid == tgoid)
3963  {
3964  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
3965  break;
3966  }
3967  }
3968  if (LocTriggerData.tg_trigger == NULL)
3969  elog(ERROR, "could not find trigger %u", tgoid);
3970 
3971  /*
3972  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
3973  * to include time spent re-fetching tuples in the trigger cost.
3974  */
3975  if (instr)
3976  InstrStartNode(instr + tgindx);
3977 
3978  /*
3979  * Fetch the required tuple(s).
3980  */
3981  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
3982  {
3984  {
3985  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
3986 
3987  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
3988  trig_tuple_slot1))
3989  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
3990 
3991  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
3993  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
3994  trig_tuple_slot2))
3995  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
3996  }
3997  /* fall through */
3999 
4000  /*
4001  * Using ExecMaterializeSlot() rather than ExecFetchSlotTuple()
4002  * ensures that tg_trigtuple does not reference tuplestore memory.
4003  * (It is formally possible for the trigger function to queue
4004  * trigger events that add to the same tuplestore, which can push
4005  * other tuples out of memory.) The distinction is academic,
4006  * because we start with a minimal tuple that ExecFetchSlotTuple()
4007  * must materialize anyway.
4008  */
4009  LocTriggerData.tg_trigtuple =
4010  ExecMaterializeSlot(trig_tuple_slot1);
4011  LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
4012 
4013  LocTriggerData.tg_newtuple =
4014  ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4016  ExecMaterializeSlot(trig_tuple_slot2) : NULL;
4017  LocTriggerData.tg_newtuplebuf = InvalidBuffer;
4018 
4019  break;
4020 
4021  default:
4022  if (ItemPointerIsValid(&(event->ate_ctid1)))
4023  {
4024  ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
4025  if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
4026  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4027  LocTriggerData.tg_trigtuple = &tuple1;
4028  LocTriggerData.tg_trigtuplebuf = buffer1;
4029  }
4030  else
4031  {
4032  LocTriggerData.tg_trigtuple = NULL;
4033  LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
4034  }
4035 
4036  /* don't touch ctid2 if not there */
4037  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4039  ItemPointerIsValid(&(event->ate_ctid2)))
4040  {
4041  ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
4042  if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
4043  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4044  LocTriggerData.tg_newtuple = &tuple2;
4045  LocTriggerData.tg_newtuplebuf = buffer2;
4046  }
4047  else
4048  {
4049  LocTriggerData.tg_newtuple = NULL;
4050  LocTriggerData.tg_newtuplebuf = InvalidBuffer;
4051  }
4052  }
4053 
4054  /*
4055  * Set up the tuplestore information to let the trigger have access to
4056  * transition tables. When we first make a transition table available to
4057  * a trigger, mark it "closed" so that it cannot change anymore. If any
4058  * additional events of the same type get queued in the current trigger
4059  * query level, they'll go into new transition tables.
4060  */
4061  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4062  if (evtshared->ats_table)
4063  {
4064  if (LocTriggerData.tg_trigger->tgoldtable)
4065  {
4066  LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4067  evtshared->ats_table->closed = true;
4068  }
4069 
4070  if (LocTriggerData.tg_trigger->tgnewtable)
4071  {
4072  LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4073  evtshared->ats_table->closed = true;
4074  }
4075  }
4076 
4077  /*
4078  * Setup the remaining trigger information
4079  */
4080  LocTriggerData.type = T_TriggerData;
4081  LocTriggerData.tg_event =
4083  LocTriggerData.tg_relation = rel;
4084 
4085  MemoryContextReset(per_tuple_context);
4086 
4087  /*
4088  * Call the trigger and throw away any possibly returned updated tuple.
4089  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4090  */
4091  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4092  tgindx,
4093  finfo,
4094  NULL,
4095  per_tuple_context);
4096  if (rettuple != NULL &&
4097  rettuple != LocTriggerData.tg_trigtuple &&
4098  rettuple != LocTriggerData.tg_newtuple)
4099  heap_freetuple(rettuple);
4100 
4101  /*
4102  * Release buffers
4103  */
4104  if (buffer1 != InvalidBuffer)
4105  ReleaseBuffer(buffer1);
4106  if (buffer2 != InvalidBuffer)
4107  ReleaseBuffer(buffer2);
4108 
4109  /*
4110  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4111  * one "tuple returned" (really the number of firings).
4112  */
4113  if (instr)
4114  InstrStopNode(instr + tgindx, 1);
4115 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:60
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
TriggerEvent ats_event
Definition: trigger.c:3392
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:80
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3382
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3626
ItemPointerData ate_ctid2
Definition: trigger.c:3405
TriggerFlags ate_flags
Definition: trigger.c:3403
Buffer tg_newtuplebuf
Definition: trigger.h:39
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, Relation stats_relation)
Definition: heapam.c:1876
Oid tgoid
Definition: reltrigger.h:25
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3386
#define InvalidBuffer
Definition: buf.h:25
Tuplestorestate * old_tuplestore
Definition: trigger.c:3598
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:135
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
unsigned int Oid
Definition: postgres_ext.h:31
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:102
HeapTuple tg_trigtuple
Definition: trigger.h:35
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
#define ERROR
Definition: elog.h:43
void InstrStartNode(Instrumentation *instr)
Definition: instrument.c:63
ItemPointerData t_self
Definition: htup.h:65
Trigger * triggers
Definition: reltrigger.h:48
Buffer tg_trigtuplebuf
Definition: trigger.h:38
Tuplestorestate * new_tuplestore
Definition: trigger.c:3599
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3385
int numtriggers
Definition: reltrigger.h:49
#define SnapshotAny
Definition: tqual.h:28
char * tgnewtable
Definition: reltrigger.h:43
Trigger * tg_trigger
Definition: trigger.h:37
HeapTuple tg_newtuple
Definition: trigger.h:36
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3396
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
TriggerEvent tg_event
Definition: trigger.h:33
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3383
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:725
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:100
Tuplestorestate * tg_oldtable
Definition: trigger.h:40
NodeTag type
Definition: trigger.h:32
Tuplestorestate * tg_newtable
Definition: trigger.h:41
#define elog
Definition: elog.h:219
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition: trigger.c:2126
ItemPointerData ate_ctid1
Definition: trigger.c:3404
char * tgoldtable
Definition: reltrigger.h:42
int Buffer
Definition: buf.h:23
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:139
Relation tg_relation
Definition: trigger.h:34

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

Definition at line 4690 of file trigger.c.

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

Referenced by CommitTransaction(), and PrepareTransaction().

4691 {
4692  AfterTriggerEventList *events;
4693  bool snap_pushed = false;
4694 
4695  /* Must not be inside a query */
4697 
4698  /*
4699  * If there are any triggers to fire, make sure we have set a snapshot for
4700  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4701  * can't assume ActiveSnapshot is valid on entry.)
4702  */
4703  events = &afterTriggers.events;
4704  if (events->head != NULL)
4705  {
4707  snap_pushed = true;
4708  }
4709 
4710  /*
4711  * Run all the remaining triggers. Loop until they are all gone, in case
4712  * some trigger queues more for us to do.
4713  */
4714  while (afterTriggerMarkEvents(events, NULL, false))
4715  {
4716  CommandId firing_id = afterTriggers.firing_counter++;
4717 
4718  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4719  break; /* all fired */
4720  }
4721 
4722  /*
4723  * We don't bother freeing the event list, since it will go away anyway
4724  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4725  */
4726 
4727  if (snap_pushed)
4729 }
uint32 CommandId
Definition: c.h:459
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4205
void PopActiveSnapshot(void)
Definition: snapmgr.c:812
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:304
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:733
CommandId firing_counter
Definition: trigger.c:3558
#define Assert(condition)
Definition: c.h:670
AfterTriggerEventChunk * head
Definition: trigger.c:3452
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4133
AfterTriggerEventList events
Definition: trigger.c:3560
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 3824 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

3825 {
3826  AfterTriggerEventChunk *chunk;
3827 
3828  while ((chunk = events->head) != NULL)
3829  {
3830  events->head = chunk->next;
3831  pfree(chunk);
3832  }
3833  events->tail = NULL;
3834  events->tailfree = NULL;
3835 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
struct AfterTriggerEventChunk * next
Definition: trigger.c:3440
void pfree(void *pointer)
Definition: mcxt.c:949
AfterTriggerEventChunk * head
Definition: trigger.c:3452

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 4637 of file trigger.c.

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

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

4638 {
4639  Tuplestorestate *ts;
4640  List *tables;
4641  ListCell *lc;
4642 
4643  /* Drop the trigger events */
4645 
4646  /* Drop FDW tuplestore if any */
4647  ts = qs->fdw_tuplestore;
4648  qs->fdw_tuplestore = NULL;
4649  if (ts)
4650  tuplestore_end(ts);
4651 
4652  /* Release per-table subsidiary storage */
4653  tables = qs->tables;
4654  foreach(lc, tables)
4655  {
4657 
4658  ts = table->old_tuplestore;
4659  table->old_tuplestore = NULL;
4660  if (ts)
4661  tuplestore_end(ts);
4662  ts = table->new_tuplestore;
4663  table->new_tuplestore = NULL;
4664  if (ts)
4665  tuplestore_end(ts);
4666  }
4667 
4668  /*
4669  * Now free the AfterTriggersTableData structs and list cells. Reset list
4670  * pointer first; if list_free_deep somehow gets an error, better to leak
4671  * that storage than have an infinite loop.
4672  */
4673  qs->tables = NIL;
4674  list_free_deep(tables);
4675 }
#define NIL
Definition: pg_list.h:69
Tuplestorestate * old_tuplestore
Definition: trigger.c:3598
void list_free_deep(List *list)
Definition: list.c:1147
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3824
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3576
Tuplestorestate * new_tuplestore
Definition: trigger.c:3599
#define lfirst(lc)
Definition: pg_list.h:106
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3575
Definition: pg_list.h:45

◆ afterTriggerInvokeEvents()

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

Definition at line 4205 of file trigger.c.

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

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

4209 {
4210  bool all_fired = true;
4211  AfterTriggerEventChunk *chunk;
4212  MemoryContext per_tuple_context;
4213  bool local_estate = false;
4214  Relation rel = NULL;
4215  TriggerDesc *trigdesc = NULL;
4216  FmgrInfo *finfo = NULL;
4217  Instrumentation *instr = NULL;
4218  TupleTableSlot *slot1 = NULL,
4219  *slot2 = NULL;
4220 
4221  /* Make a local EState if need be */
4222  if (estate == NULL)
4223  {
4224  estate = CreateExecutorState();
4225  local_estate = true;
4226  }
4227 
4228  /* Make a per-tuple memory context for trigger function calls */
4229  per_tuple_context =
4231  "AfterTriggerTupleContext",
4233 
4234  for_each_chunk(chunk, *events)
4235  {
4236  AfterTriggerEvent event;
4237  bool all_fired_in_chunk = true;
4238 
4239  for_each_event(event, chunk)
4240  {
4241  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4242 
4243  /*
4244  * Is it one for me to fire?
4245  */
4246  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4247  evtshared->ats_firing_id == firing_id)
4248  {
4249  /*
4250  * So let's fire it... but first, find the correct relation if
4251  * this is not the same relation as before.
4252  */
4253  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4254  {
4255  ResultRelInfo *rInfo;
4256 
4257  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4258  rel = rInfo->ri_RelationDesc;
4259  trigdesc = rInfo->ri_TrigDesc;
4260  finfo = rInfo->ri_TrigFunctions;
4261  instr = rInfo->ri_TrigInstrument;
4262  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4263  {
4264  if (slot1 != NULL)
4265  {
4268  }
4269  slot1 = MakeSingleTupleTableSlot(rel->rd_att);
4270  slot2 = MakeSingleTupleTableSlot(rel->rd_att);
4271  }
4272  if (trigdesc == NULL) /* should not happen */
4273  elog(ERROR, "relation %u has no triggers",
4274  evtshared->ats_relid);
4275  }
4276 
4277  /*
4278  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4279  * still set, so recursive examinations of the event list
4280  * won't try to re-fire it.
4281  */
4282  AfterTriggerExecute(event, rel, trigdesc, finfo, instr,
4283  per_tuple_context, slot1, slot2);
4284 
4285  /*
4286  * Mark the event as done.
4287  */
4288  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4289  event->ate_flags |= AFTER_TRIGGER_DONE;
4290  }
4291  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4292  {
4293  /* something remains to be done */
4294  all_fired = all_fired_in_chunk = false;
4295  }
4296  }
4297 
4298  /* Clear the chunk if delete_ok and nothing left of interest */
4299  if (delete_ok && all_fired_in_chunk)
4300  {
4301  chunk->freeptr = CHUNK_DATA_START(chunk);
4302  chunk->endfree = chunk->endptr;
4303 
4304  /*
4305  * If it's last chunk, must sync event list's tailfree too. Note
4306  * that delete_ok must NOT be passed as true if there could be
4307  * additional AfterTriggerEventList values pointing at this event
4308  * list, since we'd fail to fix their copies of tailfree.
4309  */
4310  if (chunk == events->tail)
4311  events->tailfree = chunk->freeptr;
4312  }
4313  }
4314  if (slot1 != NULL)
4315  {
4318  }
4319 
4320  /* Release working resources */
4321  MemoryContextDelete(per_tuple_context);
4322 
4323  if (local_estate)
4324  {
4325  ExecCleanUpTriggerState(estate);
4326  FreeExecutorState(estate);
4327  }
4328 
4329  return all_fired;
4330 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:355
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
TriggerFlags ate_flags
Definition: trigger.c:3403
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3447
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:376
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
Form_pg_class rd_rel
Definition: rel.h:114
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
#define for_each_event(eptr, cptr)
Definition: trigger.c:3460
void FreeExecutorState(EState *estate)
Definition: execUtils.c:186
#define ERROR
Definition: elog.h:43
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:170
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:216
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc)
Definition: execTuples.c:199
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:367
EState * CreateExecutorState(void)
Definition: execUtils.c:81
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1388
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:342
TupleDesc rd_att
Definition: rel.h:115
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3458
CommandId ats_firing_id
Definition: trigger.c:3395
void ExecCleanUpTriggerState(EState *estate)
Definition: execMain.c:1466
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3380
#define elog
Definition: elog.h:219
#define RelationGetRelid(relation)
Definition: rel.h:425
static void AfterTriggerExecute(AfterTriggerEvent event, Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
Definition: trigger.c:3939
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:370

◆ afterTriggerMarkEvents()

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

Definition at line 4133 of file trigger.c.

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, afterTriggerAddEvent(), afterTriggerCheckState(), AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_firing_id, AfterTriggersData::firing_counter, for_each_event_chunk, and GetTriggerSharedData.

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

4136 {
4137  bool found = false;
4138  AfterTriggerEvent event;
4139  AfterTriggerEventChunk *chunk;
4140 
4141  for_each_event_chunk(event, chunk, *events)
4142  {
4143  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4144  bool defer_it = false;
4145 
4146  if (!(event->ate_flags &
4148  {
4149  /*
4150  * This trigger hasn't been called or scheduled yet. Check if we
4151  * should call it now.
4152  */
4153  if (immediate_only && afterTriggerCheckState(evtshared))
4154  {
4155  defer_it = true;
4156  }
4157  else
4158  {
4159  /*
4160  * Mark it as to be fired in this firing cycle.
4161  */
4163  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4164  found = true;
4165  }
4166  }
4167 
4168  /*
4169  * If it's deferred, move it to move_list, if requested.
4170  */
4171  if (defer_it && move_list != NULL)
4172  {
4173  /* add it to move_list */
4174  afterTriggerAddEvent(move_list, event, evtshared);
4175  /* mark original copy "done" so we don't do it again */
4176  event->ate_flags |= AFTER_TRIGGER_DONE;
4177  }
4178  }
4179 
4180  return found;
4181 }
TriggerFlags ate_flags
Definition: trigger.c:3403
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3465
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3662
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
CommandId firing_counter
Definition: trigger.c:3558
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3708
CommandId ats_firing_id
Definition: trigger.c:3395
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3380
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

Definition at line 5360 of file trigger.c.

References AFTER_TRIGGER_DONE, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_relid, AfterTriggersData::events, AfterTriggersQueryData::events, for_each_event_chunk, GetTriggerSharedData, AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.

Referenced by CheckTableNotInUse().

5361 {
5362  AfterTriggerEvent event;
5363  AfterTriggerEventChunk *chunk;
5364  int depth;
5365 
5366  /* Scan queued events */
5368  {
5369  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5370 
5371  /*
5372  * We can ignore completed events. (Even if a DONE flag is rolled
5373  * back by subxact abort, it's OK because the effects of the TRUNCATE
5374  * or whatever must get rolled back too.)
5375  */
5376  if (event->ate_flags & AFTER_TRIGGER_DONE)
5377  continue;
5378 
5379  if (evtshared->ats_relid == relid)
5380  return true;
5381  }
5382 
5383  /*
5384  * Also scan events queued by incomplete queries. This could only matter
5385  * if TRUNCATE/etc is executed by a function or trigger within an updating
5386  * query on the same relation, which is pretty perverse, but let's check.
5387  */
5388  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5389  {
5391  {
5392  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5393 
5394  if (event->ate_flags & AFTER_TRIGGER_DONE)
5395  continue;
5396 
5397  if (evtshared->ats_relid == relid)
5398  return true;
5399  }
5400  }
5401 
5402  return false;
5403 }
TriggerFlags ate_flags
Definition: trigger.c:3403
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3465
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
AfterTriggerEventList events
Definition: trigger.c:3560
AfterTriggerEventList events
Definition: trigger.c:3575
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ afterTriggerRestoreEventList()

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

Definition at line 3845 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

3847 {
3848  AfterTriggerEventChunk *chunk;
3849  AfterTriggerEventChunk *next_chunk;
3850 
3851  if (old_events->tail == NULL)
3852  {
3853  /* restoring to a completely empty state, so free everything */
3854  afterTriggerFreeEventList(events);
3855  }
3856  else
3857  {
3858  *events = *old_events;
3859  /* free any chunks after the last one we want to keep */
3860  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
3861  {
3862  next_chunk = chunk->next;
3863  pfree(chunk);
3864  }
3865  /* and clean up the tail chunk to be the right length */
3866  events->tail->next = NULL;
3867  events->tail->freeptr = events->tailfree;
3868 
3869  /*
3870  * We don't make any effort to remove now-unused shared data records.
3871  * They might still be useful, anyway.
3872  */
3873  }
3874 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
struct AfterTriggerEventChunk * next
Definition: trigger.c:3440
void pfree(void *pointer)
Definition: mcxt.c:949
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3824

◆ AfterTriggerSaveEvent()

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

Definition at line 5425 of file trigger.c.

References AFTER_TRIGGER_1CTID, AFTER_TRIGGER_2CTID, AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_INITDEFERRED, afterTriggerAddEvent(), AfterTriggerEnlargeQueryState(), Assert, AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_relid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, cancel_prior_stmt_triggers(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, do_convert_tuple(), elog, ERROR, AfterTriggersQueryData::events, GetCurrentFDWTuplestore(), i, ItemPointerCopy, ItemPointerSetInvalid, list_member_oid(), AfterTriggersData::maxquerydepth, AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, pfree(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, RelationData::rd_rel, RelationGetRelid, RELKIND_FOREIGN_TABLE, 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, HeapTupleData::t_self, TransitionCaptureState::tcs_delete_old_table, TransitionCaptureState::tcs_insert_new_table, TransitionCaptureState::tcs_map, TransitionCaptureState::tcs_original_insert_tuple, TransitionCaptureState::tcs_private, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, Trigger::tgconstrindid, Trigger::tgdeferrable, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgtype, TriggerDesc::trig_delete_after_row, TriggerDesc::trig_insert_after_row, TriggerDesc::trig_update_after_row, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_TRUNCATE, TRIGGER_EVENT_UPDATE, TRIGGER_FIRED_BY_UPDATE, TRIGGER_TYPE_AFTER, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_TRUNCATE, TRIGGER_TYPE_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and tuplestore_puttuple().

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

5430 {
5431  Relation rel = relinfo->ri_RelationDesc;
5432  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
5433  AfterTriggerEventData new_event;
5434  AfterTriggerSharedData new_shared;
5435  char relkind = rel->rd_rel->relkind;
5436  int tgtype_event;
5437  int tgtype_level;
5438  int i;
5439  Tuplestorestate *fdw_tuplestore = NULL;
5440 
5441  /*
5442  * Check state. We use a normal test not Assert because it is possible to
5443  * reach here in the wrong state given misconfigured RI triggers, in
5444  * particular deferring a cascade action trigger.
5445  */
5446  if (afterTriggers.query_depth < 0)
5447  elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
5448 
5449  /* Be sure we have enough space to record events at this query depth. */
5452 
5453  /*
5454  * If the directly named relation has any triggers with transition tables,
5455  * then we need to capture transition tuples.
5456  */
5457  if (row_trigger && transition_capture != NULL)
5458  {
5459  HeapTuple original_insert_tuple = transition_capture->tcs_original_insert_tuple;
5460  TupleConversionMap *map = transition_capture->tcs_map;
5461  bool delete_old_table = transition_capture->tcs_delete_old_table;
5462  bool update_old_table = transition_capture->tcs_update_old_table;
5463  bool update_new_table = transition_capture->tcs_update_new_table;
5464  bool insert_new_table = transition_capture->tcs_insert_new_table;;
5465 
5466  if ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
5467  (event == TRIGGER_EVENT_UPDATE && update_old_table))
5468  {
5469  Tuplestorestate *old_tuplestore;
5470 
5471  Assert(oldtup != NULL);
5472  old_tuplestore = transition_capture->tcs_private->old_tuplestore;
5473 
5474  if (map != NULL)
5475  {
5476  HeapTuple converted = do_convert_tuple(oldtup, map);
5477 
5478  tuplestore_puttuple(old_tuplestore, converted);
5479  pfree(converted);
5480  }
5481  else
5482  tuplestore_puttuple(old_tuplestore, oldtup);
5483  }
5484  if ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
5485  (event == TRIGGER_EVENT_UPDATE && update_new_table))
5486  {
5487  Tuplestorestate *new_tuplestore;
5488 
5489  Assert(newtup != NULL);
5490  new_tuplestore = transition_capture->tcs_private->new_tuplestore;
5491 
5492  if (original_insert_tuple != NULL)
5493  tuplestore_puttuple(new_tuplestore, original_insert_tuple);
5494  else if (map != NULL)
5495  {
5496  HeapTuple converted = do_convert_tuple(newtup, map);
5497 
5498  tuplestore_puttuple(new_tuplestore, converted);
5499  pfree(converted);
5500  }
5501  else
5502  tuplestore_puttuple(new_tuplestore, newtup);
5503  }
5504 
5505  /* If transition tables are the only reason we're here, return. */
5506  if (trigdesc == NULL ||
5507  (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
5508  (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
5509  (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row))
5510  return;
5511  }
5512 
5513  /*
5514  * Validate the event code and collect the associated tuple CTIDs.
5515  *
5516  * The event code will be used both as a bitmask and an array offset, so
5517  * validation is important to make sure we don't walk off the edge of our
5518  * arrays.
5519  *
5520  * Also, if we're considering statement-level triggers, check whether we
5521  * already queued a set of them for this event, and cancel the prior set
5522  * if so. This preserves the behavior that statement-level triggers fire
5523  * just once per statement and fire after row-level triggers.
5524  */
5525  switch (event)
5526  {
5527  case TRIGGER_EVENT_INSERT:
5528  tgtype_event = TRIGGER_TYPE_INSERT;
5529  if (row_trigger)
5530  {
5531  Assert(oldtup == NULL);
5532  Assert(newtup != NULL);
5533  ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid1));
5534  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5535  }
5536  else
5537  {
5538  Assert(oldtup == NULL);
5539  Assert(newtup == NULL);
5540  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5541  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5543  CMD_INSERT, event);
5544  }
5545  break;
5546  case TRIGGER_EVENT_DELETE:
5547  tgtype_event = TRIGGER_TYPE_DELETE;
5548  if (row_trigger)
5549  {
5550  Assert(oldtup != NULL);
5551  Assert(newtup == NULL);
5552  ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
5553  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5554  }
5555  else
5556  {
5557  Assert(oldtup == NULL);
5558  Assert(newtup == NULL);
5559  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5560  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5562  CMD_DELETE, event);
5563  }
5564  break;
5565  case TRIGGER_EVENT_UPDATE:
5566  tgtype_event = TRIGGER_TYPE_UPDATE;
5567  if (row_trigger)
5568  {
5569  Assert(oldtup != NULL);
5570  Assert(newtup != NULL);
5571  ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
5572  ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid2));
5573  }
5574  else
5575  {
5576  Assert(oldtup == NULL);
5577  Assert(newtup == NULL);
5578  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5579  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5581  CMD_UPDATE, event);
5582  }
5583  break;
5585  tgtype_event = TRIGGER_TYPE_TRUNCATE;
5586  Assert(oldtup == NULL);
5587  Assert(newtup == NULL);
5588  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5589  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5590  break;
5591  default:
5592  elog(ERROR, "invalid after-trigger event code: %d", event);
5593  tgtype_event = 0; /* keep compiler quiet */
5594  break;
5595  }
5596 
5597  if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
5598  new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ?
5600  /* else, we'll initialize ate_flags for each trigger */
5601 
5602  tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
5603 
5604  for (i = 0; i < trigdesc->numtriggers; i++)
5605  {
5606  Trigger *trigger = &trigdesc->triggers[i];
5607 
5608  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
5609  tgtype_level,
5611  tgtype_event))
5612  continue;
5613  if (!TriggerEnabled(estate, relinfo, trigger, event,
5614  modifiedCols, oldtup, newtup))
5615  continue;
5616 
5617  if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
5618  {
5619  if (fdw_tuplestore == NULL)
5620  {
5621  fdw_tuplestore = GetCurrentFDWTuplestore();
5622  new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH;
5623  }
5624  else
5625  /* subsequent event for the same tuple */
5626  new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE;
5627  }
5628 
5629  /*
5630  * If the trigger is a foreign key enforcement trigger, there are
5631  * certain cases where we can skip queueing the event because we can
5632  * tell by inspection that the FK constraint will still pass.
5633  */
5634  if (TRIGGER_FIRED_BY_UPDATE(event))
5635  {
5636  switch (RI_FKey_trigger_type(trigger->tgfoid))
5637  {
5638  case RI_TRIGGER_PK:
5639  /* Update on trigger's PK table */
5640  if (!RI_FKey_pk_upd_check_required(trigger, rel,
5641  oldtup, newtup))
5642  {
5643  /* skip queuing this event */
5644  continue;
5645  }
5646  break;
5647 
5648  case RI_TRIGGER_FK:
5649  /* Update on trigger's FK table */
5650  if (!RI_FKey_fk_upd_check_required(trigger, rel,
5651  oldtup, newtup))
5652  {
5653  /* skip queuing this event */
5654  continue;
5655  }
5656  break;
5657 
5658  case RI_TRIGGER_NONE:
5659  /* Not an FK trigger */
5660  break;
5661  }
5662  }
5663 
5664  /*
5665  * If the trigger is a deferred unique constraint check trigger, only
5666  * queue it if the unique constraint was potentially violated, which
5667  * we know from index insertion time.
5668  */
5669  if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
5670  {
5671  if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
5672  continue; /* Uniqueness definitely not violated */
5673  }
5674 
5675  /*
5676  * Fill in event structure and add it to the current query's queue.
5677  * Note we set ats_table to NULL whenever this trigger doesn't use
5678  * transition tables, to improve sharability of the shared event data.
5679  */
5680  new_shared.ats_event =
5681  (event & TRIGGER_EVENT_OPMASK) |
5682  (row_trigger ? TRIGGER_EVENT_ROW : 0) |
5683  (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
5684  (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
5685  new_shared.ats_tgoid = trigger->tgoid;
5686  new_shared.ats_relid = RelationGetRelid(rel);
5687  new_shared.ats_firing_id = 0;
5688  if ((trigger->tgoldtable || trigger->tgnewtable) &&
5689  transition_capture != NULL)
5690  new_shared.ats_table = transition_capture->tcs_private;
5691  else
5692  new_shared.ats_table = NULL;
5693 
5695  &new_event, &new_shared);
5696  }
5697 
5698  /*
5699  * Finally, spool any foreign tuple(s). The tuplestore squashes them to
5700  * minimal tuples, so this loses any system columns. The executor lost
5701  * those columns before us, for an unrelated reason, so this is fine.
5702  */
5703  if (fdw_tuplestore)
5704  {
5705  if (oldtup != NULL)
5706  tuplestore_puttuple(fdw_tuplestore, oldtup);
5707  if (newtup != NULL)
5708  tuplestore_puttuple(fdw_tuplestore, newtup);
5709  }
5710 }
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
TriggerEvent ats_event
Definition: trigger.c:3392
Relation ri_RelationDesc
Definition: execnodes.h:355
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3271
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3382
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:114
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3626
ItemPointerData ate_ctid2
Definition: trigger.c:3405
#define TRIGGER_TYPE_DELETE
Definition: pg_trigger.h:101
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:99
Oid tgfoid
Definition: reltrigger.h:28
TriggerFlags ate_flags
Definition: trigger.c:3403
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:5763
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:113
#define TRIGGER_TYPE_MATCHES(type, level, timing, event)
Definition: pg_trigger.h:146
Oid tgoid
Definition: reltrigger.h:25
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
Tuplestorestate * old_tuplestore
Definition: trigger.c:3598
Form_pg_class rd_rel
Definition: rel.h:114
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:102
HeapTuple tcs_original_insert_tuple
Definition: trigger.h:82
void pfree(void *pointer)
Definition: mcxt.c:949
struct AfterTriggersTableData * tcs_private
Definition: trigger.h:87
#define ERROR
Definition: elog.h:43
#define TRIGGER_TYPE_AFTER
Definition: pg_trigger.h:112
TupleConversionMap * tcs_map
Definition: trigger.h:73
int16 tgtype
Definition: reltrigger.h:29
bool tgdeferrable
Definition: reltrigger.h:35
ItemPointerData t_self
Definition: htup.h:65
bool tginitdeferred
Definition: reltrigger.h:36
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition: tuplestore.c:730
bool trig_delete_after_row
Definition: reltrigger.h:66
Trigger * triggers
Definition: reltrigger.h:48
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
bool trig_insert_after_row
Definition: reltrigger.h:56
#define TRIGGER_TYPE_ROW
Definition: pg_trigger.h:98
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3384
Tuplestorestate * new_tuplestore
Definition: trigger.c:3599
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3385
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:367
bool trig_update_after_row
Definition: reltrigger.h:61
int numtriggers
Definition: reltrigger.h:49
bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, HeapTuple old_row, HeapTuple new_row)
Definition: ri_triggers.c:1718
#define TRIGGER_TYPE_TRUNCATE
Definition: pg_trigger.h:103
char * tgnewtable
Definition: reltrigger.h:43
#define TRIGGER_TYPE_UPDATE
Definition: pg_trigger.h:102
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3396
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, HeapTuple oldtup, HeapTuple newtup)
Definition: trigger.c:3151
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4944
#define RI_TRIGGER_FK
Definition: trigger.h:267
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:505
#define Assert(condition)
Definition: c.h:670
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3708
#define TRIGGER_EVENT_TRUNCATE
Definition: trigger.h:101
Oid tgconstrindid
Definition: reltrigger.h:33
bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, HeapTuple old_row, HeapTuple new_row)
Definition: ri_triggers.c:1661
#define TRIGGER_TYPE_INSERT
Definition: pg_trigger.h:100
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3383
CommandId ats_firing_id
Definition: trigger.c:3395
HeapTuple do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
Definition: tupconvert.c:354
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:100
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:98
#define RI_TRIGGER_PK
Definition: trigger.h:266
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
int i
#define elog
Definition: elog.h:219
#define TRIGGER_TYPE_STATEMENT
Definition: pg_trigger.h:107
ItemPointerData ate_ctid1
Definition: trigger.c:3404
#define RI_TRIGGER_NONE
Definition: trigger.h:268
char * tgoldtable
Definition: reltrigger.h:42
AfterTriggerEventList events
Definition: trigger.c:3575
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:122
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
#define RelationGetRelid(relation)
Definition: rel.h:425
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:139

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5066 of file trigger.c.

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

Referenced by standard_ProcessUtility().

5067 {
5068  int my_level = GetCurrentTransactionNestLevel();
5069 
5070  /* If we haven't already done so, initialize our state. */
5071  if (afterTriggers.state == NULL)
5073 
5074  /*
5075  * If in a subtransaction, and we didn't save the current state already,
5076  * save it so it can be restored if the subtransaction aborts.
5077  */
5078  if (my_level > 1 &&
5079  afterTriggers.trans_stack[my_level].state == NULL)
5080  {
5081  afterTriggers.trans_stack[my_level].state =
5083  }
5084 
5085  /*
5086  * Handle SET CONSTRAINTS ALL ...
5087  */
5088  if (stmt->constraints == NIL)
5089  {
5090  /*
5091  * Forget any previous SET CONSTRAINTS commands in this transaction.
5092  */
5094 
5095  /*
5096  * Set the per-transaction ALL state to known.
5097  */
5098  afterTriggers.state->all_isset = true;
5100  }
5101  else
5102  {
5103  Relation conrel;
5104  Relation tgrel;
5105  List *conoidlist = NIL;
5106  List *tgoidlist = NIL;
5107  ListCell *lc;
5108 
5109  /*
5110  * Handle SET CONSTRAINTS constraint-name [, ...]
5111  *
5112  * First, identify all the named constraints and make a list of their
5113  * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5114  * the same name within a schema, the specifications are not
5115  * necessarily unique. Our strategy is to target all matching
5116  * constraints within the first search-path schema that has any
5117  * matches, but disregard matches in schemas beyond the first match.
5118  * (This is a bit odd but it's the historical behavior.)
5119  */
5121 
5122  foreach(lc, stmt->constraints)
5123  {
5124  RangeVar *constraint = lfirst(lc);
5125  bool found;
5126  List *namespacelist;
5127  ListCell *nslc;
5128 
5129  if (constraint->catalogname)
5130  {
5131  if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5132  ereport(ERROR,
5133  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5134  errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5135  constraint->catalogname, constraint->schemaname,
5136  constraint->relname)));
5137  }
5138 
5139  /*
5140  * If we're given the schema name with the constraint, look only
5141  * in that schema. If given a bare constraint name, use the
5142  * search path to find the first matching constraint.
5143  */
5144  if (constraint->schemaname)
5145  {
5146  Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5147  false);
5148 
5149  namespacelist = list_make1_oid(namespaceId);
5150  }
5151  else
5152  {
5153  namespacelist = fetch_search_path(true);
5154  }
5155 
5156  found = false;
5157  foreach(nslc, namespacelist)
5158  {
5159  Oid namespaceId = lfirst_oid(nslc);
5160  SysScanDesc conscan;
5161  ScanKeyData skey[2];
5162  HeapTuple tup;
5163 
5164  ScanKeyInit(&skey[0],
5166  BTEqualStrategyNumber, F_NAMEEQ,
5167  CStringGetDatum(constraint->relname));
5168  ScanKeyInit(&skey[1],
5170  BTEqualStrategyNumber, F_OIDEQ,
5171  ObjectIdGetDatum(namespaceId));
5172 
5173  conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5174  true, NULL, 2, skey);
5175 
5176  while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5177  {
5179 
5180  if (con->condeferrable)
5181  conoidlist = lappend_oid(conoidlist,
5182  HeapTupleGetOid(tup));
5183  else if (stmt->deferred)
5184  ereport(ERROR,
5185  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5186  errmsg("constraint \"%s\" is not deferrable",
5187  constraint->relname)));
5188  found = true;
5189  }
5190 
5191  systable_endscan(conscan);
5192 
5193  /*
5194  * Once we've found a matching constraint we do not search
5195  * later parts of the search path.
5196  */
5197  if (found)
5198  break;
5199  }
5200 
5201  list_free(namespacelist);
5202 
5203  /*
5204  * Not found ?
5205  */
5206  if (!found)
5207  ereport(ERROR,
5208  (errcode(ERRCODE_UNDEFINED_OBJECT),
5209  errmsg("constraint \"%s\" does not exist",
5210  constraint->relname)));
5211  }
5212 
5213  heap_close(conrel, AccessShareLock);
5214 
5215  /*
5216  * Now, locate the trigger(s) implementing each of these constraints,
5217  * and make a list of their OIDs.
5218  */
5220 
5221  foreach(lc, conoidlist)
5222  {
5223  Oid conoid = lfirst_oid(lc);
5224  bool found;
5225  ScanKeyData skey;
5226  SysScanDesc tgscan;
5227  HeapTuple htup;
5228 
5229  found = false;
5230 
5231  ScanKeyInit(&skey,
5233  BTEqualStrategyNumber, F_OIDEQ,
5234  ObjectIdGetDatum(conoid));
5235 
5236  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5237  NULL, 1, &skey);
5238 
5239  while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5240  {
5241  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5242 
5243  /*
5244  * Silently skip triggers that are marked as non-deferrable in
5245  * pg_trigger. This is not an error condition, since a
5246  * deferrable RI constraint may have some non-deferrable
5247  * actions.
5248  */
5249  if (pg_trigger->tgdeferrable)
5250  tgoidlist = lappend_oid(tgoidlist,
5251  HeapTupleGetOid(htup));
5252 
5253  found = true;
5254  }
5255 
5256  systable_endscan(tgscan);
5257 
5258  /* Safety check: a deferrable constraint should have triggers */
5259  if (!found)
5260  elog(ERROR, "no triggers found for constraint with OID %u",
5261  conoid);
5262  }
5263 
5264  heap_close(tgrel, AccessShareLock);
5265 
5266  /*
5267  * Now we can set the trigger states of individual triggers for this
5268  * xact.
5269  */
5270  foreach(lc, tgoidlist)
5271  {
5272  Oid tgoid = lfirst_oid(lc);
5274  bool found = false;
5275  int i;
5276 
5277  for (i = 0; i < state->numstates; i++)
5278  {
5279  if (state->trigstates[i].sct_tgoid == tgoid)
5280  {
5281  state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5282  found = true;
5283  break;
5284  }
5285  }
5286  if (!found)
5287  {
5289  SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5290  }
5291  }
5292  }
5293 
5294  /*
5295  * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5296  * checks against that constraint must be made when the SET CONSTRAINTS
5297  * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5298  * apply retroactively. We've updated the constraints state, so scan the
5299  * list of previously deferred events to fire any that have now become
5300  * immediate.
5301  *
5302  * Obviously, if this was SET ... DEFERRED then it can't have converted
5303  * any unfired events to immediate, so we need do nothing in that case.
5304  */
5305  if (!stmt->deferred)
5306  {
5308  bool snapshot_set = false;
5309 
5310  while (afterTriggerMarkEvents(events, NULL, true))
5311  {
5312  CommandId firing_id = afterTriggers.firing_counter++;
5313 
5314  /*
5315  * Make sure a snapshot has been established in case trigger
5316  * functions need one. Note that we avoid setting a snapshot if
5317  * we don't find at least one trigger that has to be fired now.
5318  * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
5319  * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
5320  * at the start of a transaction it's not possible for any trigger
5321  * events to be queued yet.)
5322  */
5323  if (!snapshot_set)
5324  {
5326  snapshot_set = true;
5327  }
5328 
5329  /*
5330  * We can delete fired events if we are at top transaction level,
5331  * but we'd better not if inside a subtransaction, since the
5332  * subtransaction could later get rolled back.
5333  */
5334  if (afterTriggerInvokeEvents(events, firing_id, NULL,
5335  !IsSubTransaction()))
5336  break; /* all fired */
5337  }
5338 
5339  if (snapshot_set)
5341  }
5342 }
#define NIL
Definition: pg_list.h:69
uint32 CommandId
Definition: c.h:459
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2853
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
#define GETSTRUCT(TUP)
Definition: htup_details.h:661
#define Anum_pg_trigger_tgconstraint
Definition: pg_trigger.h:87
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3333
#define AccessShareLock
Definition: lockdefs.h:36
int errcode(int sqlerrcode)
Definition: elog.c:575
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition: trigger.c:5036
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4205
void PopActiveSnapshot(void)
Definition: snapmgr.c:812
#define heap_close(r, l)
Definition: heapam.h:97
#define Anum_pg_constraint_conname
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:164
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:304
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:328
char * schemaname
Definition: primnodes.h:67
char * relname
Definition: primnodes.h:68
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
SetConstraintState state
Definition: trigger.c:3559
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2056
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:733
#define CStringGetDatum(X)
Definition: postgres.h:584
#define TriggerConstraintIndexId
Definition: indexing.h:247
#define ereport(elevel, rest)
Definition: elog.h:122
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:4991
#define list_make1_oid(x1)
Definition: pg_list.h:151
Oid MyDatabaseId
Definition: globals.c:77
#define Anum_pg_constraint_connamespace
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
CommandId firing_counter
Definition: trigger.c:3558
#define ConstraintNameNspIndexId
Definition: indexing.h:124
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:754
FormData_pg_constraint * Form_pg_constraint
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define lfirst(lc)
Definition: pg_list.h:106
Definition: regguts.h:298
#define TriggerRelationId
Definition: pg_trigger.h:34
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:72
bool IsSubTransaction(void)
Definition: xact.c:4520
int errmsg(const char *fmt,...)
Definition: elog.c:797
void list_free(List *list)
Definition: list.c:1133
int i
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4133
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
AfterTriggerEventList events
Definition: trigger.c:3560
#define ConstraintRelationId
Definition: pg_constraint.h:29
#define elog
Definition: elog.h:219
#define HeapTupleGetOid(tuple)
Definition: htup_details.h:700
Definition: pg_list.h:45
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4168
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
char * catalogname
Definition: primnodes.h:66
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define lfirst_oid(lc)
Definition: pg_list.h:108
SetConstraintState state
Definition: trigger.c:3583
static SetConstraintState SetConstraintStateCopy(SetConstraintState state)
Definition: trigger.c:5016

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 5717 of file trigger.c.

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

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

5718 {
5719  bool result;
5720  AfterTriggersTableData *table;
5721 
5722  /* Check state, like AfterTriggerSaveEvent. */
5723  if (afterTriggers.query_depth < 0)
5724  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
5725 
5726  /* Be sure we have enough space to record events at this query depth. */
5729 
5730  /*
5731  * We keep this state in the AfterTriggersTableData that also holds
5732  * transition tables for the relation + operation. In this way, if we are
5733  * forced to make a new set of transition tables because more tuples get
5734  * entered after we've already fired triggers, we will allow a new set of
5735  * statement triggers to get queued.
5736  */
5737  table = GetAfterTriggersTableData(relid, cmdType);
5738  result = table->before_trig_done;
5739  table->before_trig_done = true;
5740  return result;
5741 }
#define ERROR
Definition: elog.h:43
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4346
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4944
#define elog
Definition: elog.h:219
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ cancel_prior_stmt_triggers()

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

Definition at line 5763 of file trigger.c.

References AfterTriggersTableData::after_trig_done, AfterTriggersTableData::after_trig_events, AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_relid, CHUNK_DATA_START, AfterTriggersQueryData::events, for_each_chunk_from, for_each_event_from, GetAfterTriggersTableData(), GetTriggerSharedData, AfterTriggerEventList::head, AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, TRIGGER_EVENT_OPMASK, TRIGGER_FIRED_AFTER, and TRIGGER_FIRED_FOR_STATEMENT.

Referenced by AfterTriggerSaveEvent().

5764 {
5765  AfterTriggersTableData *table;
5767 
5768  /*
5769  * We keep this state in the AfterTriggersTableData that also holds
5770  * transition tables for the relation + operation. In this way, if we are
5771  * forced to make a new set of transition tables because more tuples get
5772  * entered after we've already fired triggers, we will allow a new set of
5773  * statement triggers to get queued without canceling the old ones.
5774  */
5775  table = GetAfterTriggersTableData(relid, cmdType);
5776 
5777  if (table->after_trig_done)
5778  {
5779  /*
5780  * We want to start scanning from the tail location that existed just
5781  * before we inserted any statement triggers. But the events list
5782  * might've been entirely empty then, in which case scan from the
5783  * current head.
5784  */
5785  AfterTriggerEvent event;
5786  AfterTriggerEventChunk *chunk;
5787 
5788  if (table->after_trig_events.tail)
5789  {
5790  chunk = table->after_trig_events.tail;
5791  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
5792  }
5793  else
5794  {
5795  chunk = qs->events.head;
5796  event = NULL;
5797  }
5798 
5799  for_each_chunk_from(chunk)
5800  {
5801  if (event == NULL)
5802  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
5803  for_each_event_from(event, chunk)
5804  {
5805  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5806 
5807  /*
5808  * Exit loop when we reach events that aren't AS triggers for
5809  * the target relation.
5810  */
5811  if (evtshared->ats_relid != relid)
5812  goto done;
5813  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
5814  goto done;
5815  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
5816  goto done;
5817  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
5818  goto done;
5819  /* OK, mark it DONE */
5820  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
5821  event->ate_flags |= AFTER_TRIGGER_DONE;
5822  }
5823  /* signal we must reinitialize event ptr for next chunk */
5824  event = NULL;
5825  }
5826  }
5827 done:
5828 
5829  /* In any case, save current insertion point for next time */
5830  table->after_trig_done = true;
5831  table->after_trig_events = qs->events;
5832 }
TriggerEvent ats_event
Definition: trigger.c:3392
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3447
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:137
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:102
#define TRIGGER_FIRED_FOR_STATEMENT(event)
Definition: trigger.h:131
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4346
AfterTriggerEventList after_trig_events
Definition: trigger.c:3597
#define for_each_chunk_from(cptr)
Definition: trigger.c:3469
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3471
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3399
AfterTriggerEventChunk * head
Definition: trigger.c:3452
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3380
AfterTriggerEventList events
Definition: trigger.c:3575
static AfterTriggersData afterTriggers
Definition: trigger.c:3602

◆ ConvertTriggerToFK()

static void ConvertTriggerToFK ( CreateTrigStmt stmt,
Oid  funcoid 
)
static

Definition at line 1019 of file trigger.c.

References _, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), arg, OldTriggerInfo::args, CreateTrigStmt::args, AT_AddConstraint, buf, PlannedStmt::canSetTag, CMD_UTILITY, AlterTableStmt::cmds, PlannedStmt::commandType, Constraint::conname, CONSTR_FOREIGN, CreateTrigStmt::constrrel, Constraint::contype, copyObject, StringInfoData::data, AlterTableCmd::def, Constraint::deferrable, CreateTrigStmt::deferrable, elog, equal(), ereport, errdetail_internal(), errmsg(), ERROR, Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_matchtype, Constraint::fk_upd_action, FKCONSTR_ACTION_CASCADE, FKCONSTR_ACTION_NOACTION, FKCONSTR_ACTION_RESTRICT, FKCONSTR_ACTION_SETDEFAULT, FKCONSTR_ACTION_SETNULL, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_SIMPLE, OldTriggerInfo::funcoids, gettext_noop, i, Constraint::initdeferred, CreateTrigStmt::initdeferred, Constraint::initially_valid, initStringInfo(), InvalidOid, lappend(), lfirst, linitial, list_delete_ptr(), list_make1, Constraint::location, lsecond, lthird, makeNode, makeRangeVar(), MemoryContextSwitchTo(), NIL, None_Receiver, NOTICE, OBJECT_TABLE, palloc0(), pfree(), Constraint::pk_attrs, Constraint::pktable, PROCESS_UTILITY_SUBCOMMAND, ProcessUtility(), quote_identifier(), AlterTableStmt::relation, CreateTrigStmt::relation, AlterTableStmt::relkind, Constraint::skip_validation, PlannedStmt::stmt_len, PlannedStmt::stmt_location, strVal, AlterTableCmd::subtype, TopMemoryContext, and PlannedStmt::utilityStmt.

Referenced by CreateTrigger().

1020 {
1021  static List *info_list = NIL;
1022 
1023  static const char *const funcdescr[3] = {
1024  gettext_noop("Found referenced table's UPDATE trigger."),
1025  gettext_noop("Found referenced table's DELETE trigger."),
1026  gettext_noop("Found referencing table's trigger.")
1027  };
1028 
1029  char *constr_name;
1030  char *fk_table_name;
1031  char *pk_table_name;
1032  char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1033  List *fk_attrs = NIL;
1034  List *pk_attrs = NIL;
1036  int funcnum;
1037  OldTriggerInfo *info = NULL;
1038  ListCell *l;
1039  int i;
1040 
1041  /* Parse out the trigger arguments */
1042  constr_name = strVal(linitial(stmt->args));
1043  fk_table_name = strVal(lsecond(stmt->args));
1044  pk_table_name = strVal(lthird(stmt->args));
1045  i = 0;
1046  foreach(l, stmt->args)
1047  {
1048  Value *arg = (Value *) lfirst(l);
1049 
1050  i++;
1051  if (i < 4) /* skip constraint and table names */
1052  continue;
1053  if (i == 4) /* handle match type */
1054  {
1055  if (strcmp(strVal(arg), "FULL") == 0)
1056  fk_matchtype = FKCONSTR_MATCH_FULL;
1057  else
1058  fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1059  continue;
1060  }
1061  if (i % 2)
1062  fk_attrs = lappend(fk_attrs, arg);
1063  else
1064  pk_attrs = lappend(pk_attrs, arg);
1065  }
1066 
1067  /* Prepare description of constraint for use in messages */
1068  initStringInfo(&buf);
1069  appendStringInfo(&buf, "FOREIGN KEY %s(",
1070  quote_identifier(fk_table_name));
1071  i = 0;
1072  foreach(l, fk_attrs)
1073  {
1074  Value *arg = (Value *) lfirst(l);
1075 
1076  if (i++ > 0)
1077  appendStringInfoChar(&buf, ',');
1079  }
1080  appendStringInfo(&buf, ") REFERENCES %s(",
1081  quote_identifier(pk_table_name));
1082  i = 0;
1083  foreach(l, pk_attrs)
1084  {
1085  Value *arg = (Value *) lfirst(l);
1086 
1087  if (i++ > 0)
1088  appendStringInfoChar(&buf, ',');
1090  }
1091  appendStringInfoChar(&buf, ')');
1092 
1093  /* Identify class of trigger --- update, delete, or referencing-table */
1094  switch (funcoid)
1095  {
1096  case F_RI_FKEY_CASCADE_UPD:
1097  case F_RI_FKEY_RESTRICT_UPD:
1098  case F_RI_FKEY_SETNULL_UPD:
1099  case F_RI_FKEY_SETDEFAULT_UPD:
1100  case F_RI_FKEY_NOACTION_UPD:
1101  funcnum = 0;
1102  break;
1103 
1104  case F_RI_FKEY_CASCADE_DEL:
1105  case F_RI_FKEY_RESTRICT_DEL:
1106  case F_RI_FKEY_SETNULL_DEL:
1107  case F_RI_FKEY_SETDEFAULT_DEL:
1108  case F_RI_FKEY_NOACTION_DEL:
1109  funcnum = 1;
1110  break;
1111 
1112  default:
1113  funcnum = 2;
1114  break;
1115  }
1116 
1117  /* See if we have a match to this trigger */
1118  foreach(l, info_list)
1119  {
1120  info = (OldTriggerInfo *) lfirst(l);
1121  if (info->funcoids[funcnum] == InvalidOid &&
1122  equal(info->args, stmt->args))
1123  {
1124  info->funcoids[funcnum] = funcoid;
1125  break;
1126  }
1127  }
1128 
1129  if (l == NULL)
1130  {
1131  /* First trigger of set, so create a new list entry */
1132  MemoryContext oldContext;
1133 
1134  ereport(NOTICE,
1135  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1136  constr_name, buf.data),
1137  errdetail_internal("%s", _(funcdescr[funcnum]))));
1139  info = (OldTriggerInfo *) palloc0(sizeof(OldTriggerInfo));
1140  info->args = copyObject(stmt->args);
1141  info->funcoids[funcnum] = funcoid;
1142  info_list = lappend(info_list, info);
1143  MemoryContextSwitchTo(oldContext);
1144  }
1145  else if (info->funcoids[0] == InvalidOid ||
1146  info->funcoids[1] == InvalidOid ||
1147  info->funcoids[2] == InvalidOid)
1148  {
1149  /* Second trigger of set */
1150  ereport(NOTICE,
1151  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1152  constr_name, buf.data),
1153  errdetail_internal("%s", _(funcdescr[funcnum]))));
1154  }
1155  else
1156  {
1157  /* OK, we have a set, so make the FK constraint ALTER TABLE cmd */
1160  Constraint *fkcon = makeNode(Constraint);
1161  PlannedStmt *wrapper = makeNode(PlannedStmt);
1162 
1163  ereport(NOTICE,
1164  (errmsg("converting trigger group into constraint \"%s\" %s",
1165  constr_name, buf.data),
1166  errdetail_internal("%s", _(funcdescr[funcnum]))));
1167  fkcon->contype = CONSTR_FOREIGN;
1168  fkcon->location = -1;
1169  if (funcnum == 2)
1170  {
1171  /* This trigger is on the FK table */
1172  atstmt->relation = stmt->relation;
1173  if (stmt->constrrel)
1174  fkcon->pktable = stmt->constrrel;
1175  else
1176  {
1177  /* Work around ancient pg_dump bug that omitted constrrel */
1178  fkcon->pktable = makeRangeVar(NULL, pk_table_name, -1);
1179  }
1180  }
1181  else
1182  {
1183  /* This trigger is on the PK table */
1184  fkcon->pktable = stmt->relation;
1185  if (stmt->constrrel)
1186  atstmt->relation = stmt->constrrel;
1187  else
1188  {
1189  /* Work around ancient pg_dump bug that omitted constrrel */
1190  atstmt->relation = makeRangeVar(NULL, fk_table_name, -1);
1191  }
1192  }
1193  atstmt->cmds = list_make1(atcmd);
1194  atstmt->relkind = OBJECT_TABLE;
1195  atcmd->subtype = AT_AddConstraint;
1196  atcmd->def = (Node *) fkcon;
1197  if (strcmp(constr_name, "<unnamed>") == 0)
1198  fkcon->conname = NULL;
1199  else
1200  fkcon->conname = constr_name;
1201  fkcon->fk_attrs = fk_attrs;
1202  fkcon->pk_attrs = pk_attrs;
1203  fkcon->fk_matchtype = fk_matchtype;
1204  switch (info->funcoids[0])
1205  {
1206  case F_RI_FKEY_NOACTION_UPD:
1208  break;
1209  case F_RI_FKEY_CASCADE_UPD:
1211  break;
1212  case F_RI_FKEY_RESTRICT_UPD:
1214  break;
1215  case F_RI_FKEY_SETNULL_UPD:
1217  break;
1218  case F_RI_FKEY_SETDEFAULT_UPD:
1220  break;
1221  default:
1222  /* can't get here because of earlier checks */
1223  elog(ERROR, "confused about RI update function");
1224  }
1225  switch (info->funcoids[1])
1226  {
1227  case F_RI_FKEY_NOACTION_DEL:
1229  break;
1230  case F_RI_FKEY_CASCADE_DEL:
1232  break;
1233  case F_RI_FKEY_RESTRICT_DEL:
1235  break;
1236  case F_RI_FKEY_SETNULL_DEL:
1238  break;
1239  case F_RI_FKEY_SETDEFAULT_DEL:
1241  break;
1242  default:
1243  /* can't get here because of earlier checks */
1244  elog(ERROR, "confused about RI delete function");
1245  }
1246  fkcon->deferrable = stmt->deferrable;
1247  fkcon->initdeferred = stmt->initdeferred;
1248  fkcon->skip_validation = false;
1249  fkcon->initially_valid = true;
1250 
1251  /* finally, wrap it in a dummy PlannedStmt */
1252  wrapper->commandType = CMD_UTILITY;
1253  wrapper->canSetTag = false;
1254  wrapper->utilityStmt = (Node *) atstmt;
1255  wrapper->stmt_location = -1;
1256  wrapper->stmt_len = -1;
1257 
1258  /* ... and execute it */
1259  ProcessUtility(wrapper,
1260  "(generated ALTER TABLE ADD FOREIGN KEY command)",
1261  PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1262  None_Receiver, NULL);
1263 
1264  /* Remove the matched item from the list */
1265  info_list = list_delete_ptr(info_list, info);
1266  pfree(info);
1267  /* We leak the copied args ... not worth worrying about */
1268  }
1269 }
#define NIL
Definition: pg_list.h:69
#define FKCONSTR_MATCH_SIMPLE
Definition: parsenodes.h:2087
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:10412
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2984
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2078
char fk_matchtype
Definition: parsenodes.h:2124
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2082
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define gettext_noop(x)
Definition: c.h:981
Definition: nodes.h:512
#define strVal(v)
Definition: value.h:54
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, char *completionTag)
Definition: utility.c:335
bool initdeferred
Definition: parsenodes.h:2097
AlterTableType subtype
Definition: parsenodes.h:1787
List * pk_attrs
Definition: parsenodes.h:2123
char * conname
Definition: parsenodes.h:2095
List * list_delete_ptr(List *list, void *datum)
Definition: list.c:590
DestReceiver * None_Receiver
Definition: dest.c:91
int stmt_len
Definition: plannodes.h:98
#define lsecond(l)
Definition: pg_list.h:116
int errdetail_internal(const char *fmt,...)
Definition: elog.c:900
#define list_make1(x1)
Definition: pg_list.h:139
RangeVar * constrrel
Definition: parsenodes.h:2382
void pfree(void *pointer)
Definition: mcxt.c:949
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define linitial(l)
Definition: pg_list.h:111
#define ERROR
Definition: elog.h:43
bool deferrable
Definition: parsenodes.h:2096
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:157
int stmt_location
Definition: plannodes.h:97
static char * buf
Definition: pg_test_fsync.c:67
Node * utilityStmt
Definition: plannodes.h:94
Oid funcoids[3]
Definition: trigger.c:1014
#define ereport(elevel, rest)
Definition: elog.h:122
ObjectType relkind
Definition: parsenodes.h:1701
MemoryContext TopMemoryContext
Definition: mcxt.c:43
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2080
List * lappend(List *list, void *datum)
Definition: list.c:128
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:169
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
#define FKCONSTR_MATCH_FULL
Definition: parsenodes.h:2085
bool canSetTag
Definition: plannodes.h:53
void * palloc0(Size size)
Definition: mcxt.c:877
CmdType commandType
Definition: plannodes.h:45
#define InvalidOid
Definition: postgres_ext.h:36
bool initially_valid
Definition: parsenodes.h:2133
#define NOTICE
Definition: elog.h:37
#define makeNode(_type_)
Definition: nodes.h:560
char fk_del_action
Definition: parsenodes.h:2126
#define lfirst(lc)
Definition: pg_list.h:106
Definition: value.h:42
List * args
Definition: trigger.c:1013
int errmsg(const char *fmt,...)
Definition: elog.c:797
RangeVar * relation
Definition: parsenodes.h:1699
int i
RangeVar * relation
Definition: parsenodes.h:2366
ConstrType contype
Definition: parsenodes.h:2092
void * arg
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2079
#define lthird(l)
Definition: pg_list.h:121
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:625
RangeVar * pktable
Definition: parsenodes.h:2121
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2081
Definition: pg_list.h:45
bool skip_validation
Definition: parsenodes.h:2132
#define _(x)
Definition: elog.c:84
List * fk_attrs
Definition: parsenodes.h:2122
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:421
char fk_upd_action
Definition: parsenodes.h:2125

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

Definition at line 1911 of file trigger.c.

References i, TriggerDesc::numtriggers, palloc(), pstrdup(), Trigger::tgargs, Trigger::tgattr, Trigger::tgname, Trigger::tgnargs, Trigger::tgnattr, Trigger::tgnewtable, Trigger::tgoldtable, Trigger::tgqual, and TriggerDesc::triggers.

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

1912 {
1913  TriggerDesc *newdesc;
1914  Trigger *trigger;
1915  int i;
1916 
1917  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
1918  return NULL;
1919 
1920  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
1921  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
1922 
1923  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
1924  memcpy(trigger, trigdesc->triggers,
1925  trigdesc->numtriggers * sizeof(Trigger));
1926  newdesc->triggers = trigger;
1927 
1928  for (i = 0; i < trigdesc->numtriggers; i++)
1929  {
1930  trigger->tgname = pstrdup(trigger->tgname);
1931  if (trigger->tgnattr > 0)
1932  {
1933  int16 *newattr;
1934 
1935  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
1936  memcpy(newattr, trigger->tgattr,
1937  trigger->tgnattr * sizeof(int16));
1938  trigger->tgattr = newattr;
1939  }
1940  if (trigger->tgnargs > 0)
1941  {
1942  char **newargs;
1943  int16 j;
1944 
1945  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
1946  for (j = 0; j < trigger->tgnargs; j++)
1947  newargs[j] = pstrdup(trigger->tgargs[j]);
1948  trigger->tgargs = newargs;
1949  }
1950  if (trigger->tgqual)
1951  trigger->tgqual = pstrdup(trigger->tgqual);
1952  if (trigger->tgoldtable)
1953  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
1954  if (trigger->tgnewtable)
1955  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
1956  trigger++;
1957  }
1958 
1959  return newdesc;
1960 }
signed short int16
Definition: c.h:283
char * pstrdup(const char *in)
Definition: mcxt.c:1076
char * tgqual
Definition: reltrigger.h:41
char * tgname
Definition: reltrigger.h:27
Trigger * triggers
Definition: reltrigger.h:48
int numtriggers
Definition: reltrigger.h:49
char ** tgargs
Definition: reltrigger.h:40
int16 * tgattr
Definition: reltrigger.h:39
char * tgnewtable
Definition: reltrigger.h:43
int16 tgnattr
Definition: reltrigger.h:38
void * palloc(Size size)
Definition: mcxt.c:848
int i
int16 tgnargs
Definition: reltrigger.h:37
char * tgoldtable
Definition: reltrigger.h:42

◆ CreateTrigger()

ObjectAddress CreateTrigger ( CreateTrigStmt stmt,
const char *  queryString,
Oid  relOid,
Oid  refRelOid,
Oid  constraintOid,
Oid  indexOid,
bool  isInternal 
)

Definition at line 141 of file trigger.c.

References AccessShareLock, ACL_EXECUTE, ACL_KIND_CLASS, ACL_KIND_PROC, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addRangeTableEntryForRelation(), addRTEtoQuery(), allowSystemTableMods, Anum_pg_trigger_tgargs, Anum_pg_trigger_tgattr, Anum_pg_trigger_tgconstraint, Anum_pg_trigger_tgconstrindid, Anum_pg_trigger_tgconstrrelid, Anum_pg_trigger_tgdeferrable, Anum_pg_trigger_tgenabled, Anum_pg_trigger_tgfoid, Anum_pg_trigger_tginitdeferred, Anum_pg_trigger_tgisinternal, Anum_pg_trigger_tgname, Anum_pg_trigger_tgnargs, Anum_pg_trigger_tgnewtable, Anum_pg_trigger_tgoldtable, Anum_pg_trigger_tgqual, Anum_pg_trigger_tgrelid, Anum_pg_trigger_tgtype, generate_unaccent_rules::args, CreateTrigStmt::args, Assert, assign_expr_collations(), attnameAttNum(), BoolGetDatum, BTEqualStrategyNumber, buildint2vector(), byteain(), CatalogTupleInsert(), CatalogTupleUpdate(), CharGetDatum, ObjectAddress::classId, CreateTrigStmt::columns, CONSTRAINT_TRIGGER, ConstraintRelationId, CreateTrigStmt::constrrel, ConvertTriggerToFK(), copyObject, CreateConstraintEntry(), CStringGetDatum, CStringGetTextDatum, DatumGetPointer, CreateTrigStmt::deferrable, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, DirectFunctionCall1, elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errhint(), errmsg(), ERROR, CreateTrigStmt::events, EXPR_KIND_TRIGGER_WHEN, free_parsestate(), CreateTrigStmt::funcname, get_func_rettype(), get_rel_name(), GetNewOid(), GETSTRUCT, GetUserId(), has_superclass(), heap_close, heap_form_tuple(), heap_freetuple(), heap_open(), heap_openrv(), HeapTupleIsValid, HeapTupleSetOid, i, CreateTrigStmt::initdeferred, Int16GetDatum, InvalidAttrNumber, InvalidObjectAddress, InvalidOid, InvokeObjectPostCreateHookArg, CreateTrigStmt::isconstraint, TriggerTransition::isNew, IsSystemRelation(), TriggerTransition::isTable, lfirst, lfirst_node, list_length(), Var::location, LockRelationOid(), LookupFuncName(), make_parsestate(), makeAlias(), name, TriggerTransition::name, NAMEDATALEN, namein(), NameListToString(), namestrcmp(), Natts_pg_trigger, NIL, nodeToString(), NoLock, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, OidIsValid, OPAQUEOID, ParseState::p_rtable, ParseState::p_sourcetext, palloc(), parser_errposition(), pfree(), pg_class_aclcheck(), pg_proc_aclcheck(), PointerGetDatum, ProcedureRelationId, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pull_var_clause(), RangeVarGetRelid, RelationData::rd_att, RelationData::rd_id, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnExpr(), CreateTrigStmt::relation, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RelationRelationId, RELKIND_FOREIGN_TABLE, RELKIND_PARTITIONED_TABLE, RELKIND_RELATION, RELKIND_VIEW, RELOID, RI_FKey_trigger_type(), RI_TRIGGER_NONE, CreateTrigStmt::row, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, SetFunctionReturnType(), ShareRowExclusiveLock, snprintf(), strVal, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, CreateTrigStmt::timing, transformWhereClause(), CreateTrigStmt::transitionRels, TRIGGER_CLEAR_TYPE, TRIGGER_FIRES_ON_ORIGIN, TRIGGER_FOR_BEFORE, TRIGGER_FOR_DELETE, TRIGGER_FOR_INSERT, TRIGGER_FOR_INSTEAD, TRIGGER_FOR_ROW, TRIGGER_FOR_TRUNCATE, TRIGGER_FOR_UPDATE, TRIGGER_SETT_ROW, TRIGGER_TYPE_AFTER, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSTEAD, TRIGGEROID, TriggerRelationId, TriggerRelidNameIndexId, CreateTrigStmt::trigname, values, Var::varattno, Var::varno, WARNING, and CreateTrigStmt::whenClause.

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

144 {
145  int16 tgtype;
146  int ncolumns;
147  int16 *columns;
148  int2vector *tgattr;
149  Node *whenClause;
150  List *whenRtable;
151  char *qual;
153  bool nulls[Natts_pg_trigger];
154  Relation rel;
155  AclResult aclresult;
156  Relation tgrel;
157  SysScanDesc tgscan;
158  ScanKeyData key;
159  Relation pgrel;
160  HeapTuple tuple;
161  Oid fargtypes[1]; /* dummy */
162  Oid funcoid;
163  Oid funcrettype;
164  Oid trigoid;
165  char internaltrigname[NAMEDATALEN];
166  char *trigname;
167  Oid constrrelid = InvalidOid;
168  ObjectAddress myself,
169  referenced;
170  char *oldtablename = NULL;
171  char *newtablename = NULL;
172 
173  if (OidIsValid(relOid))
174  rel = heap_open(relOid, ShareRowExclusiveLock);
175  else
177 
178  /*
179  * Triggers must be on tables or views, and there are additional
180  * relation-type-specific restrictions.
181  */
182  if (rel->rd_rel->relkind == RELKIND_RELATION ||
183  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
184  {
185  /* Tables can't have INSTEAD OF triggers */
186  if (stmt->timing != TRIGGER_TYPE_BEFORE &&
187  stmt->timing != TRIGGER_TYPE_AFTER)
188  ereport(ERROR,
189  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
190  errmsg("\"%s\" is a table",
192  errdetail("Tables cannot have INSTEAD OF triggers.")));
193  /* Disallow ROW triggers on partitioned tables */
194  if (stmt->row && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
195  ereport(ERROR,
196  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
197  errmsg("\"%s\" is a partitioned table",
199  errdetail("Partitioned tables cannot have ROW triggers.")));
200  }
201  else if (rel->rd_rel->relkind == RELKIND_VIEW)
202  {
203  /*
204  * Views can have INSTEAD OF triggers (which we check below are
205  * row-level), or statement-level BEFORE/AFTER triggers.
206  */
207  if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row)
208  ereport(ERROR,
209  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
210  errmsg("\"%s\" is a view",
212  errdetail("Views cannot have row-level BEFORE or AFTER triggers.")));
213  /* Disallow TRUNCATE triggers on VIEWs */
214  if (TRIGGER_FOR_TRUNCATE(stmt->events))
215  ereport(ERROR,
216  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
217  errmsg("\"%s\" is a view",
219  errdetail("Views cannot have TRUNCATE triggers.")));
220  }
221  else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
222  {
223  if (stmt->timing != TRIGGER_TYPE_BEFORE &&
224  stmt->timing != TRIGGER_TYPE_AFTER)
225  ereport(ERROR,
226  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
227  errmsg("\"%s\" is a foreign table",
229  errdetail("Foreign tables cannot have INSTEAD OF triggers.")));
230 
231  if (TRIGGER_FOR_TRUNCATE(stmt->events))
232  ereport(ERROR,
233  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
234  errmsg("\"%s\" is a foreign table",
236  errdetail("Foreign tables cannot have TRUNCATE triggers.")));
237 
238  /*
239  * We disallow constraint triggers to protect the assumption that
240  * triggers on FKs can't be deferred. See notes with AfterTriggers
241  * data structures, below.
242  */
243  if (stmt->isconstraint)
244  ereport(ERROR,
245  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
246  errmsg("\"%s\" is a foreign table",
248  errdetail("Foreign tables cannot have constraint triggers.")));
249  }
250  else
251  ereport(ERROR,
252  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
253  errmsg("\"%s\" is not a table or view",
254  RelationGetRelationName(rel))));
255 
257  ereport(ERROR,
258  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
259  errmsg("permission denied: \"%s\" is a system catalog",
260  RelationGetRelationName(rel))));
261 
262  if (stmt->isconstraint)
263  {
264  /*
265  * We must take a lock on the target relation to protect against
266  * concurrent drop. It's not clear that AccessShareLock is strong
267  * enough, but we certainly need at least that much... otherwise, we
268  * might end up creating a pg_constraint entry referencing a
269  * nonexistent table.
270  */
271  if (OidIsValid(refRelOid))
272  {
273  LockRelationOid(refRelOid, AccessShareLock);
274  constrrelid = refRelOid;
275  }
276  else if (stmt->constrrel != NULL)
277  constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock,
278  false);
279  }
280 
281  /* permission checks */
282  if (!isInternal)
283  {
284  aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
285  ACL_TRIGGER);
286  if (aclresult != ACLCHECK_OK)
287  aclcheck_error(aclresult, ACL_KIND_CLASS,
289 
290  if (OidIsValid(constrrelid))
291  {
292  aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
293  ACL_TRIGGER);
294  if (aclresult != ACLCHECK_OK)
295  aclcheck_error(aclresult, ACL_KIND_CLASS,
296  get_rel_name(constrrelid));
297  }
298  }
299 
300  /* Compute tgtype */
301  TRIGGER_CLEAR_TYPE(tgtype);
302  if (stmt->row)
303  TRIGGER_SETT_ROW(tgtype);
304  tgtype |= stmt->timing;
305  tgtype |= stmt->events;
306 
307  /* Disallow ROW-level TRUNCATE triggers */
308  if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
309  ereport(ERROR,
310  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
311  errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
312 
313  /* INSTEAD triggers must be row-level, and can't have WHEN or columns */
314  if (TRIGGER_FOR_INSTEAD(tgtype))
315  {
316  if (!TRIGGER_FOR_ROW(tgtype))
317  ereport(ERROR,
318  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
319  errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
320  if (stmt->whenClause)
321  ereport(ERROR,
322  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
323  errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
324  if (stmt->columns != NIL)
325  ereport(ERROR,
326  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
327  errmsg("INSTEAD OF triggers cannot have column lists")));
328  }
329 
330  /*
331  * We don't yet support naming ROW transition variables, but the parser
332  * recognizes the syntax so we can give a nicer message here.
333  *
334  * Per standard, REFERENCING TABLE names are only allowed on AFTER
335  * triggers. Per standard, REFERENCING ROW names are not allowed with FOR
336  * EACH STATEMENT. Per standard, each OLD/NEW, ROW/TABLE permutation is
337  * only allowed once. Per standard, OLD may not be specified when
338  * creating a trigger only for INSERT, and NEW may not be specified when
339  * creating a trigger only for DELETE.
340  *
341  * Notice that the standard allows an AFTER ... FOR EACH ROW trigger to
342  * reference both ROW and TABLE transition data.
343  */
344  if (stmt->transitionRels != NIL)
345  {
346  List *varList = stmt->transitionRels;
347  ListCell *lc;
348 
349  foreach(lc, varList)
350  {
352 
353  if (!(tt->isTable))
354  ereport(ERROR,
355  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
356  errmsg("ROW variable naming in the REFERENCING clause is not supported"),
357  errhint("Use OLD TABLE or NEW TABLE for naming transition tables.")));
358 
359  /*
360  * Because of the above test, we omit further ROW-related testing
361  * below. If we later allow naming OLD and NEW ROW variables,
362  * adjustments will be needed below.
363  */
364 
365  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
366  ereport(ERROR,
367  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
368  errmsg("\"%s\" is a foreign table",
370  errdetail("Triggers on foreign tables cannot have transition tables.")));
371 
372  if (rel->rd_rel->relkind == RELKIND_VIEW)
373  ereport(ERROR,
374  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
375  errmsg("\"%s\" is a view",
377  errdetail("Triggers on views cannot have transition tables.")));
378 
379  /*
380  * We currently don't allow row-level triggers with transition
381  * tables on partition or inheritance children. Such triggers
382  * would somehow need to see tuples converted to the format of the
383  * table they're attached to, and it's not clear which subset of
384  * tuples each child should see. See also the prohibitions in
385  * ATExecAttachPartition() and ATExecAddInherit().
386  */
387  if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id))
388  {
389  /* Use appropriate error message. */
390  if (rel->rd_rel->relispartition)
391  ereport(ERROR,
392  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
393  errmsg("ROW triggers with transition tables are not supported on partitions")));
394  else
395  ereport(ERROR,
396  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
397  errmsg("ROW triggers with transition tables are not supported on inheritance children")));
398  }
399 
400  if (stmt->timing != TRIGGER_TYPE_AFTER)
401  ereport(ERROR,
402  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
403  errmsg("transition table name can only be specified for an AFTER trigger")));
404 
405  if (TRIGGER_FOR_TRUNCATE(tgtype))
406  ereport(ERROR,
407  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
408  errmsg("TRUNCATE triggers with transition tables are not supported")));
409 
410  /*
411  * We currently don't allow multi-event triggers ("INSERT OR
412  * UPDATE") with transition tables, because it's not clear how to
413  * handle INSERT ... ON CONFLICT statements which can fire both
414  * INSERT and UPDATE triggers. We show the inserted tuples to
415  * INSERT triggers and the updated tuples to UPDATE triggers, but
416  * it's not yet clear what INSERT OR UPDATE trigger should see.
417  * This restriction could be lifted if we can decide on the right
418  * semantics in a later release.
419  */
420  if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) +
421  (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) +
422  (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1)
423  ereport(ERROR,
424  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
425  errmsg("transition tables cannot be specified for triggers with more than one event")));
426 
427  /*
428  * We currently don't allow column-specific triggers with
429  * transition tables. Per spec, that seems to require
430  * accumulating separate transition tables for each combination of
431  * columns, which is a lot of work for a rather marginal feature.
432  */
433  if (stmt->columns != NIL)
434  ereport(ERROR,
435  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
436  errmsg("transition tables cannot be specified for triggers with column lists")));
437 
438  /*
439  * We disallow constraint triggers with transition tables, to
440  * protect the assumption that such triggers can't be deferred.
441  * See notes with AfterTriggers data structures, below.
442  *
443  * Currently this is enforced by the grammar, so just Assert here.
444  */
445  Assert(!stmt->isconstraint);
446 
447  if (tt->isNew)
448  {
449  if (!(TRIGGER_FOR_INSERT(tgtype) ||
450  TRIGGER_FOR_UPDATE(tgtype)))
451  ereport(ERROR,
452  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
453  errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger")));
454 
455  if (newtablename != NULL)
456  ereport(ERROR,
457  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
458  errmsg("NEW TABLE cannot be specified multiple times")));
459 
460  newtablename = tt->name;
461  }
462  else
463  {
464  if (!(TRIGGER_FOR_DELETE(tgtype) ||
465  TRIGGER_FOR_UPDATE(tgtype)))
466  ereport(ERROR,
467  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
468  errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger")));
469 
470  if (oldtablename != NULL)
471  ereport(ERROR,
472  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
473  errmsg("OLD TABLE cannot be specified multiple times")));
474 
475  oldtablename = tt->name;
476  }
477  }
478 
479  if (newtablename != NULL && oldtablename != NULL &&
480  strcmp(newtablename, oldtablename) == 0)
481  ereport(ERROR,
482  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
483  errmsg("OLD TABLE name and NEW TABLE name cannot be the same")));
484  }
485 
486  /*
487  * Parse the WHEN clause, if any
488  */
489  if (stmt->whenClause)
490  {
491  ParseState *pstate;
492  RangeTblEntry *rte;
493  List *varList;
494  ListCell *lc;
495 
496  /* Set up a pstate to parse with */
497  pstate = make_parsestate(NULL);
498  pstate->p_sourcetext = queryString;
499 
500  /*
501  * Set up RTEs for OLD and NEW references.
502  *
503  * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
504  */
505  rte = addRangeTableEntryForRelation(pstate, rel,
506  makeAlias("old", NIL),
507  false, false);
508  addRTEtoQuery(pstate, rte, false, true, true);
509  rte = addRangeTableEntryForRelation(pstate, rel,
510  makeAlias("new", NIL),
511  false, false);
512  addRTEtoQuery(pstate, rte, false, true, true);
513 
514  /* Transform expression. Copy to be sure we don't modify original */
515  whenClause = transformWhereClause(pstate,
516  copyObject(stmt->whenClause),
518  "WHEN");
519  /* we have to fix its collations too */
520  assign_expr_collations(pstate, whenClause);
521 
522  /*
523  * Check for disallowed references to OLD/NEW.
524  *
525  * NB: pull_var_clause is okay here only because we don't allow
526  * subselects in WHEN clauses; it would fail to examine the contents
527  * of subselects.
528  */
529  varList = pull_var_clause(whenClause, 0);
530  foreach(lc, varList)
531  {
532  Var *var = (Var *) lfirst(lc);
533 
534  switch (var->varno)
535  {
536  case PRS2_OLD_VARNO:
537  if (!TRIGGER_FOR_ROW(tgtype))
538  ereport(ERROR,
539  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
540  errmsg("statement trigger's WHEN condition cannot reference column values"),
541  parser_errposition(pstate, var->location)));
542  if (TRIGGER_FOR_INSERT(tgtype))
543  ereport(ERROR,
544  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
545  errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
546  parser_errposition(pstate, var->location)));
547  /* system columns are okay here */
548  break;
549  case PRS2_NEW_VARNO:
550  if (!TRIGGER_FOR_ROW(tgtype))
551  ereport(ERROR,
552  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
553  errmsg("statement trigger's WHEN condition cannot reference column values"),
554  parser_errposition(pstate, var->location)));
555  if (TRIGGER_FOR_DELETE(tgtype))
556  ereport(ERROR,
557  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
558  errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
559  parser_errposition(pstate, var->location)));
560  if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
561  ereport(ERROR,
562  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
563  errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
564  parser_errposition(pstate, var->location)));
565  break;
566  default:
567  /* can't happen without add_missing_from, so just elog */
568  elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
569  break;
570  }
571  }
572 
573  /* we'll need the rtable for recordDependencyOnExpr */
574  whenRtable = pstate->p_rtable;
575 
576  qual = nodeToString(whenClause);
577 
578  free_parsestate(pstate);
579  }
580  else
581  {
582  whenClause = NULL;
583  whenRtable = NIL;
584  qual = NULL;
585  }
586 
587  /*
588  * Find and validate the trigger function.
589  */
590  funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false);
591  if (!isInternal)
592  {
593  aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE);
594  if (aclresult != ACLCHECK_OK)
595  aclcheck_error(aclresult, ACL_KIND_PROC,
596  NameListToString(stmt->funcname));
597  }
598  funcrettype = get_func_rettype(funcoid);
599  if (funcrettype != TRIGGEROID)
600  {
601  /*
602  * We allow OPAQUE just so we can load old dump files. When we see a
603  * trigger function declared OPAQUE, change it to TRIGGER.
604  */
605  if (funcrettype == OPAQUEOID)
606  {
608  (errmsg("changing return type of function %s from %s to %s",
609  NameListToString(stmt->funcname),
610  "opaque", "trigger")));
612  }
613  else
614  ereport(ERROR,
615  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
616  errmsg("function %s must return type %s",
617  NameListToString(stmt->funcname), "trigger")));
618  }
619 
620  /*
621  * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that
622  * references one of the built-in RI_FKey trigger functions, assume it is
623  * from a dump of a pre-7.3 foreign key constraint, and take steps to
624  * convert this legacy representation into a regular foreign key
625  * constraint. Ugly, but necessary for loading old dump files.
626  */
627  if (stmt->isconstraint && !isInternal &&
628  list_length(stmt->args) >= 6 &&
629  (list_length(stmt->args) % 2) == 0 &&
631  {
632  /* Keep lock on target rel until end of xact */
633  heap_close(rel, NoLock);
634 
635  ConvertTriggerToFK(stmt, funcoid);
636 
637  return InvalidObjectAddress;
638  }
639 
640  /*
641  * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
642  * corresponding pg_constraint entry.
643  */
644  if (stmt->isconstraint && !OidIsValid(constraintOid))
645  {
646  /* Internal callers should have made their own constraints */
647  Assert(!isInternal);
648  constraintOid = CreateConstraintEntry(stmt->trigname,
651  stmt->deferrable,
652  stmt->initdeferred,
653  true,
654  RelationGetRelid(rel),
655  NULL, /* no conkey */
656  0,
657  InvalidOid, /* no domain */
658  InvalidOid, /* no index */
659  InvalidOid, /* no foreign key */
660  NULL,
661  NULL,
662  NULL,
663  NULL,
664  0,
665  ' ',
666  ' ',
667  ' ',
668  NULL, /* no exclusion */
669  NULL, /* no check constraint */
670  NULL,
671  NULL,
672  true, /* islocal */
673  0, /* inhcount */
674  true, /* isnoinherit */
675  isInternal); /* is_internal */
676  }
677 
678  /*
679  * Generate the trigger's OID now, so that we can use it in the name if
680  * needed.
681  */
683 
684  trigoid = GetNewOid(tgrel);
685 
686  /*
687  * If trigger is internally generated, modify the provided trigger name to
688  * ensure uniqueness by appending the trigger OID. (Callers will usually
689  * supply a simple constant trigger name in these cases.)
690  */
691  if (isInternal)
692  {
693  snprintf(internaltrigname, sizeof(internaltrigname),
694  "%s_%u", stmt->trigname, trigoid);
695  trigname = internaltrigname;
696  }
697  else
698  {
699  /* user-defined trigger; use the specified trigger name as-is */
700  trigname = stmt->trigname;
701  }
702 
703  /*
704  * Scan pg_trigger for existing triggers on relation. We do this only to
705  * give a nice error message if there's already a trigger of the same
706  * name. (The unique index on tgrelid/tgname would complain anyway.) We
707  * can skip this for internally generated triggers, since the name
708  * modification above should be sufficient.
709  *
710  * NOTE that this is cool only because we have ShareRowExclusiveLock on
711  * the relation, so the trigger set won't be changing underneath us.
712  */
713  if (!isInternal)
714  {
715  ScanKeyInit(&key,
717  BTEqualStrategyNumber, F_OIDEQ,
719  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
720  NULL, 1, &key);
721  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
722  {
723  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
724 
725  if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
726  ereport(ERROR,
728  errmsg("trigger \"%s\" for relation \"%s\" already exists",
729  trigname, RelationGetRelationName(rel))));
730  }
731  systable_endscan(tgscan);
732  }
733 
734  /*
735  * Build the new pg_trigger tuple.
736  */
737  memset(nulls, false, sizeof(nulls));
738 
741  CStringGetDatum(trigname));
742  values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
743  values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
745  values[Anum_pg_trigger_tgisinternal - 1] = BoolGetDatum(isInternal);
746  values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
747  values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid);
748  values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
751 
752  if (stmt->args)
753  {
754  ListCell *le;
755  char *args;
756  int16 nargs = list_length(stmt->args);
757  int len = 0;
758 
759  foreach(le, stmt->args)
760  {
761  char *ar = strVal(lfirst(le));
762 
763  len += strlen(ar) + 4;
764  for (; *ar; ar++)
765  {
766  if (*ar == '\\')
767  len++;
768  }
769  }
770  args = (char *) palloc(len + 1);
771  args[0] = '\0';
772  foreach(le, stmt->args)
773  {
774  char *s = strVal(lfirst(le));
775  char *d = args + strlen(args);
776 
777  while (*s)
778  {
779  if (*s == '\\')
780  *d++ = '\\';
781  *d++ = *s++;
782  }
783  strcpy(d, "\\000");
784  }
785  values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
787  CStringGetDatum(args));
788  }
789  else
790  {
791  values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
793  CStringGetDatum(""));
794  }
795 
796  /* build column number array if it's a column-specific trigger */
797  ncolumns = list_length(stmt->columns);
798  if (ncolumns == 0)
799  columns = NULL;
800  else
801  {
802  ListCell *cell;
803  int i = 0;
804 
805  columns = (int16 *) palloc(ncolumns * sizeof(int16));
806  foreach(cell, stmt->columns)
807  {
808  char *name = strVal(lfirst(cell));
809  int16 attnum;
810  int j;
811 
812  /* Lookup column name. System columns are not allowed */
813  attnum = attnameAttNum(rel, name, false);
814  if (attnum == InvalidAttrNumber)
815  ereport(ERROR,
816  (errcode(ERRCODE_UNDEFINED_COLUMN),
817  errmsg("column \"%s\" of relation \"%s\" does not exist",
818  name, RelationGetRelationName(rel))));
819 
820  /* Check for duplicates */
821  for (j = i - 1; j >= 0; j--)
822  {
823  if (columns[j] == attnum)
824  ereport(ERROR,
825  (errcode(ERRCODE_DUPLICATE_COLUMN),
826  errmsg("column \"%s\" specified more than once",
827  name)));
828  }
829 
830  columns[i++] = attnum;
831  }
832  }
833  tgattr = buildint2vector(columns, ncolumns);
834  values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
835 
836  /* set tgqual if trigger has WHEN clause */
837  if (qual)
838  values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(qual);
839  else
840  nulls[Anum_pg_trigger_tgqual - 1] = true;
841 
842  if (oldtablename)
844  CStringGetDatum(oldtablename));
845  else
846  nulls[Anum_pg_trigger_tgoldtable - 1] = true;
847  if (newtablename)
849  CStringGetDatum(newtablename));
850  else
851  nulls[Anum_pg_trigger_tgnewtable - 1] = true;
852 
853  tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
854 
855  /* force tuple to have the desired OID */
856  HeapTupleSetOid(tuple, trigoid);
857 
858  /*
859  * Insert tuple into pg_trigger.
860  */
861  CatalogTupleInsert(tgrel, tuple);
862 
863  heap_freetuple(tuple);
865 
869  if (oldtablename)
871  if (newtablename)
873 
874  /*
875  * Update relation's pg_class entry. Crucial side-effect: other backends
876  * (and this one too!) are sent SI message to make them rebuild relcache
877  * entries.
878  */
880  tuple = SearchSysCacheCopy1(RELOID,
882  if (!HeapTupleIsValid(tuple))
883  elog(ERROR, "cache lookup failed for relation %u",
884  RelationGetRelid(rel));
885 
886  ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
887 
888  CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
889 
890  heap_freetuple(tuple);
892 
893  /*
894  * We used to try to update the rel's relcache entry here, but that's
895  * fairly pointless since it will happen as a byproduct of the upcoming
896  * CommandCounterIncrement...
897  */
898 
899  /*
900  * Record dependencies for trigger. Always place a normal dependency on
901  * the function.
902  */
903  myself.classId = TriggerRelationId;
904  myself.objectId = trigoid;
905  myself.objectSubId = 0;
906 
907  referenced.classId = ProcedureRelationId;
908  referenced.objectId = funcoid;
909  referenced.objectSubId = 0;
910  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
911 
912  if (isInternal && OidIsValid(constraintOid))
913  {
914  /*
915  * Internally-generated trigger for a constraint, so make it an
916  * internal dependency of the constraint. We can skip depending on
917  * the relation(s), as there'll be an indirect dependency via the
918  * constraint.
919  */
920  referenced.classId = ConstraintRelationId;
921  referenced.objectId = constraintOid;
922  referenced.objectSubId = 0;
923  recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
924  }
925  else
926  {
927  /*
928  * User CREATE TRIGGER, so place dependencies. We make trigger be
929  * auto-dropped if its relation is dropped or if the FK relation is
930  * dropped. (Auto drop is compatible with our pre-7.3 behavior.)
931  */
932  referenced.classId = RelationRelationId;
933  referenced.objectId = RelationGetRelid(rel);
934  referenced.objectSubId = 0;
935  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
936  if (OidIsValid(constrrelid))
937  {
938  referenced.classId = RelationRelationId;
939  referenced.objectId = constrrelid;
940  referenced.objectSubId = 0;
941  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
942  }
943  /* Not possible to have an index dependency in this case */
944  Assert(!OidIsValid(indexOid));
945 
946  /*
947  * If it's a user-specified constraint trigger, make the constraint
948  * internally dependent on the trigger instead of vice versa.
949  */
950  if (OidIsValid(constraintOid))
951  {
952  referenced.classId = ConstraintRelationId;
953  referenced.objectId = constraintOid;
954  referenced.objectSubId = 0;
955  recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
956  }
957  }
958 
959  /* If column-specific trigger, add normal dependencies on columns */
960  if (columns != NULL)
961  {
962  int i;
963 
964  referenced.classId = RelationRelationId;
965  referenced.objectId = RelationGetRelid(rel);
966  for (i = 0; i < ncolumns; i++)
967  {
968  referenced.objectSubId = columns[i];
969  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
970  }
971  }
972 
973  /*
974  * If it has a WHEN clause, add dependencies on objects mentioned in the
975  * expression (eg, functions, as well as any columns used).
976  */
977  if (whenClause != NULL)
978  recordDependencyOnExpr(&myself, whenClause, whenRtable,
980 
981  /* Post creation hook for new trigger */
983  isInternal);
984 
985  /* Keep lock on target rel until end of xact */
986  heap_close(rel, NoLock);
987 
988  return myself;
989 }
signed short int16
Definition: c.h:283
#define NIL
Definition: pg_list.h:69
#define Anum_pg_trigger_tgdeferrable
Definition: pg_trigger.h:88
#define TRIGGER_FOR_DELETE(type)
Definition: pg_trigger.h:135
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3271
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:46
int errhint(const char *fmt,...)
Definition: elog.c:987
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
#define GETSTRUCT(TUP)
Definition: htup_details.h:661
bool IsSystemRelation(Relation relation)
Definition: catalog.c:63
Oid GetUserId(void)
Definition: miscinit.c:284
#define PointerGetDatum(X)
Definition: postgres.h:562
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:53
#define Anum_pg_trigger_tgconstraint
Definition: pg_trigger.h:87
#define ProcedureRelationId
Definition: pg_proc.h:33
Node * whenClause
Definition: parsenodes.h:2375
#define RelationRelationId
Definition: pg_class.h:29
int2vector * buildint2vector(const int16 *int2s, int n)
Definition: int.c:112
#define Anum_pg_trigger_tgqual
Definition: pg_trigger.h:93
#define TRIGGER_CLEAR_TYPE(type)
Definition: pg_trigger.h:118
#define Int16GetDatum(X)
Definition: postgres.h:457
#define Anum_pg_trigger_tgtype
Definition: pg_trigger.h:82
#define AccessShareLock
Definition: lockdefs.h:36
#define Anum_pg_trigger_tgnargs
Definition: pg_trigger.h:90
Definition: nodes.h:512
#define strVal(v)
Definition: value.h:54
int errcode(int sqlerrcode)
Definition: elog.c:575
int namestrcmp(Name name, const char *str)
Definition: name.c:247
#define TRIGGER_FOR_UPDATE(type)
Definition: pg_trigger.h:136
AttrNumber varattno
Definition: primnodes.h:168
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
List * pull_var_clause(Node *node, int flags)
Definition: var.c:535
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:695
#define heap_close(r, l)
Definition: heapam.h:97
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:585
Form_pg_class rd_rel
Definition: rel.h:114
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
unsigned int Oid
Definition: postgres_ext.h:31
Definition: primnodes.h:163
#define Anum_pg_trigger_tgisinternal
Definition: pg_trigger.h:84
#define OidIsValid(objectId)
Definition: c.h:576
#define TRIGGER_FIRES_ON_ORIGIN
Definition: trigger.h:155
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:328
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1459
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
#define TriggerRelidNameIndexId
Definition: indexing.h:249
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:703
#define NAMEDATALEN
void assign_expr_collations(ParseState *pstate, Node *expr)
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:386
RangeVar * constrrel
Definition: parsenodes.h:2382
#define TRIGGER_TYPE_INSTEAD
Definition: pg_trigger.h:104
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
#define TRIGGER_FOR_ROW(type)
Definition: pg_trigger.h:130
void pfree(void *pointer)
Definition: mcxt.c:949
#define OPAQUEOID
Definition: pg_type.h:700
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
Oid CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:162
#define TRIGGER_TYPE_AFTER
Definition: pg_trigger.h:112
#define TRIGGEROID
Definition: pg_type.h:692
ItemPointerData t_self
Definition: htup.h:65
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:282
Datum byteain(PG_FUNCTION_ARGS)
Definition: varlena.c:255
#define lfirst_node(type, lc)
Definition: pg_list.h:109
#define ACL_TRIGGER
Definition: parsenodes.h:78
int location
Definition: primnodes.h:178
#define NoLock
Definition: lockdefs.h:34
#define TRIGGER_SETT_ROW(type)
Definition: pg_trigger.h:120
void aclcheck_error(AclResult aclerr, AclObjectKind objectkind, const char *objectname)
Definition: aclchk.c:3457
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define CStringGetDatum(X)
Definition: postgres.h:584
void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior)
Definition: dependency.c:1351
#define RelationGetRelationName(relation)
Definition: rel.h:445
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
const char * p_sourcetext
Definition: parse_node.h:172
#define TRIGGER_FOR_BEFORE(type)
Definition: pg_trigger.h:131
static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
Definition: trigger.c:1019
List * transitionRels
Definition: parsenodes.h:2378
#define ereport(elevel, rest)
Definition: elog.h:122
void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Oid rd_id
Definition: rel.h:116
#define PRS2_OLD_VARNO
Definition: primnodes.h:160
#define Anum_pg_trigger_tgrelid
Definition: pg_trigger.h:79
#define WARNING
Definition: elog.h:40
Index varno
Definition: primnodes.h:166
char * NameListToString(List *names)
Definition: namespace.c:3063
#define Anum_pg_trigger_tgnewtable
Definition: pg_trigger.h:95
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
Definition: c.h:515
#define Anum_pg_trigger_tgconstrindid
Definition: pg_trigger.h:86
AclResult
Definition: acl.h:178
uintptr_t Datum
Definition: postgres.h:372
#define Anum_pg_trigger_tgenabled
Definition: pg_trigger.h:83
Oid GetNewOid(Relation relation)
Definition: catalog.c:289
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
void SetFunctionReturnType(Oid funcOid, Oid newRetType)
#define TRIGGER_FOR_TRUNCATE(type)
Definition: pg_trigger.h:137
#define InvokeObjectPostCreateHookArg(classId, objectId, subId, is_internal)
Definition: objectaccess.h:147
TupleDesc rd_att
Definition: rel.h:115
#define BoolGetDatum(X)
Definition: postgres.h:408
Relation heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: heapam.c:1318
bool allowSystemTableMods
Definition: globals.c:112
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
int attnameAttNum(Relation rd, const char *attname, bool sysColOK)
#define Assert(condition)
Definition: c.h:670
#define lfirst(lc)
Definition: pg_list.h:106
#define Anum_pg_trigger_tgargs
Definition: pg_trigger.h:92
#define ShareRowExclusiveLock
Definition: lockdefs.h:42
#define Anum_pg_trigger_tgconstrrelid
Definition: pg_trigger.h:85
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
#define Anum_pg_trigger_tgname
Definition: pg_trigger.h:80
#define TriggerRelationId
Definition: pg_trigger.h:34
#define TRIGGER_FOR_INSERT(type)
Definition: pg_trigger.h:134
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:210
static int list_length(const List *l)
Definition: pg_list.h:89
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:72
RangeTblEntry * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, Alias *alias, bool inh, bool inFromCl)
const char * name
Definition: encode.c:521
#define InvalidAttrNumber
Definition: attnum.h:23
#define CharGetDatum(X)
Definition: postgres.h:422
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4480
#define DatumGetPointer(X)
Definition: postgres.h:555
#define TRIGGER_TYPE_BEFORE
Definition: pg_trigger.h:99
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
Definition: parse_func.c:1952
#define Anum_pg_trigger_tgoldtable
Definition: pg_trigger.h:94
static Datum values[MAXATTR]
Definition: bootstrap.c:164
FormData_pg_class * Form_pg_class
Definition: pg_class.h:95
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:173
const ObjectAddress InvalidObjectAddress
void * palloc(Size size)
Definition: mcxt.c:848
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define ACL_EXECUTE
Definition: parsenodes.h:79
#define RELKIND_VIEW
Definition: pg_class.h:164
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4504
int i
RangeVar * relation
Definition: parsenodes.h:2366
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define CStringGetTextDatum(s)
Definition: builtins.h:91
char * nodeToString(const void *obj)
Definition: outfuncs.c:4265
#define ConstraintRelationId
Definition: pg_constraint.h:29
#define elog
Definition: elog.h:219
#define CONSTRAINT_TRIGGER
#define copyObject(obj)
Definition: nodes.h:625
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:105
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:77
#define RELKIND_RELATION
Definition: pg_class.h:160
#define RI_TRIGGER_NONE
Definition: trigger.h:268
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
Definition: pg_list.h:45
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1745
#define RelationGetRelid(relation)
Definition: rel.h:425
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isValidated, Oid relId, const int16 *constraintKey, int constraintNKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, const char *conSrc, bool conIsLocal, int conInhCount, bool conNoInherit, bool is_internal)
Definition: pg_constraint.c:49
#define PRS2_NEW_VARNO
Definition: primnodes.h:161
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define Anum_pg_trigger_tgfoid
Definition: pg_trigger.h:81
#define Anum_pg_trigger_tginitdeferred
Definition: pg_trigger.h:89
#define Anum_pg_trigger_tgattr
Definition: pg_trigger.h:91
#define Natts_pg_trigger
Definition: pg_trigger.h:78
#define TRIGGER_FOR_INSTEAD(type)
Definition: pg_trigger.h:133
#define RelationGetNamespace(relation)
Definition: rel.h:452
List * p_rtable
Definition: parse_node.h:173

◆ EnableDisableTrigger()

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

Definition at line 1581 of file trigger.c.

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

Referenced by ATExecEnableDisableTrigger().

1583 {
1584  Relation tgrel;
1585  int nkeys;
1586  ScanKeyData keys[2];
1587  SysScanDesc tgscan;
1588  HeapTuple tuple;
1589  bool found;
1590  bool changed;
1591 
1592  /* Scan the relevant entries in pg_triggers */
1594 
1595  ScanKeyInit(&keys[0],
1597  BTEqualStrategyNumber, F_OIDEQ,
1599  if (tgname)
1600  {
1601  ScanKeyInit(&keys[1],
1603  BTEqualStrategyNumber, F_NAMEEQ,
1604  CStringGetDatum(tgname));
1605  nkeys = 2;
1606  }
1607  else
1608  nkeys = 1;
1609 
1610  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1611  NULL, nkeys, keys);
1612 
1613  found = changed = false;
1614 
1615  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1616  {
1617  Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
1618 
1619  if (oldtrig->tgisinternal)
1620  {