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

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3393 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 3465 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 3476 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:3454
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3428
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3406

Definition at line 3467 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:3428
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3406

Definition at line 3478 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:3393
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3391
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3392

Definition at line 3428 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3406 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3395 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

◆ AfterTriggersTableData

◆ AfterTriggersTransData

◆ SetConstraintState

◆ SetConstraintStateData

◆ SetConstraintTrigger

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3383 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

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

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4533 of file trigger.c.

References AfterTriggersData::query_depth.

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

4534 {
4535  /* Increase the query stack depth */
4537 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3609

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

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

4802 {
4803  int my_level = GetCurrentTransactionNestLevel();
4804 
4805  /*
4806  * Allocate more space in the trans_stack if needed. (Note: because the
4807  * minimum nest level of a subtransaction is 2, we waste the first couple
4808  * entries of the array; not worth the notational effort to avoid it.)
4809  */
4810  while (my_level >= afterTriggers.maxtransdepth)
4811  {
4812  if (afterTriggers.maxtransdepth == 0)
4813  {
4814  /* Arbitrarily initialize for max of 8 subtransaction levels */
4817  8 * sizeof(AfterTriggersTransData));
4819  }
4820  else
4821  {
4822  /* repalloc will keep the stack in the same context */
4823  int new_alloc = afterTriggers.maxtransdepth * 2;
4824 
4827  new_alloc * sizeof(AfterTriggersTransData));
4828  afterTriggers.maxtransdepth = new_alloc;
4829  }
4830  }
4831 
4832  /*
4833  * Push the current information into the stack. The SET CONSTRAINTS state
4834  * is not saved until/unless changed. Likewise, we don't make a
4835  * per-subtransaction event context until needed.
4836  */
4837  afterTriggers.trans_stack[my_level].state = NULL;
4841 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3576
MemoryContext TopTransactionContext
Definition: mcxt.c:48
AfterTriggerEventList events
Definition: trigger.c:3591
CommandId firing_counter
Definition: trigger.c:3593
CommandId firing_counter
Definition: trigger.c:3565
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:754
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:949
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:693
AfterTriggerEventList events
Definition: trigger.c:3567
static AfterTriggersData afterTriggers
Definition: trigger.c:3609
SetConstraintState state
Definition: trigger.c:3590

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

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

4502 {
4503  /*
4504  * Initialize after-trigger state structure to empty
4505  */
4506  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4508 
4509  /*
4510  * Verify that there is no leftover state remaining. If these assertions
4511  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4512  * up properly.
4513  */
4514  Assert(afterTriggers.state == NULL);
4515  Assert(afterTriggers.query_stack == NULL);
4517  Assert(afterTriggers.event_cxt == NULL);
4518  Assert(afterTriggers.events.head == NULL);
4519  Assert(afterTriggers.trans_stack == NULL);
4521 }
uint32 CommandId
Definition: c.h:477
AfterTriggersTransData * trans_stack
Definition: trigger.c:3576
AfterTriggersQueryData * query_stack
Definition: trigger.c:3571
SetConstraintState state
Definition: trigger.c:3566
CommandId firing_counter
Definition: trigger.c:3565
#define Assert(condition)
Definition: c.h:688
AfterTriggerEventChunk * head
Definition: trigger.c:3459
MemoryContext event_cxt
Definition: trigger.c:3568
AfterTriggerEventList events
Definition: trigger.c:3567
static AfterTriggersData afterTriggers
Definition: trigger.c:3609

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

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

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

3893 {
3894  AfterTriggerEventChunk *target = qs->events.head;
3895  ListCell *lc;
3896 
3897  Assert(target && target->next);
3898 
3899  /*
3900  * First, update any pointers in the per-table data, so that they won't be
3901  * dangling. Resetting obsoleted pointers to NULL will make
3902  * cancel_prior_stmt_triggers start from the list head, which is fine.
3903  */
3904  foreach(lc, qs->tables)
3905  {
3907 
3908  if (table->after_trig_done &&
3909  table->after_trig_events.tail == target)
3910  {
3911  table->after_trig_events.head = NULL;
3912  table->after_trig_events.tail = NULL;
3913  table->after_trig_events.tailfree = NULL;
3914  }
3915  }
3916 
3917  /* Now we can flush the head chunk */
3918  qs->events.head = target->next;
3919  pfree(target);
3920 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3460
struct AfterTriggerEventChunk * next
Definition: trigger.c:3447
void pfree(void *pointer)
Definition: mcxt.c:936
AfterTriggerEventList after_trig_events
Definition: trigger.c:3604
#define Assert(condition)
Definition: c.h:688
#define lfirst(lc)
Definition: pg_list.h:106
AfterTriggerEventChunk * head
Definition: trigger.c:3459
AfterTriggerEventList events
Definition: trigger.c:3582

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

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

4850 {
4851  int my_level = GetCurrentTransactionNestLevel();
4853  AfterTriggerEvent event;
4854  AfterTriggerEventChunk *chunk;
4855  CommandId subxact_firing_id;
4856 
4857  /*
4858  * Pop the prior state if needed.
4859  */
4860  if (isCommit)
4861  {
4862  Assert(my_level < afterTriggers.maxtransdepth);
4863  /* If we saved a prior state, we don't need it anymore */
4864  state = afterTriggers.trans_stack[my_level].state;
4865  if (state != NULL)
4866  pfree(state);
4867  /* this avoids double pfree if error later: */
4868  afterTriggers.trans_stack[my_level].state = NULL;
4871  }
4872  else
4873  {
4874  /*
4875  * Aborting. It is possible subxact start failed before calling
4876  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
4877  * trans_stack levels that aren't there.
4878  */
4879  if (my_level >= afterTriggers.maxtransdepth)
4880  return;
4881 
4882  /*
4883  * Release query-level storage for queries being aborted, and restore
4884  * query_depth to its pre-subxact value. This assumes that a
4885  * subtransaction will not add events to query levels started in a
4886  * earlier transaction state.
4887  */
4889  {
4893  }
4896 
4897  /*
4898  * Restore the global deferred-event list to its former length,
4899  * discarding any events queued by the subxact.
4900  */
4902  &afterTriggers.trans_stack[my_level].events);
4903 
4904  /*
4905  * Restore the trigger state. If the saved state is NULL, then this
4906  * subxact didn't save it, so it doesn't need restoring.
4907  */
4908  state = afterTriggers.trans_stack[my_level].state;
4909  if (state != NULL)
4910  {
4912  afterTriggers.state = state;
4913  }
4914  /* this avoids double pfree if error later: */
4915  afterTriggers.trans_stack[my_level].state = NULL;
4916 
4917  /*
4918  * Scan for any remaining deferred events that were marked DONE or IN
4919  * PROGRESS by this subxact or a child, and un-mark them. We can
4920  * recognize such events because they have a firing ID greater than or
4921  * equal to the firing_counter value we saved at subtransaction start.
4922  * (This essentially assumes that the current subxact includes all
4923  * subxacts started after it.)
4924  */
4925  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
4927  {
4928  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4929 
4930  if (event->ate_flags &
4932  {
4933  if (evtshared->ats_firing_id >= subxact_firing_id)
4934  event->ate_flags &=
4936  }
4937  }
4938  }
4939 }
uint32 CommandId
Definition: c.h:477
AfterTriggersTransData * trans_stack
Definition: trigger.c:3576
TriggerFlags ate_flags
Definition: trigger.c:3410
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3386
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3472
AfterTriggerEventList events
Definition: trigger.c:3591
AfterTriggersQueryData * query_stack
Definition: trigger.c:3571
CommandId firing_counter
Definition: trigger.c:3593
#define GetTriggerSharedData(evt)
Definition: trigger.c:3435
void pfree(void *pointer)
Definition: mcxt.c:936
SetConstraintState state
Definition: trigger.c:3566
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:754
#define Assert(condition)
Definition: c.h:688
Definition: regguts.h:298
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:3852
CommandId ats_firing_id
Definition: trigger.c:3402
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3387
AfterTriggerEventList events
Definition: trigger.c:3567
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4644
static AfterTriggersData afterTriggers
Definition: trigger.c:3609
SetConstraintState state
Definition: trigger.c:3590

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

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

4754 {
4755  /*
4756  * Forget the pending-events list.
4757  *
4758  * Since all the info is in TopTransactionContext or children thereof, we
4759  * don't really need to do anything to reclaim memory. However, the
4760  * pending-events list could be large, and so it's useful to discard it as
4761  * soon as possible --- especially if we are aborting because we ran out
4762  * of memory for the list!
4763  */
4765  {
4767  afterTriggers.event_cxt = NULL;
4768  afterTriggers.events.head = NULL;
4769  afterTriggers.events.tail = NULL;
4770  afterTriggers.events.tailfree = NULL;
4771  }
4772 
4773  /*
4774  * Forget any subtransaction state as well. Since this can't be very
4775  * large, we let the eventual reset of TopTransactionContext free the
4776  * memory instead of doing it here.
4777  */
4778  afterTriggers.trans_stack = NULL;
4780 
4781 
4782  /*
4783  * Forget the query stack and constraint-related state information. As
4784  * with the subtransaction state information, we don't bother freeing the
4785  * memory here.
4786  */
4787  afterTriggers.query_stack = NULL;
4789  afterTriggers.state = NULL;
4790 
4791  /* No more afterTriggers manipulation until next transaction starts. */
4793 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:198
AfterTriggersTransData * trans_stack
Definition: trigger.c:3576
AfterTriggersQueryData * query_stack
Definition: trigger.c:3571
AfterTriggerEventChunk * tail
Definition: trigger.c:3460
SetConstraintState state
Definition: trigger.c:3566
AfterTriggerEventChunk * head
Definition: trigger.c:3459
MemoryContext event_cxt
Definition: trigger.c:3568
AfterTriggerEventList events
Definition: trigger.c:3567
static AfterTriggersData afterTriggers
Definition: trigger.c:3609

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

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

4952 {
4953  int init_depth = afterTriggers.maxquerydepth;
4954 
4956 
4957  if (afterTriggers.maxquerydepth == 0)
4958  {
4959  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
4960 
4963  new_alloc * sizeof(AfterTriggersQueryData));
4964  afterTriggers.maxquerydepth = new_alloc;
4965  }
4966  else
4967  {
4968  /* repalloc will keep the stack in the same context */
4969  int old_alloc = afterTriggers.maxquerydepth;
4970  int new_alloc = Max(afterTriggers.query_depth + 1,
4971  old_alloc * 2);
4972 
4975  new_alloc * sizeof(AfterTriggersQueryData));
4976  afterTriggers.maxquerydepth = new_alloc;
4977  }
4978 
4979  /* Initialize new array entries to empty */
4980  while (init_depth < afterTriggers.maxquerydepth)
4981  {
4983 
4984  qs->events.head = NULL;
4985  qs->events.tail = NULL;
4986  qs->events.tailfree = NULL;
4987  qs->fdw_tuplestore = NULL;
4988  qs->tables = NIL;
4989 
4990  ++init_depth;
4991  }
4992 }
#define NIL
Definition: pg_list.h:69
MemoryContext TopTransactionContext
Definition: mcxt.c:48
AfterTriggersQueryData * query_stack
Definition: trigger.c:3571
AfterTriggerEventChunk * tail
Definition: trigger.c:3460
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3583
#define Max(x, y)
Definition: c.h:840
#define Assert(condition)
Definition: c.h:688
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:949
AfterTriggerEventChunk * head
Definition: trigger.c:3459
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:693
AfterTriggerEventList events
Definition: trigger.c:3582
static AfterTriggersData afterTriggers
Definition: trigger.c:3609

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

3952 {
3953  AfterTriggerShared evtshared = GetTriggerSharedData(event);
3954  Oid tgoid = evtshared->ats_tgoid;
3955  TriggerData LocTriggerData;
3956  HeapTupleData tuple1;
3957  HeapTupleData tuple2;
3958  HeapTuple rettuple;
3959  Buffer buffer1 = InvalidBuffer;
3960  Buffer buffer2 = InvalidBuffer;
3961  int tgindx;
3962 
3963  /*
3964  * Locate trigger in trigdesc.
3965  */
3966  LocTriggerData.tg_trigger = NULL;
3967  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
3968  {
3969  if (trigdesc->triggers[tgindx].tgoid == tgoid)
3970  {
3971  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
3972  break;
3973  }
3974  }
3975  if (LocTriggerData.tg_trigger == NULL)
3976  elog(ERROR, "could not find trigger %u", tgoid);
3977 
3978  /*
3979  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
3980  * to include time spent re-fetching tuples in the trigger cost.
3981  */
3982  if (instr)
3983  InstrStartNode(instr + tgindx);
3984 
3985  /*
3986  * Fetch the required tuple(s).
3987  */
3988  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
3989  {
3991  {
3992  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
3993 
3994  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
3995  trig_tuple_slot1))
3996  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
3997 
3998  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4000  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4001  trig_tuple_slot2))
4002  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4003  }
4004  /* fall through */
4006 
4007  /*
4008  * Using ExecMaterializeSlot() rather than ExecFetchSlotTuple()
4009  * ensures that tg_trigtuple does not reference tuplestore memory.
4010  * (It is formally possible for the trigger function to queue
4011  * trigger events that add to the same tuplestore, which can push
4012  * other tuples out of memory.) The distinction is academic,
4013  * because we start with a minimal tuple that ExecFetchSlotTuple()
4014  * must materialize anyway.
4015  */
4016  LocTriggerData.tg_trigtuple =
4017  ExecMaterializeSlot(trig_tuple_slot1);
4018  LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
4019 
4020  LocTriggerData.tg_newtuple =
4021  ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4023  ExecMaterializeSlot(trig_tuple_slot2) : NULL;
4024  LocTriggerData.tg_newtuplebuf = InvalidBuffer;
4025 
4026  break;
4027 
4028  default:
4029  if (ItemPointerIsValid(&(event->ate_ctid1)))
4030  {
4031  ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
4032  if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
4033  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4034  LocTriggerData.tg_trigtuple = &tuple1;
4035  LocTriggerData.tg_trigtuplebuf = buffer1;
4036  }
4037  else
4038  {
4039  LocTriggerData.tg_trigtuple = NULL;
4040  LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
4041  }
4042 
4043  /* don't touch ctid2 if not there */
4044  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4046  ItemPointerIsValid(&(event->ate_ctid2)))
4047  {
4048  ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
4049  if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
4050  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4051  LocTriggerData.tg_newtuple = &tuple2;
4052  LocTriggerData.tg_newtuplebuf = buffer2;
4053  }
4054  else
4055  {
4056  LocTriggerData.tg_newtuple = NULL;
4057  LocTriggerData.tg_newtuplebuf = InvalidBuffer;
4058  }
4059  }
4060 
4061  /*
4062  * Set up the tuplestore information to let the trigger have access to
4063  * transition tables. When we first make a transition table available to
4064  * a trigger, mark it "closed" so that it cannot change anymore. If any
4065  * additional events of the same type get queued in the current trigger
4066  * query level, they'll go into new transition tables.
4067  */
4068  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4069  if (evtshared->ats_table)
4070  {
4071  if (LocTriggerData.tg_trigger->tgoldtable)
4072  {
4073  LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4074  evtshared->ats_table->closed = true;
4075  }
4076 
4077  if (LocTriggerData.tg_trigger->tgnewtable)
4078  {
4079  LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4080  evtshared->ats_table->closed = true;
4081  }
4082  }
4083 
4084  /*
4085  * Setup the remaining trigger information
4086  */
4087  LocTriggerData.type = T_TriggerData;
4088  LocTriggerData.tg_event =
4090  LocTriggerData.tg_relation = rel;
4091 
4092  MemoryContextReset(per_tuple_context);
4093 
4094  /*
4095  * Call the trigger and throw away any possibly returned updated tuple.
4096  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4097  */
4098  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4099  tgindx,
4100  finfo,
4101  NULL,
4102  per_tuple_context);
4103  if (rettuple != NULL &&
4104  rettuple != LocTriggerData.tg_trigtuple &&
4105  rettuple != LocTriggerData.tg_newtuple)
4106  heap_freetuple(rettuple);
4107 
4108  /*
4109  * Release buffers
4110  */
4111  if (buffer1 != InvalidBuffer)
4112  ReleaseBuffer(buffer1);
4113  if (buffer2 != InvalidBuffer)
4114  ReleaseBuffer(buffer2);
4115 
4116  /*
4117  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4118  * one "tuple returned" (really the number of firings).
4119  */
4120  if (instr)
4121  InstrStopNode(instr + tgindx, 1);
4122 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:60
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
TriggerEvent ats_event
Definition: trigger.c:3399
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:80
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3389
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3633
ItemPointerData ate_ctid2
Definition: trigger.c:3412
TriggerFlags ate_flags
Definition: trigger.c:3410
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:1899
Oid tgoid
Definition: reltrigger.h:25
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3393
#define InvalidBuffer
Definition: buf.h:25
Tuplestorestate * old_tuplestore
Definition: trigger.c:3605
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:134
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:3435
#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:3606
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3392
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:3403
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:3390
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:761
#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:3411
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 4697 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().

4698 {
4699  AfterTriggerEventList *events;
4700  bool snap_pushed = false;
4701 
4702  /* Must not be inside a query */
4704 
4705  /*
4706  * If there are any triggers to fire, make sure we have set a snapshot for
4707  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4708  * can't assume ActiveSnapshot is valid on entry.)
4709  */
4710  events = &afterTriggers.events;
4711  if (events->head != NULL)
4712  {
4714  snap_pushed = true;
4715  }
4716 
4717  /*
4718  * Run all the remaining triggers. Loop until they are all gone, in case
4719  * some trigger queues more for us to do.
4720  */
4721  while (afterTriggerMarkEvents(events, NULL, false))
4722  {
4723  CommandId firing_id = afterTriggers.firing_counter++;
4724 
4725  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4726  break; /* all fired */
4727  }
4728 
4729  /*
4730  * We don't bother freeing the event list, since it will go away anyway
4731  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4732  */
4733 
4734  if (snap_pushed)
4736 }
uint32 CommandId
Definition: c.h:477
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4212
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:3565
#define Assert(condition)
Definition: c.h:688
AfterTriggerEventChunk * head
Definition: trigger.c:3459
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4140
AfterTriggerEventList events
Definition: trigger.c:3567
static AfterTriggersData afterTriggers
Definition: trigger.c:3609

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 3831 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

3832 {
3833  AfterTriggerEventChunk *chunk;
3834 
3835  while ((chunk = events->head) != NULL)
3836  {
3837  events->head = chunk->next;
3838  pfree(chunk);
3839  }
3840  events->tail = NULL;
3841  events->tailfree = NULL;
3842 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3460
struct AfterTriggerEventChunk * next
Definition: trigger.c:3447
void pfree(void *pointer)
Definition: mcxt.c:936
AfterTriggerEventChunk * head
Definition: trigger.c:3459

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

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

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

◆ afterTriggerInvokeEvents()

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

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

4216 {
4217  bool all_fired = true;
4218  AfterTriggerEventChunk *chunk;
4219  MemoryContext per_tuple_context;
4220  bool local_estate = false;
4221  Relation rel = NULL;
4222  TriggerDesc *trigdesc = NULL;
4223  FmgrInfo *finfo = NULL;
4224  Instrumentation *instr = NULL;
4225  TupleTableSlot *slot1 = NULL,
4226  *slot2 = NULL;
4227 
4228  /* Make a local EState if need be */
4229  if (estate == NULL)
4230  {
4231  estate = CreateExecutorState();
4232  local_estate = true;
4233  }
4234 
4235  /* Make a per-tuple memory context for trigger function calls */
4236  per_tuple_context =
4238  "AfterTriggerTupleContext",
4240 
4241  for_each_chunk(chunk, *events)
4242  {
4243  AfterTriggerEvent event;
4244  bool all_fired_in_chunk = true;
4245 
4246  for_each_event(event, chunk)
4247  {
4248  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4249 
4250  /*
4251  * Is it one for me to fire?
4252  */
4253  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4254  evtshared->ats_firing_id == firing_id)
4255  {
4256  /*
4257  * So let's fire it... but first, find the correct relation if
4258  * this is not the same relation as before.
4259  */
4260  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4261  {
4262  ResultRelInfo *rInfo;
4263 
4264  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4265  rel = rInfo->ri_RelationDesc;
4266  trigdesc = rInfo->ri_TrigDesc;
4267  finfo = rInfo->ri_TrigFunctions;
4268  instr = rInfo->ri_TrigInstrument;
4269  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4270  {
4271  if (slot1 != NULL)
4272  {
4275  }
4276  slot1 = MakeSingleTupleTableSlot(rel->rd_att);
4277  slot2 = MakeSingleTupleTableSlot(rel->rd_att);
4278  }
4279  if (trigdesc == NULL) /* should not happen */
4280  elog(ERROR, "relation %u has no triggers",
4281  evtshared->ats_relid);
4282  }
4283 
4284  /*
4285  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4286  * still set, so recursive examinations of the event list
4287  * won't try to re-fire it.
4288  */
4289  AfterTriggerExecute(event, rel, trigdesc, finfo, instr,
4290  per_tuple_context, slot1, slot2);
4291 
4292  /*
4293  * Mark the event as done.
4294  */
4295  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4296  event->ate_flags |= AFTER_TRIGGER_DONE;
4297  }
4298  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4299  {
4300  /* something remains to be done */
4301  all_fired = all_fired_in_chunk = false;
4302  }
4303  }
4304 
4305  /* Clear the chunk if delete_ok and nothing left of interest */
4306  if (delete_ok && all_fired_in_chunk)
4307  {
4308  chunk->freeptr = CHUNK_DATA_START(chunk);
4309  chunk->endfree = chunk->endptr;
4310 
4311  /*
4312  * If it's last chunk, must sync event list's tailfree too. Note
4313  * that delete_ok must NOT be passed as true if there could be
4314  * additional AfterTriggerEventList values pointing at this event
4315  * list, since we'd fail to fix their copies of tailfree.
4316  */
4317  if (chunk == events->tail)
4318  events->tailfree = chunk->freeptr;
4319  }
4320  }
4321  if (slot1 != NULL)
4322  {
4325  }
4326 
4327  /* Release working resources */
4328  MemoryContextDelete(per_tuple_context);
4329 
4330  if (local_estate)
4331  {
4332  ExecCleanUpTriggerState(estate);
4333  FreeExecutorState(estate);
4334  }
4335 
4336  return all_fired;
4337 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:368
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:198
TriggerFlags ate_flags
Definition: trigger.c:3410
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3386
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3454
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:389
AfterTriggerEventChunk * tail
Definition: trigger.c:3460
Form_pg_class rd_rel
Definition: rel.h:114
#define GetTriggerSharedData(evt)
Definition: trigger.c:3435
#define for_each_event(eptr, cptr)
Definition: trigger.c:3467
void FreeExecutorState(EState *estate)
Definition: execUtils.c:185
#define ERROR
Definition: elog.h:43
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:247
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc)
Definition: execTuples.c:232
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:380
EState * CreateExecutorState(void)
Definition: execUtils.c:80
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1388
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:165
TupleDesc rd_att
Definition: rel.h:115
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3465
CommandId ats_firing_id
Definition: trigger.c:3402
void ExecCleanUpTriggerState(EState *estate)
Definition: execMain.c:1469
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3387
#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:3946
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:383

◆ afterTriggerMarkEvents()

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

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

4143 {
4144  bool found = false;
4145  AfterTriggerEvent event;
4146  AfterTriggerEventChunk *chunk;
4147 
4148  for_each_event_chunk(event, chunk, *events)
4149  {
4150  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4151  bool defer_it = false;
4152 
4153  if (!(event->ate_flags &
4155  {
4156  /*
4157  * This trigger hasn't been called or scheduled yet. Check if we
4158  * should call it now.
4159  */
4160  if (immediate_only && afterTriggerCheckState(evtshared))
4161  {
4162  defer_it = true;
4163  }
4164  else
4165  {
4166  /*
4167  * Mark it as to be fired in this firing cycle.
4168  */
4170  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4171  found = true;
4172  }
4173  }
4174 
4175  /*
4176  * If it's deferred, move it to move_list, if requested.
4177  */
4178  if (defer_it && move_list != NULL)
4179  {
4180  /* add it to move_list */
4181  afterTriggerAddEvent(move_list, event, evtshared);
4182  /* mark original copy "done" so we don't do it again */
4183  event->ate_flags |= AFTER_TRIGGER_DONE;
4184  }
4185  }
4186 
4187  return found;
4188 }
TriggerFlags ate_flags
Definition: trigger.c:3410
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3386
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3472
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3669
#define GetTriggerSharedData(evt)
Definition: trigger.c:3435
CommandId firing_counter
Definition: trigger.c:3565
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3715
CommandId ats_firing_id
Definition: trigger.c:3402
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3387
static AfterTriggersData afterTriggers
Definition: trigger.c:3609

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

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

◆ afterTriggerRestoreEventList()

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

Definition at line 3852 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

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

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

5442 {
5443  Relation rel = relinfo->ri_RelationDesc;
5444  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
5445  AfterTriggerEventData new_event;
5446  AfterTriggerSharedData new_shared;
5447  char relkind = rel->rd_rel->relkind;
5448  int tgtype_event;
5449  int tgtype_level;
5450  int i;
5451  Tuplestorestate *fdw_tuplestore = NULL;
5452 
5453  /*
5454  * Check state. We use a normal test not Assert because it is possible to
5455  * reach here in the wrong state given misconfigured RI triggers, in
5456  * particular deferring a cascade action trigger.
5457  */
5458  if (afterTriggers.query_depth < 0)
5459  elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
5460 
5461  /* Be sure we have enough space to record events at this query depth. */
5464 
5465  /*
5466  * If the directly named relation has any triggers with transition tables,
5467  * then we need to capture transition tuples.
5468  */
5469  if (row_trigger && transition_capture != NULL)
5470  {
5471  HeapTuple original_insert_tuple = transition_capture->tcs_original_insert_tuple;
5472  TupleConversionMap *map = transition_capture->tcs_map;
5473  bool delete_old_table = transition_capture->tcs_delete_old_table;
5474  bool update_old_table = transition_capture->tcs_update_old_table;
5475  bool update_new_table = transition_capture->tcs_update_new_table;
5476  bool insert_new_table = transition_capture->tcs_insert_new_table;;
5477 
5478  /*
5479  * For INSERT events newtup should be non-NULL, for DELETE events
5480  * oldtup should be non-NULL, whereas for UPDATE events normally both
5481  * oldtup and newtup are non-NULL. But for UPDATE events fired for
5482  * capturing transition tuples during UPDATE partition-key row
5483  * movement, oldtup is NULL when the event is for a row being inserted,
5484  * whereas newtup is NULL when the event is for a row being deleted.
5485  */
5486  Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
5487  oldtup == NULL));
5488  Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
5489  newtup == NULL));
5490 
5491  if (oldtup != NULL &&
5492  ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
5493  (event == TRIGGER_EVENT_UPDATE && update_old_table)))
5494  {
5495  Tuplestorestate *old_tuplestore;
5496 
5497  old_tuplestore = transition_capture->tcs_private->old_tuplestore;
5498 
5499  if (map != NULL)
5500  {
5501  HeapTuple converted = do_convert_tuple(oldtup, map);
5502 
5503  tuplestore_puttuple(old_tuplestore, converted);
5504  pfree(converted);
5505  }
5506  else
5507  tuplestore_puttuple(old_tuplestore, oldtup);
5508  }
5509  if (newtup != NULL &&
5510  ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
5511  (event == TRIGGER_EVENT_UPDATE && update_new_table)))
5512  {
5513  Tuplestorestate *new_tuplestore;
5514 
5515  new_tuplestore = transition_capture->tcs_private->new_tuplestore;
5516 
5517  if (original_insert_tuple != NULL)
5518  tuplestore_puttuple(new_tuplestore, original_insert_tuple);
5519  else if (map != NULL)
5520  {
5521  HeapTuple converted = do_convert_tuple(newtup, map);
5522 
5523  tuplestore_puttuple(new_tuplestore, converted);
5524  pfree(converted);
5525  }
5526  else
5527  tuplestore_puttuple(new_tuplestore, newtup);
5528  }
5529 
5530  /*
5531  * If transition tables are the only reason we're here, return. As
5532  * mentioned above, we can also be here during update tuple routing in
5533  * presence of transition tables, in which case this function is called
5534  * separately for oldtup and newtup, so we expect exactly one of them
5535  * to be NULL.
5536  */
5537  if (trigdesc == NULL ||
5538  (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
5539  (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
5540  (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
5541  (event == TRIGGER_EVENT_UPDATE && ((oldtup == NULL) ^ (newtup == NULL))))
5542  return;
5543  }
5544 
5545  /*
5546  * Validate the event code and collect the associated tuple CTIDs.
5547  *
5548  * The event code will be used both as a bitmask and an array offset, so
5549  * validation is important to make sure we don't walk off the edge of our
5550  * arrays.
5551  *
5552  * Also, if we're considering statement-level triggers, check whether we
5553  * already queued a set of them for this event, and cancel the prior set
5554  * if so. This preserves the behavior that statement-level triggers fire
5555  * just once per statement and fire after row-level triggers.
5556  */
5557  switch (event)
5558  {
5559  case TRIGGER_EVENT_INSERT:
5560  tgtype_event = TRIGGER_TYPE_INSERT;
5561  if (row_trigger)
5562  {
5563  Assert(oldtup == NULL);
5564  Assert(newtup != NULL);
5565  ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid1));
5566  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5567  }
5568  else
5569  {
5570  Assert(oldtup == NULL);
5571  Assert(newtup == NULL);
5572  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5573  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5575  CMD_INSERT, event);
5576  }
5577  break;
5578  case TRIGGER_EVENT_DELETE:
5579  tgtype_event = TRIGGER_TYPE_DELETE;
5580  if (row_trigger)
5581  {
5582  Assert(oldtup != NULL);
5583  Assert(newtup == NULL);
5584  ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
5585  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5586  }
5587  else
5588  {
5589  Assert(oldtup == NULL);
5590  Assert(newtup == NULL);
5591  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5592  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5594  CMD_DELETE, event);
5595  }
5596  break;
5597  case TRIGGER_EVENT_UPDATE:
5598  tgtype_event = TRIGGER_TYPE_UPDATE;
5599  if (row_trigger)
5600  {
5601  Assert(oldtup != NULL);
5602  Assert(newtup != NULL);
5603  ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
5604  ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid2));
5605  }
5606  else
5607  {
5608  Assert(oldtup == NULL);
5609  Assert(newtup == NULL);
5610  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5611  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5613  CMD_UPDATE, event);
5614  }
5615  break;
5617  tgtype_event = TRIGGER_TYPE_TRUNCATE;
5618  Assert(oldtup == NULL);
5619  Assert(newtup == NULL);
5620  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5621  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5622  break;
5623  default:
5624  elog(ERROR, "invalid after-trigger event code: %d", event);
5625  tgtype_event = 0; /* keep compiler quiet */
5626  break;
5627  }
5628 
5629  if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
5630  new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ?
5632  /* else, we'll initialize ate_flags for each trigger */
5633 
5634  tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
5635 
5636  for (i = 0; i < trigdesc->numtriggers; i++)
5637  {
5638  Trigger *trigger = &trigdesc->triggers[i];
5639 
5640  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
5641  tgtype_level,
5643  tgtype_event))
5644  continue;
5645  if (!TriggerEnabled(estate, relinfo, trigger, event,
5646  modifiedCols, oldtup, newtup))
5647  continue;
5648 
5649  if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
5650  {
5651  if (fdw_tuplestore == NULL)
5652  {
5653  fdw_tuplestore = GetCurrentFDWTuplestore();
5654  new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH;
5655  }
5656  else
5657  /* subsequent event for the same tuple */
5658  new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE;
5659  }
5660 
5661  /*
5662  * If the trigger is a foreign key enforcement trigger, there are
5663  * certain cases where we can skip queueing the event because we can
5664  * tell by inspection that the FK constraint will still pass.
5665  */
5666  if (TRIGGER_FIRED_BY_UPDATE(event))
5667  {
5668  switch (RI_FKey_trigger_type(trigger->tgfoid))
5669  {
5670  case RI_TRIGGER_PK:
5671  /* Update on trigger's PK table */
5672  if (!RI_FKey_pk_upd_check_required(trigger, rel,
5673  oldtup, newtup))
5674  {
5675  /* skip queuing this event */
5676  continue;
5677  }
5678  break;
5679 
5680  case RI_TRIGGER_FK:
5681  /* Update on trigger's FK table */
5682  if (!RI_FKey_fk_upd_check_required(trigger, rel,
5683  oldtup, newtup))
5684  {
5685  /* skip queuing this event */
5686  continue;
5687  }
5688  break;
5689 
5690  case RI_TRIGGER_NONE:
5691  /* Not an FK trigger */
5692  break;
5693  }
5694  }
5695 
5696  /*
5697  * If the trigger is a deferred unique constraint check trigger, only
5698  * queue it if the unique constraint was potentially violated, which
5699  * we know from index insertion time.
5700  */
5701  if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
5702  {
5703  if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
5704  continue; /* Uniqueness definitely not violated */
5705  }
5706 
5707  /*
5708  * Fill in event structure and add it to the current query's queue.
5709  * Note we set ats_table to NULL whenever this trigger doesn't use
5710  * transition tables, to improve sharability of the shared event data.
5711  */
5712  new_shared.ats_event =
5713  (event & TRIGGER_EVENT_OPMASK) |
5714  (row_trigger ? TRIGGER_EVENT_ROW : 0) |
5715  (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
5716  (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
5717  new_shared.ats_tgoid = trigger->tgoid;
5718  new_shared.ats_relid = RelationGetRelid(rel);
5719  new_shared.ats_firing_id = 0;
5720  if ((trigger->tgoldtable || trigger->tgnewtable) &&
5721  transition_capture != NULL)
5722  new_shared.ats_table = transition_capture->tcs_private;
5723  else
5724  new_shared.ats_table = NULL;
5725 
5727  &new_event, &new_shared);
5728  }
5729 
5730  /*
5731  * Finally, spool any foreign tuple(s). The tuplestore squashes them to
5732  * minimal tuples, so this loses any system columns. The executor lost
5733  * those columns before us, for an unrelated reason, so this is fine.
5734  */
5735  if (fdw_tuplestore)
5736  {
5737  if (oldtup != NULL)
5738  tuplestore_puttuple(fdw_tuplestore, oldtup);
5739  if (newtup != NULL)
5740  tuplestore_puttuple(fdw_tuplestore, newtup);
5741  }
5742 }
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
TriggerEvent ats_event
Definition: trigger.c:3399
Relation ri_RelationDesc
Definition: execnodes.h:368
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3271
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3389
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:114
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3633
ItemPointerData ate_ctid2
Definition: trigger.c:3412
#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:3410
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:5795
#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:3571
Tuplestorestate * old_tuplestore
Definition: trigger.c:3605
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:936
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:3391
Tuplestorestate * new_tuplestore
Definition: trigger.c:3606
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3392
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:380
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:3403
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, HeapTuple oldtup, HeapTuple newtup)
Definition: trigger.c:3156
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4951
#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:688
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3715
#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:3390
CommandId ats_firing_id
Definition: trigger.c:3402
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:3411
#define RI_TRIGGER_NONE
Definition: trigger.h:268
char * tgoldtable
Definition: reltrigger.h:42
AfterTriggerEventList events
Definition: trigger.c:3582
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:122
static AfterTriggersData afterTriggers
Definition: trigger.c:3609
#define RelationGetRelid(relation)
Definition: rel.h:425
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:139

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

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

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

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

5750 {
5751  bool result;
5752  AfterTriggersTableData *table;
5753 
5754  /* Check state, like AfterTriggerSaveEvent. */
5755  if (afterTriggers.query_depth < 0)
5756  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
5757 
5758  /* Be sure we have enough space to record events at this query depth. */
5761 
5762  /*
5763  * We keep this state in the AfterTriggersTableData that also holds
5764  * transition tables for the relation + operation. In this way, if we are
5765  * forced to make a new set of transition tables because more tuples get
5766  * entered after we've already fired triggers, we will allow a new set of
5767  * statement triggers to get queued.
5768  */
5769  table = GetAfterTriggersTableData(relid, cmdType);
5770  result = table->before_trig_done;
5771  table->before_trig_done = true;
5772  return result;
5773 }
#define ERROR
Definition: elog.h:43
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4353
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4951
#define elog
Definition: elog.h:219
static AfterTriggersData afterTriggers
Definition: trigger.c:3609

◆ cancel_prior_stmt_triggers()

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

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

5796 {
5797  AfterTriggersTableData *table;
5799 
5800  /*
5801  * We keep this state in the AfterTriggersTableData that also holds
5802  * transition tables for the relation + operation. In this way, if we are
5803  * forced to make a new set of transition tables because more tuples get
5804  * entered after we've already fired triggers, we will allow a new set of
5805  * statement triggers to get queued without canceling the old ones.
5806  */
5807  table = GetAfterTriggersTableData(relid, cmdType);
5808 
5809  if (table->after_trig_done)
5810  {
5811  /*
5812  * We want to start scanning from the tail location that existed just
5813  * before we inserted any statement triggers. But the events list
5814  * might've been entirely empty then, in which case scan from the
5815  * current head.
5816  */
5817  AfterTriggerEvent event;
5818  AfterTriggerEventChunk *chunk;
5819 
5820  if (table->after_trig_events.tail)
5821  {
5822  chunk = table->after_trig_events.tail;
5823  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
5824  }
5825  else
5826  {
5827  chunk = qs->events.head;
5828  event = NULL;
5829  }
5830 
5831  for_each_chunk_from(chunk)
5832  {
5833  if (event == NULL)
5834  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
5835  for_each_event_from(event, chunk)
5836  {
5837  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5838 
5839  /*
5840  * Exit loop when we reach events that aren't AS triggers for
5841  * the target relation.
5842  */
5843  if (evtshared->ats_relid != relid)
5844  goto done;
5845  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
5846  goto done;
5847  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
5848  goto done;
5849  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
5850  goto done;
5851  /* OK, mark it DONE */
5852  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
5853  event->ate_flags |= AFTER_TRIGGER_DONE;
5854  }
5855  /* signal we must reinitialize event ptr for next chunk */
5856  event = NULL;
5857  }
5858  }
5859 done:
5860 
5861  /* In any case, save current insertion point for next time */
5862  table->after_trig_done = true;
5863  table->after_trig_events = qs->events;
5864 }
TriggerEvent ats_event
Definition: trigger.c:3399
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3386
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3454
AfterTriggersQueryData * query_stack
Definition: trigger.c:3571
AfterTriggerEventChunk * tail
Definition: trigger.c:3460
#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:3435
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4353
AfterTriggerEventList after_trig_events
Definition: trigger.c:3604
#define for_each_chunk_from(cptr)
Definition: trigger.c:3476
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3478
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3406
AfterTriggerEventChunk * head
Definition: trigger.c:3459
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3387
AfterTriggerEventList events
Definition: trigger.c:3582
static AfterTriggersData afterTriggers
Definition: trigger.c:3609

◆ 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:2084
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:10435
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2991
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2075
char fk_matchtype
Definition: parsenodes.h:2121
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2079
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define gettext_noop(x)
Definition: c.h:1025
Definition: nodes.h:513
#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:336
bool initdeferred
Definition: parsenodes.h:2094
AlterTableType subtype
Definition: parsenodes.h:1803
List * pk_attrs
Definition: parsenodes.h:2120
char * conname
Definition: parsenodes.h:2092
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:2379
void pfree(void *pointer)
Definition: mcxt.c:936
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:2093
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:1717
MemoryContext TopMemoryContext
Definition: mcxt.c:43
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2077
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:2082
bool canSetTag
Definition: plannodes.h:53
void * palloc0(Size size)
Definition: mcxt.c:864
CmdType commandType
Definition: plannodes.h:45
#define InvalidOid
Definition: postgres_ext.h:36
bool initially_valid
Definition: parsenodes.h:2130
#define NOTICE
Definition: elog.h:37
#define makeNode(_type_)
Definition: nodes.h:561
char fk_del_action
Definition: parsenodes.h:2123
#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:1715
int i
RangeVar * relation
Definition: parsenodes.h:2363
ConstrType contype
Definition: parsenodes.h:2089
void * arg
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2076
#define lthird(l)
Definition: pg_list.h:121
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:626
RangeVar * pktable
Definition: parsenodes.h:2118
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2078
Definition: pg_list.h:45
bool skip_validation
Definition: parsenodes.h:2129
#define _(x)
Definition: elog.c:84
List * fk_attrs
Definition: parsenodes.h:2119
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:421
char fk_upd_action
Definition: parsenodes.h:2122

◆ 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:301
char * pstrdup(const char *in)
Definition: mcxt.c:1063
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:835
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_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(), get_rel_relkind(), get_relkind_objtype(), 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, OBJECT_FUNCTION, 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, get_relkind_objtype(rel->rd_rel->relkind),
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, get_relkind_objtype(get_rel_relkind(constrrelid)),
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, OBJECT_FUNCTION,
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:301
#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:539
#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:2372
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1805
#define RelationRelationId
Definition: pg_class.h:29
int2vector * buildint2vector(const int16 *int2s, int n)
Definition: int.c:110
#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:434
#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:513
#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:594
#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:1444
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:2379
#define TRIGGER_TYPE_INSTEAD
Definition: pg_trigger.h:104
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3352
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:936
#define OPAQUEOID
Definition: pg_type.h:700
#define ObjectIdGetDatum(X)
Definition: postgres.h:490
#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:256
#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
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define CStringGetDatum(X)
Definition: postgres.h:561
void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior)
Definition: dependency.c:1363
#define RelationGetRelationName(relation)
Definition: rel.h:445
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
const char * p_sourcetext
Definition: parse_node.h:173
#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:2375
#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:533
#define Anum_pg_trigger_tgconstrindid
Definition: pg_trigger.h:86
AclResult
Definition: acl.h:178
uintptr_t Datum
Definition: postgres.h:365
#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:385
Relation heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: heapam.c:1319
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:688
#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:399
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4628
#define DatumGetPointer(X)
Definition: postgres.h:532
#define TRIGGER_TYPE_BEFORE
Definition: pg_trigger.h:99
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
Definition: parse_func.c:1961
#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:835
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:4652
int i
RangeVar * relation
Definition: parsenodes.h:2363
ObjectType get_relkind_objtype(char relkind)
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:4281
#define ConstraintRelationId
Definition: pg_constraint.h:29
#define elog
Definition: elog.h:219
#define CONSTRAINT_TRIGGER
#define copyObject(obj)
Definition: nodes.h:626
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:1730
#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:174

◆ EnableDisableTrigger()

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