PostgreSQL Source Code  git master
trigger.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/partition.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/bitmapset.h"
#include "nodes/makefuncs.h"
#include "optimizer/optimizer.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tuplestore.h"
Include dependency graph for trigger.c:

Go to the source code of this file.

Data Structures

struct  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 GetAllUpdatedColumns(relinfo, estate)
 
#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 bool GetTupleForTrigger (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, TupleTableSlot **newSlot)
 
static bool TriggerEnabled (EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
static HeapTuple ExecCallTriggerFunc (TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
 
static void AfterTriggerSaveEvent (EState *estate, ResultRelInfo *relinfo, int event, bool row_trigger, TupleTableSlot *oldtup, TupleTableSlot *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, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
 
void RemoveTriggerById (Oid trigOid)
 
Oid get_trigger_oid (Oid relid, const char *trigname, bool missing_ok)
 
static void RangeVarCallbackForRenameTrigger (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
ObjectAddress renametrig (RenameStmt *stmt)
 
void EnableDisableTrigger (Relation rel, const char *tgname, char fires_when, bool skip_system, LOCKMODE lockmode)
 
void RelationBuildTriggers (Relation relation)
 
TriggerDescCopyTriggerDesc (TriggerDesc *trigdesc)
 
void FreeTriggerDesc (TriggerDesc *trigdesc)
 
const char * FindTriggerIncompatibleWithInheritance (TriggerDesc *trigdesc)
 
void ExecBSInsertTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASInsertTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecARInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
bool ExecIRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecBSDeleteTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASDeleteTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRDeleteTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot)
 
void ExecARDeleteTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
 
bool ExecIRDeleteTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
 
void ExecBSUpdateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASUpdateTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRUpdateTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot)
 
void ExecARUpdateTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
bool ExecIRUpdateTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
 
void ExecBSTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
static void AfterTriggerExecute (EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, 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 3625 of file trigger.c.

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3627 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 3699 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 3710 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:3688
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3662
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3640

Definition at line 3701 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:3662
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3640

Definition at line 3712 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

◆ GetAllUpdatedColumns

#define GetAllUpdatedColumns (   relinfo,
  estate 
)
Value:
(bms_union(exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->updatedCols, \
exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->extraUpdatedCols))
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:537
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:225

Definition at line 78 of file trigger.c.

Referenced by ExecARUpdateTriggers(), ExecASUpdateTriggers(), ExecBRUpdateTriggers(), and ExecBSUpdateTriggers().

◆ GetTriggerSharedData

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

◆ 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:3627
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3625
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3626

Definition at line 3662 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3640 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3629 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3793 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3795 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3794 of file trigger.c.

◆ SetConstraintState

Definition at line 3577 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3556 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3617 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

3954 {
3955  Size eventsize = SizeofTriggerEvent(event);
3956  Size needed = eventsize + sizeof(AfterTriggerSharedData);
3957  AfterTriggerEventChunk *chunk;
3958  AfterTriggerShared newshared;
3959  AfterTriggerEvent newevent;
3960 
3961  /*
3962  * If empty list or not enough room in the tail chunk, make a new chunk.
3963  * We assume here that a new shared record will always be needed.
3964  */
3965  chunk = events->tail;
3966  if (chunk == NULL ||
3967  chunk->endfree - chunk->freeptr < needed)
3968  {
3969  Size chunksize;
3970 
3971  /* Create event context if we didn't already */
3972  if (afterTriggers.event_cxt == NULL)
3975  "AfterTriggerEvents",
3977 
3978  /*
3979  * Chunk size starts at 1KB and is allowed to increase up to 1MB.
3980  * These numbers are fairly arbitrary, though there is a hard limit at
3981  * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
3982  * shared records using the available space in ate_flags. Another
3983  * constraint is that if the chunk size gets too huge, the search loop
3984  * below would get slow given a (not too common) usage pattern with
3985  * many distinct event types in a chunk. Therefore, we double the
3986  * preceding chunk size only if there weren't too many shared records
3987  * in the preceding chunk; otherwise we halve it. This gives us some
3988  * ability to adapt to the actual usage pattern of the current query
3989  * while still having large chunk sizes in typical usage. All chunk
3990  * sizes used should be MAXALIGN multiples, to ensure that the shared
3991  * records will be aligned safely.
3992  */
3993 #define MIN_CHUNK_SIZE 1024
3994 #define MAX_CHUNK_SIZE (1024*1024)
3995 
3996 #if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
3997 #error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
3998 #endif
3999 
4000  if (chunk == NULL)
4001  chunksize = MIN_CHUNK_SIZE;
4002  else
4003  {
4004  /* preceding chunk size... */
4005  chunksize = chunk->endptr - (char *) chunk;
4006  /* check number of shared records in preceding chunk */
4007  if ((chunk->endptr - chunk->endfree) <=
4008  (100 * sizeof(AfterTriggerSharedData)))
4009  chunksize *= 2; /* okay, double it */
4010  else
4011  chunksize /= 2; /* too many shared records */
4012  chunksize = Min(chunksize, MAX_CHUNK_SIZE);
4013  }
4014  chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize);
4015  chunk->next = NULL;
4016  chunk->freeptr = CHUNK_DATA_START(chunk);
4017  chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
4018  Assert(chunk->endfree - chunk->freeptr >= needed);
4019 
4020  if (events->head == NULL)
4021  events->head = chunk;
4022  else
4023  events->tail->next = chunk;
4024  events->tail = chunk;
4025  /* events->tailfree is now out of sync, but we'll fix it below */
4026  }
4027 
4028  /*
4029  * Try to locate a matching shared-data record already in the chunk. If
4030  * none, make a new one.
4031  */
4032  for (newshared = ((AfterTriggerShared) chunk->endptr) - 1;
4033  (char *) newshared >= chunk->endfree;
4034  newshared--)
4035  {
4036  if (newshared->ats_tgoid == evtshared->ats_tgoid &&
4037  newshared->ats_relid == evtshared->ats_relid &&
4038  newshared->ats_event == evtshared->ats_event &&
4039  newshared->ats_table == evtshared->ats_table &&
4040  newshared->ats_firing_id == 0)
4041  break;
4042  }
4043  if ((char *) newshared < chunk->endfree)
4044  {
4045  *newshared = *evtshared;
4046  newshared->ats_firing_id = 0; /* just to be sure */
4047  chunk->endfree = (char *) newshared;
4048  }
4049 
4050  /* Insert the data */
4051  newevent = (AfterTriggerEvent) chunk->freeptr;
4052  memcpy(newevent, event, eventsize);
4053  /* ... and link the new event to its shared record */
4054  newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
4055  newevent->ate_flags |= (char *) newshared - (char *) newevent;
4056 
4057  chunk->freeptr += eventsize;
4058  events->tailfree = chunk->freeptr;
4059 }
TriggerEvent ats_event
Definition: trigger.c:3633
#define AllocSetContextCreate
Definition: memutils.h:170
MemoryContext TopTransactionContext
Definition: mcxt.c:49
#define MIN_CHUNK_SIZE
TriggerFlags ate_flags
Definition: trigger.c:3644
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3688
struct AfterTriggerSharedData AfterTriggerSharedData
#define Min(x, y)
Definition: c.h:904
AfterTriggerEventChunk * tail
Definition: trigger.c:3694
struct AfterTriggerEventChunk * next
Definition: trigger.c:3681
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3662
#define AFTER_TRIGGER_OFFSET
Definition: trigger.c:3619
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3637
#define Assert(condition)
Definition: c.h:732
size_t Size
Definition: c.h:466
CommandId ats_firing_id
Definition: trigger.c:3636
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3640
#define MAX_CHUNK_SIZE
AfterTriggerEventChunk * head
Definition: trigger.c:3693
MemoryContext event_cxt
Definition: trigger.c:3802
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4785 of file trigger.c.

References AfterTriggersData::query_depth.

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

4786 {
4787  /* Increase the query stack depth */
4789 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

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

5054 {
5055  int my_level = GetCurrentTransactionNestLevel();
5056 
5057  /*
5058  * Allocate more space in the trans_stack if needed. (Note: because the
5059  * minimum nest level of a subtransaction is 2, we waste the first couple
5060  * entries of the array; not worth the notational effort to avoid it.)
5061  */
5062  while (my_level >= afterTriggers.maxtransdepth)
5063  {
5064  if (afterTriggers.maxtransdepth == 0)
5065  {
5066  /* Arbitrarily initialize for max of 8 subtransaction levels */
5069  8 * sizeof(AfterTriggersTransData));
5071  }
5072  else
5073  {
5074  /* repalloc will keep the stack in the same context */
5075  int new_alloc = afterTriggers.maxtransdepth * 2;
5076 
5079  new_alloc * sizeof(AfterTriggersTransData));
5080  afterTriggers.maxtransdepth = new_alloc;
5081  }
5082  }
5083 
5084  /*
5085  * Push the current information into the stack. The SET CONSTRAINTS state
5086  * is not saved until/unless changed. Likewise, we don't make a
5087  * per-subtransaction event context until needed.
5088  */
5089  afterTriggers.trans_stack[my_level].state = NULL;
5093 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3810
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggerEventList events
Definition: trigger.c:3825
CommandId firing_counter
Definition: trigger.c:3827
CommandId firing_counter
Definition: trigger.c:3799
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:842
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
AfterTriggerEventList events
Definition: trigger.c:3801
static AfterTriggersData afterTriggers
Definition: trigger.c:3844
SetConstraintState state
Definition: trigger.c:3824

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

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

4754 {
4755  /*
4756  * Initialize after-trigger state structure to empty
4757  */
4758  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4760 
4761  /*
4762  * Verify that there is no leftover state remaining. If these assertions
4763  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4764  * up properly.
4765  */
4766  Assert(afterTriggers.state == NULL);
4767  Assert(afterTriggers.query_stack == NULL);
4769  Assert(afterTriggers.event_cxt == NULL);
4770  Assert(afterTriggers.events.head == NULL);
4771  Assert(afterTriggers.trans_stack == NULL);
4773 }
uint32 CommandId
Definition: c.h:521
AfterTriggersTransData * trans_stack
Definition: trigger.c:3810
AfterTriggersQueryData * query_stack
Definition: trigger.c:3805
SetConstraintState state
Definition: trigger.c:3800
CommandId firing_counter
Definition: trigger.c:3799
#define Assert(condition)
Definition: c.h:732
AfterTriggerEventChunk * head
Definition: trigger.c:3693
MemoryContext event_cxt
Definition: trigger.c:3802
AfterTriggerEventList events
Definition: trigger.c:3801
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

3907 {
3908  Oid tgoid = evtshared->ats_tgoid;
3910  int i;
3911 
3912  /*
3913  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3914  * constraints declared NOT DEFERRABLE), the state is always false.
3915  */
3916  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3917  return false;
3918 
3919  /*
3920  * If constraint state exists, SET CONSTRAINTS might have been executed
3921  * either for this trigger or for all triggers.
3922  */
3923  if (state != NULL)
3924  {
3925  /* Check for SET CONSTRAINTS for this specific trigger. */
3926  for (i = 0; i < state->numstates; i++)
3927  {
3928  if (state->trigstates[i].sct_tgoid == tgoid)
3929  return state->trigstates[i].sct_tgisdeferred;
3930  }
3931 
3932  /* Check for SET CONSTRAINTS ALL. */
3933  if (state->all_isset)
3934  return state->all_isdeferred;
3935  }
3936 
3937  /*
3938  * Otherwise return the default state for the trigger.
3939  */
3940  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3941 }
TriggerEvent ats_event
Definition: trigger.c:3633
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:114
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:113
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3574
unsigned int Oid
Definition: postgres_ext.h:31
SetConstraintState state
Definition: trigger.c:3800
Definition: regguts.h:298
int i
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

4130 {
4131  AfterTriggerEventChunk *target = qs->events.head;
4132  ListCell *lc;
4133 
4134  Assert(target && target->next);
4135 
4136  /*
4137  * First, update any pointers in the per-table data, so that they won't be
4138  * dangling. Resetting obsoleted pointers to NULL will make
4139  * cancel_prior_stmt_triggers start from the list head, which is fine.
4140  */
4141  foreach(lc, qs->tables)
4142  {
4144 
4145  if (table->after_trig_done &&
4146  table->after_trig_events.tail == target)
4147  {
4148  table->after_trig_events.head = NULL;
4149  table->after_trig_events.tail = NULL;
4150  table->after_trig_events.tailfree = NULL;
4151  }
4152  }
4153 
4154  /* Now we can flush the head chunk */
4155  qs->events.head = target->next;
4156  pfree(target);
4157 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3694
struct AfterTriggerEventChunk * next
Definition: trigger.c:3681
void pfree(void *pointer)
Definition: mcxt.c:1056
AfterTriggerEventList after_trig_events
Definition: trigger.c:3838
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
AfterTriggerEventChunk * head
Definition: trigger.c:3693
AfterTriggerEventList events
Definition: trigger.c:3816

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 4805 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(), ExecuteTruncateGuts(), and standard_ExecutorFinish().

4806 {
4808 
4809  /* Must be inside a query, too */
4811 
4812  /*
4813  * If we never even got as far as initializing the event stack, there
4814  * certainly won't be any events, so exit quickly.
4815  */
4817  {
4819  return;
4820  }
4821 
4822  /*
4823  * Process all immediate-mode triggers queued by the query, and move the
4824  * deferred ones to the main list of deferred events.
4825  *
4826  * Notice that we decide which ones will be fired, and put the deferred
4827  * ones on the main list, before anything is actually fired. This ensures
4828  * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
4829  * IMMEDIATE: all events we have decided to defer will be available for it
4830  * to fire.
4831  *
4832  * We loop in case a trigger queues more events at the same query level.
4833  * Ordinary trigger functions, including all PL/pgSQL trigger functions,
4834  * will instead fire any triggers in a dedicated query level. Foreign key
4835  * enforcement triggers do add to the current query level, thanks to their
4836  * passing fire_triggers = false to SPI_execute_snapshot(). Other
4837  * C-language triggers might do likewise.
4838  *
4839  * If we find no firable events, we don't have to increment
4840  * firing_counter.
4841  */
4843 
4844  for (;;)
4845  {
4847  {
4848  CommandId firing_id = afterTriggers.firing_counter++;
4849  AfterTriggerEventChunk *oldtail = qs->events.tail;
4850 
4851  if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
4852  break; /* all fired */
4853 
4854  /*
4855  * Firing a trigger could result in query_stack being repalloc'd,
4856  * so we must recalculate qs after each afterTriggerInvokeEvents
4857  * call. Furthermore, it's unsafe to pass delete_ok = true here,
4858  * because that could cause afterTriggerInvokeEvents to try to
4859  * access qs->events after the stack has been repalloc'd.
4860  */
4862 
4863  /*
4864  * We'll need to scan the events list again. To reduce the cost
4865  * of doing so, get rid of completely-fired chunks. We know that
4866  * all events were marked IN_PROGRESS or DONE at the conclusion of
4867  * afterTriggerMarkEvents, so any still-interesting events must
4868  * have been added after that, and so must be in the chunk that
4869  * was then the tail chunk, or in later chunks. So, zap all
4870  * chunks before oldtail. This is approximately the same set of
4871  * events we would have gotten rid of by passing delete_ok = true.
4872  */
4873  Assert(oldtail != NULL);
4874  while (qs->events.head != oldtail)
4876  }
4877  else
4878  break;
4879  }
4880 
4881  /* Release query-level-local storage, including tuplestores if any */
4883 
4885 }
uint32 CommandId
Definition: c.h:521
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition: trigger.c:4129
AfterTriggersQueryData * query_stack
Definition: trigger.c:3805
AfterTriggerEventChunk * tail
Definition: trigger.c:3694
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4462
CommandId firing_counter
Definition: trigger.c:3799
#define Assert(condition)
Definition: c.h:732
AfterTriggerEventChunk * head
Definition: trigger.c:3693
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4390
AfterTriggerEventList events
Definition: trigger.c:3801
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4896
AfterTriggerEventList events
Definition: trigger.c:3816
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

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

5102 {
5103  int my_level = GetCurrentTransactionNestLevel();
5105  AfterTriggerEvent event;
5106  AfterTriggerEventChunk *chunk;
5107  CommandId subxact_firing_id;
5108 
5109  /*
5110  * Pop the prior state if needed.
5111  */
5112  if (isCommit)
5113  {
5114  Assert(my_level < afterTriggers.maxtransdepth);
5115  /* If we saved a prior state, we don't need it anymore */
5116  state = afterTriggers.trans_stack[my_level].state;
5117  if (state != NULL)
5118  pfree(state);
5119  /* this avoids double pfree if error later: */
5120  afterTriggers.trans_stack[my_level].state = NULL;
5123  }
5124  else
5125  {
5126  /*
5127  * Aborting. It is possible subxact start failed before calling
5128  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5129  * trans_stack levels that aren't there.
5130  */
5131  if (my_level >= afterTriggers.maxtransdepth)
5132  return;
5133 
5134  /*
5135  * Release query-level storage for queries being aborted, and restore
5136  * query_depth to its pre-subxact value. This assumes that a
5137  * subtransaction will not add events to query levels started in a
5138  * earlier transaction state.
5139  */
5141  {
5145  }
5148 
5149  /*
5150  * Restore the global deferred-event list to its former length,
5151  * discarding any events queued by the subxact.
5152  */
5154  &afterTriggers.trans_stack[my_level].events);
5155 
5156  /*
5157  * Restore the trigger state. If the saved state is NULL, then this
5158  * subxact didn't save it, so it doesn't need restoring.
5159  */
5160  state = afterTriggers.trans_stack[my_level].state;
5161  if (state != NULL)
5162  {
5164  afterTriggers.state = state;
5165  }
5166  /* this avoids double pfree if error later: */
5167  afterTriggers.trans_stack[my_level].state = NULL;
5168 
5169  /*
5170  * Scan for any remaining deferred events that were marked DONE or IN
5171  * PROGRESS by this subxact or a child, and un-mark them. We can
5172  * recognize such events because they have a firing ID greater than or
5173  * equal to the firing_counter value we saved at subtransaction start.
5174  * (This essentially assumes that the current subxact includes all
5175  * subxacts started after it.)
5176  */
5177  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5179  {
5180  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5181 
5182  if (event->ate_flags &
5184  {
5185  if (evtshared->ats_firing_id >= subxact_firing_id)
5186  event->ate_flags &=
5188  }
5189  }
5190  }
5191 }
uint32 CommandId
Definition: c.h:521
AfterTriggersTransData * trans_stack
Definition: trigger.c:3810
TriggerFlags ate_flags
Definition: trigger.c:3644
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3620
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3706
AfterTriggerEventList events
Definition: trigger.c:3825
AfterTriggersQueryData * query_stack
Definition: trigger.c:3805
CommandId firing_counter
Definition: trigger.c:3827
#define GetTriggerSharedData(evt)
Definition: trigger.c:3669
void pfree(void *pointer)
Definition: mcxt.c:1056
SetConstraintState state
Definition: trigger.c:3800
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:842
#define Assert(condition)
Definition: c.h:732
Definition: regguts.h:298
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:4089
CommandId ats_firing_id
Definition: trigger.c:3636
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3621
AfterTriggerEventList events
Definition: trigger.c:3801
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4896
static AfterTriggersData afterTriggers
Definition: trigger.c:3844
SetConstraintState state
Definition: trigger.c:3824

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

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

5006 {
5007  /*
5008  * Forget the pending-events list.
5009  *
5010  * Since all the info is in TopTransactionContext or children thereof, we
5011  * don't really need to do anything to reclaim memory. However, the
5012  * pending-events list could be large, and so it's useful to discard it as
5013  * soon as possible --- especially if we are aborting because we ran out
5014  * of memory for the list!
5015  */
5017  {
5019  afterTriggers.event_cxt = NULL;
5020  afterTriggers.events.head = NULL;
5021  afterTriggers.events.tail = NULL;
5022  afterTriggers.events.tailfree = NULL;
5023  }
5024 
5025  /*
5026  * Forget any subtransaction state as well. Since this can't be very
5027  * large, we let the eventual reset of TopTransactionContext free the
5028  * memory instead of doing it here.
5029  */
5030  afterTriggers.trans_stack = NULL;
5032 
5033 
5034  /*
5035  * Forget the query stack and constraint-related state information. As
5036  * with the subtransaction state information, we don't bother freeing the
5037  * memory here.
5038  */
5039  afterTriggers.query_stack = NULL;
5041  afterTriggers.state = NULL;
5042 
5043  /* No more afterTriggers manipulation until next transaction starts. */
5045 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
AfterTriggersTransData * trans_stack
Definition: trigger.c:3810
AfterTriggersQueryData * query_stack
Definition: trigger.c:3805
AfterTriggerEventChunk * tail
Definition: trigger.c:3694
SetConstraintState state
Definition: trigger.c:3800
AfterTriggerEventChunk * head
Definition: trigger.c:3693
MemoryContext event_cxt
Definition: trigger.c:3802
AfterTriggerEventList events
Definition: trigger.c:3801
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

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

5204 {
5205  int init_depth = afterTriggers.maxquerydepth;
5206 
5208 
5209  if (afterTriggers.maxquerydepth == 0)
5210  {
5211  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5212 
5215  new_alloc * sizeof(AfterTriggersQueryData));
5216  afterTriggers.maxquerydepth = new_alloc;
5217  }
5218  else
5219  {
5220  /* repalloc will keep the stack in the same context */
5221  int old_alloc = afterTriggers.maxquerydepth;
5222  int new_alloc = Max(afterTriggers.query_depth + 1,
5223  old_alloc * 2);
5224 
5227  new_alloc * sizeof(AfterTriggersQueryData));
5228  afterTriggers.maxquerydepth = new_alloc;
5229  }
5230 
5231  /* Initialize new array entries to empty */
5232  while (init_depth < afterTriggers.maxquerydepth)
5233  {
5235 
5236  qs->events.head = NULL;
5237  qs->events.tail = NULL;
5238  qs->events.tailfree = NULL;
5239  qs->fdw_tuplestore = NULL;
5240  qs->tables = NIL;
5241 
5242  ++init_depth;
5243  }
5244 }
#define NIL
Definition: pg_list.h:65
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggersQueryData * query_stack
Definition: trigger.c:3805
AfterTriggerEventChunk * tail
Definition: trigger.c:3694
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3817
#define Max(x, y)
Definition: c.h:898
#define Assert(condition)
Definition: c.h:732
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069
AfterTriggerEventChunk * head
Definition: trigger.c:3693
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
AfterTriggerEventList events
Definition: trigger.c:3816
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ AfterTriggerExecute()

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

Definition at line 4183 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(), ExecClearTuple(), ExecFetchSlotHeapTuple(), ExecGetTriggerNewSlot(), ExecGetTriggerOldSlot(), GetCurrentFDWTuplestore(), GetTriggerSharedData, heap_freetuple(), InstrStartNode(), InstrStopNode(), ItemPointerIsValid, MemoryContextReset(), AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, ResultRelInfo::ri_RelationDesc, SnapshotAny, T_TriggerData, table_tuple_fetch_row_version(), TriggerData::tg_event, TriggerData::tg_newslot, TriggerData::tg_newtable, TriggerData::tg_newtuple, TriggerData::tg_oldtable, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, 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().

4191 {
4192  Relation rel = relInfo->ri_RelationDesc;
4193  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4194  Oid tgoid = evtshared->ats_tgoid;
4195  TriggerData LocTriggerData;
4196  HeapTuple rettuple;
4197  int tgindx;
4198  bool should_free_trig = false;
4199  bool should_free_new = false;
4200 
4201  /*
4202  * Locate trigger in trigdesc.
4203  */
4204  LocTriggerData.tg_trigger = NULL;
4205  LocTriggerData.tg_trigslot = NULL;
4206  LocTriggerData.tg_newslot = NULL;
4207 
4208  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4209  {
4210  if (trigdesc->triggers[tgindx].tgoid == tgoid)
4211  {
4212  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4213  break;
4214  }
4215  }
4216  if (LocTriggerData.tg_trigger == NULL)
4217  elog(ERROR, "could not find trigger %u", tgoid);
4218 
4219  /*
4220  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4221  * to include time spent re-fetching tuples in the trigger cost.
4222  */
4223  if (instr)
4224  InstrStartNode(instr + tgindx);
4225 
4226  /*
4227  * Fetch the required tuple(s).
4228  */
4229  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4230  {
4232  {
4233  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4234 
4235  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4236  trig_tuple_slot1))
4237  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4238 
4239  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4241  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4242  trig_tuple_slot2))
4243  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4244  }
4245  /* fall through */
4247 
4248  /*
4249  * Store tuple in the slot so that tg_trigtuple does not reference
4250  * tuplestore memory. (It is formally possible for the trigger
4251  * function to queue trigger events that add to the same
4252  * tuplestore, which can push other tuples out of memory.) The
4253  * distinction is academic, because we start with a minimal tuple
4254  * that is stored as a heap tuple, constructed in different memory
4255  * context, in the slot anyway.
4256  */
4257  LocTriggerData.tg_trigslot = trig_tuple_slot1;
4258  LocTriggerData.tg_trigtuple =
4259  ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
4260 
4261  LocTriggerData.tg_newslot = trig_tuple_slot2;
4262  LocTriggerData.tg_newtuple =
4263  ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4265  ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new) : NULL;
4266 
4267  break;
4268 
4269  default:
4270  if (ItemPointerIsValid(&(event->ate_ctid1)))
4271  {
4272  LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
4273 
4274  if (!table_tuple_fetch_row_version(rel, &(event->ate_ctid1),
4275  SnapshotAny,
4276  LocTriggerData.tg_trigslot))
4277  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4278  LocTriggerData.tg_trigtuple =
4279  ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, &should_free_trig);
4280  }
4281  else
4282  {
4283  LocTriggerData.tg_trigtuple = NULL;
4284  }
4285 
4286  /* don't touch ctid2 if not there */
4287  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4289  ItemPointerIsValid(&(event->ate_ctid2)))
4290  {
4291  LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
4292 
4293  if (!table_tuple_fetch_row_version(rel, &(event->ate_ctid2),
4294  SnapshotAny,
4295  LocTriggerData.tg_newslot))
4296  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4297  LocTriggerData.tg_newtuple =
4298  ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, &should_free_new);
4299  }
4300  else
4301  {
4302  LocTriggerData.tg_newtuple = NULL;
4303  }
4304  }
4305 
4306  /*
4307  * Set up the tuplestore information to let the trigger have access to
4308  * transition tables. When we first make a transition table available to
4309  * a trigger, mark it "closed" so that it cannot change anymore. If any
4310  * additional events of the same type get queued in the current trigger
4311  * query level, they'll go into new transition tables.
4312  */
4313  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4314  if (evtshared->ats_table)
4315  {
4316  if (LocTriggerData.tg_trigger->tgoldtable)
4317  {
4318  LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4319  evtshared->ats_table->closed = true;
4320  }
4321 
4322  if (LocTriggerData.tg_trigger->tgnewtable)
4323  {
4324  LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4325  evtshared->ats_table->closed = true;
4326  }
4327  }
4328 
4329  /*
4330  * Setup the remaining trigger information
4331  */
4332  LocTriggerData.type = T_TriggerData;
4333  LocTriggerData.tg_event =
4335  LocTriggerData.tg_relation = rel;
4336 
4337  MemoryContextReset(per_tuple_context);
4338 
4339  /*
4340  * Call the trigger and throw away any possibly returned updated tuple.
4341  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4342  */
4343  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4344  tgindx,
4345  finfo,
4346  NULL,
4347  per_tuple_context);
4348  if (rettuple != NULL &&
4349  rettuple != LocTriggerData.tg_trigtuple &&
4350  rettuple != LocTriggerData.tg_newtuple)
4351  heap_freetuple(rettuple);
4352 
4353  /*
4354  * Release resources
4355  */
4356  if (should_free_trig)
4357  heap_freetuple(LocTriggerData.tg_trigtuple);
4358  if (should_free_new)
4359  heap_freetuple(LocTriggerData.tg_newtuple);
4360 
4361  if (LocTriggerData.tg_trigslot)
4362  ExecClearTuple(LocTriggerData.tg_trigslot);
4363  if (LocTriggerData.tg_newslot)
4364  ExecClearTuple(LocTriggerData.tg_newslot);
4365 
4366  /*
4367  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4368  * one "tuple returned" (really the number of firings).
4369  */
4370  if (instr)
4371  InstrStopNode(instr + tgindx, 1);
4372 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
TriggerEvent ats_event
Definition: trigger.c:3633
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:76
TupleTableSlot * tg_trigslot
Definition: trigger.h:38
Relation ri_RelationDesc
Definition: execnodes.h:411
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3623
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3870
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:426
ItemPointerData ate_ctid2
Definition: trigger.c:3646
TriggerFlags ate_flags
Definition: trigger.c:3644
Oid tgoid
Definition: reltrigger.h:25
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3627
Tuplestorestate * old_tuplestore
Definition: trigger.c:3839
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1104
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:102
TupleTableSlot * ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1126
HeapTuple tg_trigtuple
Definition: trigger.h:35
#define GetTriggerSharedData(evt)
Definition: trigger.c:3669
#define ERROR
Definition: elog.h:43
void InstrStartNode(Instrumentation *instr)
Definition: instrument.c:63
Trigger * triggers
Definition: reltrigger.h:48
TupleTableSlot * tg_newslot
Definition: trigger.h:39
Tuplestorestate * new_tuplestore
Definition: trigger.c:3840
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3626
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1609
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1021
int numtriggers
Definition: reltrigger.h:49
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:3637
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:3624
#define SnapshotAny
Definition: snapmgr.h:69
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:100
Tuplestorestate * tg_oldtable
Definition: trigger.h:40
NodeTag type
Definition: trigger.h:32
#define elog(elevel,...)
Definition: elog.h:226
Tuplestorestate * tg_newtable
Definition: trigger.h:41
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition: trigger.c:2373
ItemPointerData ate_ctid1
Definition: trigger.c:3645
char * tgoldtable
Definition: reltrigger.h:42
Relation tg_relation
Definition: trigger.h:34

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

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

4950 {
4951  AfterTriggerEventList *events;
4952  bool snap_pushed = false;
4953 
4954  /* Must not be inside a query */
4956 
4957  /*
4958  * If there are any triggers to fire, make sure we have set a snapshot for
4959  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4960  * can't assume ActiveSnapshot is valid on entry.)
4961  */
4962  events = &afterTriggers.events;
4963  if (events->head != NULL)
4964  {
4966  snap_pushed = true;
4967  }
4968 
4969  /*
4970  * Run all the remaining triggers. Loop until they are all gone, in case
4971  * some trigger queues more for us to do.
4972  */
4973  while (afterTriggerMarkEvents(events, NULL, false))
4974  {
4975  CommandId firing_id = afterTriggers.firing_counter++;
4976 
4977  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4978  break; /* all fired */
4979  }
4980 
4981  /*
4982  * We don't bother freeing the event list, since it will go away anyway
4983  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4984  */
4985 
4986  if (snap_pushed)
4988 }
uint32 CommandId
Definition: c.h:521
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4462
void PopActiveSnapshot(void)
Definition: snapmgr.c:814
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:306
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:735
CommandId firing_counter
Definition: trigger.c:3799
#define Assert(condition)
Definition: c.h:732
AfterTriggerEventChunk * head
Definition: trigger.c:3693
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4390
AfterTriggerEventList events
Definition: trigger.c:3801
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4068 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

4069 {
4070  AfterTriggerEventChunk *chunk;
4071 
4072  while ((chunk = events->head) != NULL)
4073  {
4074  events->head = chunk->next;
4075  pfree(chunk);
4076  }
4077  events->tail = NULL;
4078  events->tailfree = NULL;
4079 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3694
struct AfterTriggerEventChunk * next
Definition: trigger.c:3681
void pfree(void *pointer)
Definition: mcxt.c:1056
AfterTriggerEventChunk * head
Definition: trigger.c:3693

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

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

4897 {
4898  Tuplestorestate *ts;
4899  List *tables;
4900  ListCell *lc;
4901 
4902  /* Drop the trigger events */
4904 
4905  /* Drop FDW tuplestore if any */
4906  ts = qs->fdw_tuplestore;
4907  qs->fdw_tuplestore = NULL;
4908  if (ts)
4909  tuplestore_end(ts);
4910 
4911  /* Release per-table subsidiary storage */
4912  tables = qs->tables;
4913  foreach(lc, tables)
4914  {
4916 
4917  ts = table->old_tuplestore;
4918  table->old_tuplestore = NULL;
4919  if (ts)
4920  tuplestore_end(ts);
4921  ts = table->new_tuplestore;
4922  table->new_tuplestore = NULL;
4923  if (ts)
4924  tuplestore_end(ts);
4925  }
4926 
4927  /*
4928  * Now free the AfterTriggersTableData structs and list cells. Reset list
4929  * pointer first; if list_free_deep somehow gets an error, better to leak
4930  * that storage than have an infinite loop.
4931  */
4932  qs->tables = NIL;
4933  list_free_deep(tables);
4934 }
#define NIL
Definition: pg_list.h:65
Tuplestorestate * old_tuplestore
Definition: trigger.c:3839
void list_free_deep(List *list)
Definition: list.c:1391
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4068
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3817
Tuplestorestate * new_tuplestore
Definition: trigger.c:3840
#define lfirst(lc)
Definition: pg_list.h:190
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3816
Definition: pg_list.h:50

◆ afterTriggerInvokeEvents()

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

Definition at line 4462 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, EState::es_tupleTable, ExecCleanUpTriggerState(), ExecDropSingleTupleTableSlot(), ExecGetTriggerResultRel(), ExecResetTupleTable(), for_each_chunk, for_each_event, FreeExecutorState(), AfterTriggerEventChunk::freeptr, GetTriggerSharedData, MakeSingleTupleTableSlot(), MemoryContextDelete(), RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and TTSOpsMinimalTuple.

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

4466 {
4467  bool all_fired = true;
4468  AfterTriggerEventChunk *chunk;
4469  MemoryContext per_tuple_context;
4470  bool local_estate = false;
4471  ResultRelInfo *rInfo = NULL;
4472  Relation rel = NULL;
4473  TriggerDesc *trigdesc = NULL;
4474  FmgrInfo *finfo = NULL;
4475  Instrumentation *instr = NULL;
4476  TupleTableSlot *slot1 = NULL,
4477  *slot2 = NULL;
4478 
4479  /* Make a local EState if need be */
4480  if (estate == NULL)
4481  {
4482  estate = CreateExecutorState();
4483  local_estate = true;
4484  }
4485 
4486  /* Make a per-tuple memory context for trigger function calls */
4487  per_tuple_context =
4489  "AfterTriggerTupleContext",
4491 
4492  for_each_chunk(chunk, *events)
4493  {
4494  AfterTriggerEvent event;
4495  bool all_fired_in_chunk = true;
4496 
4497  for_each_event(event, chunk)
4498  {
4499  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4500 
4501  /*
4502  * Is it one for me to fire?
4503  */
4504  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4505  evtshared->ats_firing_id == firing_id)
4506  {
4507  /*
4508  * So let's fire it... but first, find the correct relation if
4509  * this is not the same relation as before.
4510  */
4511  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4512  {
4513  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4514  rel = rInfo->ri_RelationDesc;
4515  trigdesc = rInfo->ri_TrigDesc;
4516  finfo = rInfo->ri_TrigFunctions;
4517  instr = rInfo->ri_TrigInstrument;
4518  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4519  {
4520  if (slot1 != NULL)
4521  {
4524  }
4525  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4527  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4529  }
4530  if (trigdesc == NULL) /* should not happen */
4531  elog(ERROR, "relation %u has no triggers",
4532  evtshared->ats_relid);
4533  }
4534 
4535  /*
4536  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4537  * still set, so recursive examinations of the event list
4538  * won't try to re-fire it.
4539  */
4540  AfterTriggerExecute(estate, event, rInfo, trigdesc, finfo, instr,
4541  per_tuple_context, slot1, slot2);
4542 
4543  /*
4544  * Mark the event as done.
4545  */
4546  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4547  event->ate_flags |= AFTER_TRIGGER_DONE;
4548  }
4549  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4550  {
4551  /* something remains to be done */
4552  all_fired = all_fired_in_chunk = false;
4553  }
4554  }
4555 
4556  /* Clear the chunk if delete_ok and nothing left of interest */
4557  if (delete_ok && all_fired_in_chunk)
4558  {
4559  chunk->freeptr = CHUNK_DATA_START(chunk);
4560  chunk->endfree = chunk->endptr;
4561 
4562  /*
4563  * If it's last chunk, must sync event list's tailfree too. Note
4564  * that delete_ok must NOT be passed as true if there could be
4565  * additional AfterTriggerEventList values pointing at this event
4566  * list, since we'd fail to fix their copies of tailfree.
4567  */
4568  if (chunk == events->tail)
4569  events->tailfree = chunk->freeptr;
4570  }
4571  }
4572  if (slot1 != NULL)
4573  {
4576  }
4577 
4578  /* Release working resources */
4579  MemoryContextDelete(per_tuple_context);
4580 
4581  if (local_estate)
4582  {
4583  ExecCleanUpTriggerState(estate);
4584  ExecResetTupleTable(estate->es_tupleTable, false);
4585  FreeExecutorState(estate);
4586  }
4587 
4588  return all_fired;
4589 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:411
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define AllocSetContextCreate
Definition: memutils.h:170
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1203
TriggerFlags ate_flags
Definition: trigger.c:3644
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3620
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3688
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:432
AfterTriggerEventChunk * tail
Definition: trigger.c:3694
Form_pg_class rd_rel
Definition: rel.h:83
#define GetTriggerSharedData(evt)
Definition: trigger.c:3669
#define for_each_event(eptr, cptr)
Definition: trigger.c:3701
void FreeExecutorState(EState *estate)
Definition: execUtils.c:190
#define ERROR
Definition: elog.h:43
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1219
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:423
EState * CreateExecutorState(void)
Definition: execUtils.c:88
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1371
List * es_tupleTable
Definition: execnodes.h:552
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1156
TupleDesc rd_att
Definition: rel.h:84
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3699
CommandId ats_firing_id
Definition: trigger.c:3636
void ExecCleanUpTriggerState(EState *estate)
Definition: execMain.c:1454
#define elog(elevel,...)
Definition: elog.h:226
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3621
#define RelationGetRelid(relation)
Definition: rel.h:419
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:86
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:426
static void AfterTriggerExecute(EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
Definition: trigger.c:4183

◆ afterTriggerMarkEvents()

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

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

4393 {
4394  bool found = false;
4395  AfterTriggerEvent event;
4396  AfterTriggerEventChunk *chunk;
4397 
4398  for_each_event_chunk(event, chunk, *events)
4399  {
4400  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4401  bool defer_it = false;
4402 
4403  if (!(event->ate_flags &
4405  {
4406  /*
4407  * This trigger hasn't been called or scheduled yet. Check if we
4408  * should call it now.
4409  */
4410  if (immediate_only && afterTriggerCheckState(evtshared))
4411  {
4412  defer_it = true;
4413  }
4414  else
4415  {
4416  /*
4417  * Mark it as to be fired in this firing cycle.
4418  */
4420  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4421  found = true;
4422  }
4423  }
4424 
4425  /*
4426  * If it's deferred, move it to move_list, if requested.
4427  */
4428  if (defer_it && move_list != NULL)
4429  {
4430  /* add it to move_list */
4431  afterTriggerAddEvent(move_list, event, evtshared);
4432  /* mark original copy "done" so we don't do it again */
4433  event->ate_flags |= AFTER_TRIGGER_DONE;
4434  }
4435  }
4436 
4437  return found;
4438 }
TriggerFlags ate_flags
Definition: trigger.c:3644
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3620
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3706
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3906
#define GetTriggerSharedData(evt)
Definition: trigger.c:3669
CommandId firing_counter
Definition: trigger.c:3799
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3952
CommandId ats_firing_id
Definition: trigger.c:3636
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3621
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

5651 {
5652  AfterTriggerEvent event;
5653  AfterTriggerEventChunk *chunk;
5654  int depth;
5655 
5656  /* Scan queued events */
5658  {
5659  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5660 
5661  /*
5662  * We can ignore completed events. (Even if a DONE flag is rolled
5663  * back by subxact abort, it's OK because the effects of the TRUNCATE
5664  * or whatever must get rolled back too.)
5665  */
5666  if (event->ate_flags & AFTER_TRIGGER_DONE)
5667  continue;
5668 
5669  if (evtshared->ats_relid == relid)
5670  return true;
5671  }
5672 
5673  /*
5674  * Also scan events queued by incomplete queries. This could only matter
5675  * if TRUNCATE/etc is executed by a function or trigger within an updating
5676  * query on the same relation, which is pretty perverse, but let's check.
5677  */
5678  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5679  {
5681  {
5682  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5683 
5684  if (event->ate_flags & AFTER_TRIGGER_DONE)
5685  continue;
5686 
5687  if (evtshared->ats_relid == relid)
5688  return true;
5689  }
5690  }
5691 
5692  return false;
5693 }
TriggerFlags ate_flags
Definition: trigger.c:3644
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3620
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3706
AfterTriggersQueryData * query_stack
Definition: trigger.c:3805
#define GetTriggerSharedData(evt)
Definition: trigger.c:3669
AfterTriggerEventList events
Definition: trigger.c:3801
AfterTriggerEventList events
Definition: trigger.c:3816
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ afterTriggerRestoreEventList()

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

Definition at line 4089 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

4091 {
4092  AfterTriggerEventChunk *chunk;
4093  AfterTriggerEventChunk *next_chunk;
4094 
4095  if (old_events->tail == NULL)
4096  {
4097  /* restoring to a completely empty state, so free everything */
4098  afterTriggerFreeEventList(events);
4099  }
4100  else
4101  {
4102  *events = *old_events;
4103  /* free any chunks after the last one we want to keep */
4104  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
4105  {
4106  next_chunk = chunk->next;
4107  pfree(chunk);
4108  }
4109  /* and clean up the tail chunk to be the right length */
4110  events->tail->next = NULL;
4111  events->tail->freeptr = events->tailfree;
4112 
4113  /*
4114  * We don't make any effort to remove now-unused shared data records.
4115  * They might still be useful, anyway.
4116  */
4117  }
4118 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3694
struct AfterTriggerEventChunk * next
Definition: trigger.c:3681
void pfree(void *pointer)
Definition: mcxt.c:1056
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4068

◆ AfterTriggerSaveEvent()

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

Definition at line 5720 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, TupleConversionMap::attrMap, cancel_prior_stmt_triggers(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, EState::es_tupleTable, AfterTriggersQueryData::events, ExecAllocTableSlot(), execute_attr_map_slot(), GetCurrentFDWTuplestore(), i, ItemPointerCopy, ItemPointerSetInvalid, list_member_oid(), AfterTriggersData::maxquerydepth, AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, TupleConversionMap::outdesc, AfterTriggersData::query_depth, AfterTriggersData::query_stack, RelationData::rd_rel, RelationGetRelid, relkind, 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, AfterTriggersTableData::storeslot, 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_DELETE, TRIGGER_FIRED_BY_UPDATE, TriggerEnabled(), TriggerDesc::triggers, TupleTableSlot::tts_tid, TTSOpsVirtual, TupIsNull, and tuplestore_puttupleslot().

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

5725 {
5726  Relation rel = relinfo->ri_RelationDesc;
5727  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
5728  AfterTriggerEventData new_event;
5729  AfterTriggerSharedData new_shared;
5730  char relkind = rel->rd_rel->relkind;
5731  int tgtype_event;
5732  int tgtype_level;
5733  int i;
5734  Tuplestorestate *fdw_tuplestore = NULL;
5735 
5736  /*
5737  * Check state. We use a normal test not Assert because it is possible to
5738  * reach here in the wrong state given misconfigured RI triggers, in
5739  * particular deferring a cascade action trigger.
5740  */
5741  if (afterTriggers.query_depth < 0)
5742  elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
5743 
5744  /* Be sure we have enough space to record events at this query depth. */
5747 
5748  /*
5749  * If the directly named relation has any triggers with transition tables,
5750  * then we need to capture transition tuples.
5751  */
5752  if (row_trigger && transition_capture != NULL)
5753  {
5754  TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
5755  TupleConversionMap *map = transition_capture->tcs_map;
5756  bool delete_old_table = transition_capture->tcs_delete_old_table;
5757  bool update_old_table = transition_capture->tcs_update_old_table;
5758  bool update_new_table = transition_capture->tcs_update_new_table;
5759  bool insert_new_table = transition_capture->tcs_insert_new_table;
5760 
5761  /*
5762  * For INSERT events NEW should be non-NULL, for DELETE events OLD
5763  * should be non-NULL, whereas for UPDATE events normally both OLD and
5764  * NEW are non-NULL. But for UPDATE events fired for capturing
5765  * transition tuples during UPDATE partition-key row movement, OLD is
5766  * NULL when the event is for a row being inserted, whereas NEW is
5767  * NULL when the event is for a row being deleted.
5768  */
5769  Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
5770  TupIsNull(oldslot)));
5771  Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
5772  TupIsNull(newslot)));
5773 
5774  if (!TupIsNull(oldslot) &&
5775  ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
5776  (event == TRIGGER_EVENT_UPDATE && update_old_table)))
5777  {
5778  Tuplestorestate *old_tuplestore;
5779 
5780  old_tuplestore = transition_capture->tcs_private->old_tuplestore;
5781 
5782  if (map != NULL)
5783  {
5784  TupleTableSlot *storeslot;
5785 
5786  storeslot = transition_capture->tcs_private->storeslot;
5787  if (!storeslot)
5788  {
5789  storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
5790  map->outdesc,
5791  &TTSOpsVirtual);
5792  transition_capture->tcs_private->storeslot = storeslot;
5793  }
5794 
5795  execute_attr_map_slot(map->attrMap, oldslot, storeslot);
5796  tuplestore_puttupleslot(old_tuplestore, storeslot);
5797  }
5798  else
5799  tuplestore_puttupleslot(old_tuplestore, oldslot);
5800  }
5801  if (!TupIsNull(newslot) &&
5802  ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
5803  (event == TRIGGER_EVENT_UPDATE && update_new_table)))
5804  {
5805  Tuplestorestate *new_tuplestore;
5806 
5807  new_tuplestore = transition_capture->tcs_private->new_tuplestore;
5808 
5809  if (original_insert_tuple != NULL)
5810  tuplestore_puttupleslot(new_tuplestore,
5811  original_insert_tuple);
5812  else if (map != NULL)
5813  {
5814  TupleTableSlot *storeslot;
5815 
5816  storeslot = transition_capture->tcs_private->storeslot;
5817 
5818  if (!storeslot)
5819  {
5820  storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
5821  map->outdesc,
5822  &TTSOpsVirtual);
5823  transition_capture->tcs_private->storeslot = storeslot;
5824  }
5825 
5826  execute_attr_map_slot(map->attrMap, newslot, storeslot);
5827  tuplestore_puttupleslot(new_tuplestore, storeslot);
5828  }
5829  else
5830  tuplestore_puttupleslot(new_tuplestore, newslot);
5831  }
5832 
5833  /*
5834  * If transition tables are the only reason we're here, return. As
5835  * mentioned above, we can also be here during update tuple routing in
5836  * presence of transition tables, in which case this function is
5837  * called separately for oldtup and newtup, so we expect exactly one
5838  * of them to be NULL.
5839  */
5840  if (trigdesc == NULL ||
5841  (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
5842  (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
5843  (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
5844  (event == TRIGGER_EVENT_UPDATE && (TupIsNull(oldslot) ^ TupIsNull(newslot))))
5845  return;
5846  }
5847 
5848  /*
5849  * Validate the event code and collect the associated tuple CTIDs.
5850  *
5851  * The event code will be used both as a bitmask and an array offset, so
5852  * validation is important to make sure we don't walk off the edge of our
5853  * arrays.
5854  *
5855  * Also, if we're considering statement-level triggers, check whether we
5856  * already queued a set of them for this event, and cancel the prior set
5857  * if so. This preserves the behavior that statement-level triggers fire
5858  * just once per statement and fire after row-level triggers.
5859  */
5860  switch (event)
5861  {
5862  case TRIGGER_EVENT_INSERT:
5863  tgtype_event = TRIGGER_TYPE_INSERT;
5864  if (row_trigger)
5865  {
5866  Assert(oldslot == NULL);
5867  Assert(newslot != NULL);
5868  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
5869  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5870  }
5871  else
5872  {
5873  Assert(oldslot == NULL);
5874  Assert(newslot == NULL);
5875  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5876  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5878  CMD_INSERT, event);
5879  }
5880  break;
5881  case TRIGGER_EVENT_DELETE:
5882  tgtype_event = TRIGGER_TYPE_DELETE;
5883  if (row_trigger)
5884  {
5885  Assert(oldslot != NULL);
5886  Assert(newslot == NULL);
5887  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
5888  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5889  }
5890  else
5891  {
5892  Assert(oldslot == NULL);
5893  Assert(newslot == NULL);
5894  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5895  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5897  CMD_DELETE, event);
5898  }
5899  break;
5900  case TRIGGER_EVENT_UPDATE:
5901  tgtype_event = TRIGGER_TYPE_UPDATE;
5902  if (row_trigger)
5903  {
5904  Assert(oldslot != NULL);
5905  Assert(newslot != NULL);
5906  ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
5907  ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
5908  }
5909  else
5910  {
5911  Assert(oldslot == NULL);
5912  Assert(newslot == NULL);
5913  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5914  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5916  CMD_UPDATE, event);
5917  }
5918  break;
5920  tgtype_event = TRIGGER_TYPE_TRUNCATE;
5921  Assert(oldslot == NULL);
5922  Assert(newslot == NULL);
5923  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5924  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5925  break;
5926  default:
5927  elog(ERROR, "invalid after-trigger event code: %d", event);
5928  tgtype_event = 0; /* keep compiler quiet */
5929  break;
5930  }
5931 
5932  if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
5933  new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ?
5935  /* else, we'll initialize ate_flags for each trigger */
5936 
5937  tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
5938 
5939  for (i = 0; i < trigdesc->numtriggers; i++)
5940  {
5941  Trigger *trigger = &trigdesc->triggers[i];
5942 
5943  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
5944  tgtype_level,
5945  TRIGGER_TYPE_AFTER,
5946  tgtype_event))
5947  continue;
5948  if (!TriggerEnabled(estate, relinfo, trigger, event,
5949  modifiedCols, oldslot, newslot))
5950  continue;
5951 
5952  if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
5953  {
5954  if (fdw_tuplestore == NULL)
5955  {
5956  fdw_tuplestore = GetCurrentFDWTuplestore();
5957  new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH;
5958  }
5959  else
5960  /* subsequent event for the same tuple */
5961  new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE;
5962  }
5963 
5964  /*
5965  * If the trigger is a foreign key enforcement trigger, there are
5966  * certain cases where we can skip queueing the event because we can
5967  * tell by inspection that the FK constraint will still pass.
5968  */
5969  if (TRIGGER_FIRED_BY_UPDATE(event) || TRIGGER_FIRED_BY_DELETE(event))
5970  {
5971  switch (RI_FKey_trigger_type(trigger->tgfoid))
5972  {
5973  case RI_TRIGGER_PK:
5974  /* Update or delete on trigger's PK table */
5975  if (!RI_FKey_pk_upd_check_required(trigger, rel,
5976  oldslot, newslot))
5977  {
5978  /* skip queuing this event */
5979  continue;
5980  }
5981  break;
5982 
5983  case RI_TRIGGER_FK:
5984  /* Update on trigger's FK table */
5985  if (!RI_FKey_fk_upd_check_required(trigger, rel,
5986  oldslot, newslot))
5987  {
5988  /* skip queuing this event */
5989  continue;
5990  }
5991  break;
5992 
5993  case RI_TRIGGER_NONE:
5994  /* Not an FK trigger */
5995  break;
5996  }
5997  }
5998 
5999  /*
6000  * If the trigger is a deferred unique constraint check trigger, only
6001  * queue it if the unique constraint was potentially violated, which
6002  * we know from index insertion time.
6003  */
6004  if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
6005  {
6006  if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
6007  continue; /* Uniqueness definitely not violated */
6008  }
6009 
6010  /*
6011  * Fill in event structure and add it to the current query's queue.
6012  * Note we set ats_table to NULL whenever this trigger doesn't use
6013  * transition tables, to improve sharability of the shared event data.
6014  */
6015  new_shared.ats_event =
6016  (event & TRIGGER_EVENT_OPMASK) |
6017  (row_trigger ? TRIGGER_EVENT_ROW : 0) |
6018  (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
6019  (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
6020  new_shared.ats_tgoid = trigger->tgoid;
6021  new_shared.ats_relid = RelationGetRelid(rel);
6022  new_shared.ats_firing_id = 0;
6023  if ((trigger->tgoldtable || trigger->tgnewtable) &&
6024  transition_capture != NULL)
6025  new_shared.ats_table = transition_capture->tcs_private;
6026  else
6027  new_shared.ats_table = NULL;
6028 
6030  &new_event, &new_shared);
6031  }
6032 
6033  /*
6034  * Finally, spool any foreign tuple(s). The tuplestore squashes them to
6035  * minimal tuples, so this loses any system columns. The executor lost
6036  * those columns before us, for an unrelated reason, so this is fine.
6037  */
6038  if (fdw_tuplestore)
6039  {
6040  if (oldslot != NULL)
6041  tuplestore_puttupleslot(fdw_tuplestore, oldslot);
6042  if (newslot != NULL)
6043  tuplestore_puttupleslot(fdw_tuplestore, newslot);
6044  }
6045 }
AttrNumber * attrMap
Definition: tupconvert.h:26
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:708
TriggerEvent ats_event
Definition: trigger.c:3633
Relation ri_RelationDesc
Definition: execnodes.h:411
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:2868
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3623
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:114
TupleDesc outdesc
Definition: tupconvert.h:25
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: trigger.c:3427
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3870
ItemPointerData ate_ctid2
Definition: trigger.c:3646
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:99
Oid tgfoid
Definition: reltrigger.h:28
TriggerFlags ate_flags
Definition: trigger.c:3644
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:6098
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:113
Oid tgoid
Definition: reltrigger.h:25
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
AfterTriggersQueryData * query_stack
Definition: trigger.c:3805
Tuplestorestate * old_tuplestore
Definition: trigger.c:3839
TupleTableSlot * execute_attr_map_slot(AttrNumber *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:425
Form_pg_class rd_rel
Definition: rel.h:83
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:102
char relkind
Definition: pg_class.h:81
struct AfterTriggersTableData * tcs_private
Definition: trigger.h:87
#define ERROR
Definition: elog.h:43
TupleConversionMap * tcs_map
Definition: trigger.h:73
int16 tgtype
Definition: reltrigger.h:29
bool tgdeferrable
Definition: reltrigger.h:35
bool tginitdeferred
Definition: reltrigger.h:36
bool trig_delete_after_row
Definition: reltrigger.h:66
Trigger * triggers
Definition: reltrigger.h:48
TupleTableSlot * storeslot
Definition: trigger.c:3841
#define TupIsNull(slot)
Definition: tuptable.h:293
bool trig_insert_after_row
Definition: reltrigger.h:56
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3625
Tuplestorestate * new_tuplestore
Definition: trigger.c:3840
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3626
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:423
TupleTableSlot * ExecAllocTableSlot(List **tupleTable, TupleDesc desc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1136
bool trig_update_after_row
Definition: reltrigger.h:61
int numtriggers
Definition: reltrigger.h:49
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:119
List * es_tupleTable
Definition: execnodes.h:552
char * tgnewtable
Definition: reltrigger.h:43
bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: ri_triggers.c:1164
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3637
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:5203
#define RI_TRIGGER_FK
Definition: trigger.h:271
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:675
#define Assert(condition)
Definition: c.h:732
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3952
#define TRIGGER_EVENT_TRUNCATE
Definition: trigger.h:101
Oid tgconstrindid
Definition: reltrigger.h:33
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3624
CommandId ats_firing_id
Definition: trigger.c:3636
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:100
bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition: ri_triggers.c:1196
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:98
#define RI_TRIGGER_PK
Definition: trigger.h:270
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
#define elog(elevel,...)
Definition: elog.h:226
int i
ItemPointerData ate_ctid1
Definition: trigger.c:3645
#define RI_TRIGGER_NONE
Definition: trigger.h:272
char * tgoldtable
Definition: reltrigger.h:42
AfterTriggerEventList events
Definition: trigger.c:3816
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:122
static AfterTriggersData afterTriggers
Definition: trigger.c:3844
#define RelationGetRelid(relation)
Definition: rel.h:419
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:161
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:82

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5325 of file trigger.c.

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

Referenced by standard_ProcessUtility().

5326 {
5327  int my_level = GetCurrentTransactionNestLevel();
5328 
5329  /* If we haven't already done so, initialize our state. */
5330  if (afterTriggers.state == NULL)
5332 
5333  /*
5334  * If in a subtransaction, and we didn't save the current state already,
5335  * save it so it can be restored if the subtransaction aborts.
5336  */
5337  if (my_level > 1 &&
5338  afterTriggers.trans_stack[my_level].state == NULL)
5339  {
5340  afterTriggers.trans_stack[my_level].state =
5342  }
5343 
5344  /*
5345  * Handle SET CONSTRAINTS ALL ...
5346  */
5347  if (stmt->constraints == NIL)
5348  {
5349  /*
5350  * Forget any previous SET CONSTRAINTS commands in this transaction.
5351  */
5353 
5354  /*
5355  * Set the per-transaction ALL state to known.
5356  */
5357  afterTriggers.state->all_isset = true;
5359  }
5360  else
5361  {
5362  Relation conrel;
5363  Relation tgrel;
5364  List *conoidlist = NIL;
5365  List *tgoidlist = NIL;
5366  ListCell *lc;
5367 
5368  /*
5369  * Handle SET CONSTRAINTS constraint-name [, ...]
5370  *
5371  * First, identify all the named constraints and make a list of their
5372  * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5373  * the same name within a schema, the specifications are not
5374  * necessarily unique. Our strategy is to target all matching
5375  * constraints within the first search-path schema that has any
5376  * matches, but disregard matches in schemas beyond the first match.
5377  * (This is a bit odd but it's the historical behavior.)
5378  *
5379  * A constraint in a partitioned table may have corresponding
5380  * constraints in the partitions. Grab those too.
5381  */
5382  conrel = table_open(ConstraintRelationId, AccessShareLock);
5383 
5384  foreach(lc, stmt->constraints)
5385  {
5386  RangeVar *constraint = lfirst(lc);
5387  bool found;
5388  List *namespacelist;
5389  ListCell *nslc;
5390 
5391  if (constraint->catalogname)
5392  {
5393  if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5394  ereport(ERROR,
5395  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5396  errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5397  constraint->catalogname, constraint->schemaname,
5398  constraint->relname)));
5399  }
5400 
5401  /*
5402  * If we're given the schema name with the constraint, look only
5403  * in that schema. If given a bare constraint name, use the
5404  * search path to find the first matching constraint.
5405  */
5406  if (constraint->schemaname)
5407  {
5408  Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5409  false);
5410 
5411  namespacelist = list_make1_oid(namespaceId);
5412  }
5413  else
5414  {
5415  namespacelist = fetch_search_path(true);
5416  }
5417 
5418  found = false;
5419  foreach(nslc, namespacelist)
5420  {
5421  Oid namespaceId = lfirst_oid(nslc);
5422  SysScanDesc conscan;
5423  ScanKeyData skey[2];
5424  HeapTuple tup;
5425 
5426  ScanKeyInit(&skey[0],
5427  Anum_pg_constraint_conname,
5428  BTEqualStrategyNumber, F_NAMEEQ,
5429  CStringGetDatum(constraint->relname));
5430  ScanKeyInit(&skey[1],
5431  Anum_pg_constraint_connamespace,
5432  BTEqualStrategyNumber, F_OIDEQ,
5433  ObjectIdGetDatum(namespaceId));
5434 
5435  conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5436  true, NULL, 2, skey);
5437 
5438  while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5439  {
5441 
5442  if (con->condeferrable)
5443  conoidlist = lappend_oid(conoidlist, con->oid);
5444  else if (stmt->deferred)
5445  ereport(ERROR,
5446  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5447  errmsg("constraint \"%s\" is not deferrable",
5448  constraint->relname)));
5449  found = true;
5450  }
5451 
5452  systable_endscan(conscan);
5453 
5454  /*
5455  * Once we've found a matching constraint we do not search
5456  * later parts of the search path.
5457  */
5458  if (found)
5459  break;
5460  }
5461 
5462  list_free(namespacelist);
5463 
5464  /*
5465  * Not found ?
5466  */
5467  if (!found)
5468  ereport(ERROR,
5469  (errcode(ERRCODE_UNDEFINED_OBJECT),
5470  errmsg("constraint \"%s\" does not exist",
5471  constraint->relname)));
5472  }
5473 
5474  /*
5475  * Scan for any possible descendants of the constraints. We append
5476  * whatever we find to the same list that we're scanning; this has the
5477  * effect that we create new scans for those, too, so if there are
5478  * further descendents, we'll also catch them.
5479  */
5480  foreach(lc, conoidlist)
5481  {
5482  Oid parent = lfirst_oid(lc);
5483  ScanKeyData key;
5484  SysScanDesc scan;
5485  HeapTuple tuple;
5486 
5487  ScanKeyInit(&key,
5488  Anum_pg_constraint_conparentid,
5489  BTEqualStrategyNumber, F_OIDEQ,
5490  ObjectIdGetDatum(parent));
5491 
5492  scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5493 
5494  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5495  {
5497 
5498  conoidlist = lappend_oid(conoidlist, con->oid);
5499  }
5500 
5501  systable_endscan(scan);
5502  }
5503 
5504  table_close(conrel, AccessShareLock);
5505 
5506  /*
5507  * Now, locate the trigger(s) implementing each of these constraints,
5508  * and make a list of their OIDs.
5509  */
5510  tgrel = table_open(TriggerRelationId, AccessShareLock);
5511 
5512  foreach(lc, conoidlist)
5513  {
5514  Oid conoid = lfirst_oid(lc);
5515  bool found;
5516  ScanKeyData skey;
5517  SysScanDesc tgscan;
5518  HeapTuple htup;
5519 
5520  found = false;
5521 
5522  ScanKeyInit(&skey,
5523  Anum_pg_trigger_tgconstraint,
5524  BTEqualStrategyNumber, F_OIDEQ,
5525  ObjectIdGetDatum(conoid));
5526 
5527  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5528  NULL, 1, &skey);
5529 
5530  while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5531  {
5532  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5533 
5534  /*
5535  * Silently skip triggers that are marked as non-deferrable in
5536  * pg_trigger. This is not an error condition, since a
5537  * deferrable RI constraint may have some non-deferrable
5538  * actions.
5539  */
5540  if (pg_trigger->tgdeferrable)
5541  tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid);
5542 
5543  found = true;
5544  }
5545 
5546  systable_endscan(tgscan);
5547 
5548  /* Safety check: a deferrable constraint should have triggers */
5549  if (!found)
5550  elog(ERROR, "no triggers found for constraint with OID %u",
5551  conoid);
5552  }
5553 
5554  table_close(tgrel, AccessShareLock);
5555 
5556  /*
5557  * Now we can set the trigger states of individual triggers for this
5558  * xact.
5559  */
5560  foreach(lc, tgoidlist)
5561  {
5562  Oid tgoid = lfirst_oid(lc);
5564  bool found = false;
5565  int i;
5566 
5567  for (i = 0; i < state->numstates; i++)
5568  {
5569  if (state->trigstates[i].sct_tgoid == tgoid)
5570  {
5571  state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5572  found = true;
5573  break;
5574  }
5575  }
5576  if (!found)
5577  {
5579  SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5580  }
5581  }
5582  }
5583 
5584  /*
5585  * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5586  * checks against that constraint must be made when the SET CONSTRAINTS
5587  * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5588  * apply retroactively. We've updated the constraints state, so scan the
5589  * list of previously deferred events to fire any that have now become
5590  * immediate.
5591  *
5592  * Obviously, if this was SET ... DEFERRED then it can't have converted
5593  * any unfired events to immediate, so we need do nothing in that case.
5594  */
5595  if (!stmt->deferred)
5596  {
5598  bool snapshot_set = false;
5599 
5600  while (afterTriggerMarkEvents(events, NULL, true))
5601  {
5602  CommandId firing_id = afterTriggers.firing_counter++;
5603 
5604  /*
5605  * Make sure a snapshot has been established in case trigger
5606  * functions need one. Note that we avoid setting a snapshot if
5607  * we don't find at least one trigger that has to be fired now.
5608  * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
5609  * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
5610  * at the start of a transaction it's not possible for any trigger
5611  * events to be queued yet.)
5612  */
5613  if (!snapshot_set)
5614  {
5616  snapshot_set = true;
5617  }
5618 
5619  /*
5620  * We can delete fired events if we are at top transaction level,
5621  * but we'd better not if inside a subtransaction, since the
5622  * subtransaction could later get rolled back.
5623  */
5624  if (afterTriggerInvokeEvents(events, firing_id, NULL,
5625  !IsSubTransaction()))
5626  break; /* all fired */
5627  }
5628 
5629  if (snapshot_set)
5631  }
5632 }
#define NIL
Definition: pg_list.h:65
uint32 CommandId
Definition: c.h:521
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2885
AfterTriggersTransData * trans_stack
Definition: trigger.c:3810
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:525
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3574
#define AccessShareLock
Definition: lockdefs.h:36
int errcode(int sqlerrcode)
Definition: elog.c:570
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition: trigger.c:5295
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4462
void PopActiveSnapshot(void)
Definition: snapmgr.c:814
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:358
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:306
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:352
char * schemaname
Definition: primnodes.h:67
char * relname
Definition: primnodes.h:68
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:444
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
SetConstraintState state
Definition: trigger.c:3800
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2100
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:735
#define CStringGetDatum(X)
Definition: postgres.h:578
#define TriggerConstraintIndexId
Definition: indexing.h:254
#define ereport(elevel, rest)
Definition: elog.h:141
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:5250
#define list_make1_oid(x1)
Definition: pg_list.h:249
Oid MyDatabaseId
Definition: globals.c:85
CommandId firing_counter
Definition: trigger.c:3799
#define ConstraintNameNspIndexId
Definition: indexing.h:126
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:842
FormData_pg_constraint * Form_pg_constraint
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
Definition: regguts.h:298
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:71
bool IsSubTransaction(void)
Definition: xact.c:4706
int errmsg(const char *fmt,...)
Definition: elog.c:784
void list_free(List *list)
Definition: list.c:1377
#define elog(elevel,...)
Definition: elog.h:226
int i
#define ConstraintParentIndexId
Definition: indexing.h:134
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4390
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
AfterTriggerEventList events
Definition: trigger.c:3801
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
Definition: pg_list.h:50
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4305
static AfterTriggersData afterTriggers
Definition: trigger.c:3844
char * catalogname
Definition: primnodes.h:66
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define lfirst_oid(lc)
Definition: pg_list.h:192
SetConstraintState state
Definition: trigger.c:3824
static SetConstraintState SetConstraintStateCopy(SetConstraintState state)
Definition: trigger.c:5275

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

6053 {
6054  bool result;
6055  AfterTriggersTableData *table;
6056 
6057  /* Check state, like AfterTriggerSaveEvent. */
6058  if (afterTriggers.query_depth < 0)
6059  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6060 
6061  /* Be sure we have enough space to record events at this query depth. */
6064 
6065  /*
6066  * We keep this state in the AfterTriggersTableData that also holds
6067  * transition tables for the relation + operation. In this way, if we are
6068  * forced to make a new set of transition tables because more tuples get
6069  * entered after we've already fired triggers, we will allow a new set of
6070  * statement triggers to get queued.
6071  */
6072  table = GetAfterTriggersTableData(relid, cmdType);
6073  result = table->before_trig_done;
6074  table->before_trig_done = true;
6075  return result;
6076 }
#define ERROR
Definition: elog.h:43
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4605
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:5203
#define elog(elevel,...)
Definition: elog.h:226
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ cancel_prior_stmt_triggers()

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

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

6099 {
6100  AfterTriggersTableData *table;
6102 
6103  /*
6104  * We keep this state in the AfterTriggersTableData that also holds
6105  * transition tables for the relation + operation. In this way, if we are
6106  * forced to make a new set of transition tables because more tuples get
6107  * entered after we've already fired triggers, we will allow a new set of
6108  * statement triggers to get queued without canceling the old ones.
6109  */
6110  table = GetAfterTriggersTableData(relid, cmdType);
6111 
6112  if (table->after_trig_done)
6113  {
6114  /*
6115  * We want to start scanning from the tail location that existed just
6116  * before we inserted any statement triggers. But the events list
6117  * might've been entirely empty then, in which case scan from the
6118  * current head.
6119  */
6120  AfterTriggerEvent event;
6121  AfterTriggerEventChunk *chunk;
6122 
6123  if (table->after_trig_events.tail)
6124  {
6125  chunk = table->after_trig_events.tail;
6126  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6127  }
6128  else
6129  {
6130  chunk = qs->events.head;
6131  event = NULL;
6132  }
6133 
6134  for_each_chunk_from(chunk)
6135  {
6136  if (event == NULL)
6137  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6138  for_each_event_from(event, chunk)
6139  {
6140  AfterTriggerShared evtshared = GetTriggerSharedData(event);
6141 
6142  /*
6143  * Exit loop when we reach events that aren't AS triggers for
6144  * the target relation.
6145  */
6146  if (evtshared->ats_relid != relid)
6147  goto done;
6148  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6149  goto done;
6150  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6151  goto done;
6152  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6153  goto done;
6154  /* OK, mark it DONE */
6155  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6156  event->ate_flags |= AFTER_TRIGGER_DONE;
6157  }
6158  /* signal we must reinitialize event ptr for next chunk */
6159  event = NULL;
6160  }
6161  }
6162 done:
6163 
6164  /* In any case, save current insertion point for next time */
6165  table->after_trig_done = true;
6166  table->after_trig_events = qs->events;
6167 }
TriggerEvent ats_event
Definition: trigger.c:3633
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3620
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3688
AfterTriggersQueryData * query_stack
Definition: trigger.c:3805
AfterTriggerEventChunk * tail
Definition: trigger.c:3694
#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:3669
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4605
AfterTriggerEventList after_trig_events
Definition: trigger.c:3838
#define for_each_chunk_from(cptr)
Definition: trigger.c:3710
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3712
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3640
AfterTriggerEventChunk * head
Definition: trigger.c:3693
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3621
AfterTriggerEventList events
Definition: trigger.c:3816
static AfterTriggersData afterTriggers
Definition: trigger.c:3844

◆ ConvertTriggerToFK()

static void ConvertTriggerToFK ( CreateTrigStmt stmt,
Oid  funcoid 
)
static

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

1244 {
1245  static List *info_list = NIL;
1246 
1247  static const char *const funcdescr[3] = {
1248  gettext_noop("Found referenced table's UPDATE trigger."),
1249  gettext_noop("Found referenced table's DELETE trigger."),
1250  gettext_noop("Found referencing table's trigger.")
1251  };
1252 
1253  char *constr_name;
1254  char *fk_table_name;
1255  char *pk_table_name;
1256  char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1257  List *fk_attrs = NIL;
1258  List *pk_attrs = NIL;
1260  int funcnum;
1261  OldTriggerInfo *info = NULL;
1262  ListCell *l;
1263  int i;
1264 
1265  /* Parse out the trigger arguments */
1266  constr_name = strVal(linitial(stmt->args));
1267  fk_table_name = strVal(lsecond(stmt->args));
1268  pk_table_name = strVal(lthird(stmt->args));
1269  i = 0;
1270  foreach(l, stmt->args)
1271  {
1272  Value *arg = (Value *) lfirst(l);
1273 
1274  i++;
1275  if (i < 4) /* skip constraint and table names */
1276  continue;
1277  if (i == 4) /* handle match type */
1278  {
1279  if (strcmp(strVal(arg), "FULL") == 0)
1280  fk_matchtype = FKCONSTR_MATCH_FULL;
1281  else
1282  fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1283  continue;
1284  }
1285  if (i % 2)
1286  fk_attrs = lappend(fk_attrs, arg);
1287  else
1288  pk_attrs = lappend(pk_attrs, arg);
1289  }
1290 
1291  /* Prepare description of constraint for use in messages */
1292  initStringInfo(&buf);
1293  appendStringInfo(&buf, "FOREIGN KEY %s(",
1294  quote_identifier(fk_table_name));
1295  i = 0;
1296  foreach(l, fk_attrs)
1297  {
1298  Value *arg = (Value *) lfirst(l);
1299 
1300  if (i++ > 0)
1301  appendStringInfoChar(&buf, ',');
1303  }
1304  appendStringInfo(&buf, ") REFERENCES %s(",
1305  quote_identifier(pk_table_name));
1306  i = 0;
1307  foreach(l, pk_attrs)
1308  {
1309  Value *arg = (Value *) lfirst(l);
1310 
1311  if (i++ > 0)
1312  appendStringInfoChar(&buf, ',');
1314  }
1315  appendStringInfoChar(&buf, ')');
1316 
1317  /* Identify class of trigger --- update, delete, or referencing-table */
1318  switch (funcoid)
1319  {
1320  case F_RI_FKEY_CASCADE_UPD:
1321  case F_RI_FKEY_RESTRICT_UPD:
1322  case F_RI_FKEY_SETNULL_UPD:
1323  case F_RI_FKEY_SETDEFAULT_UPD:
1324  case F_RI_FKEY_NOACTION_UPD:
1325  funcnum = 0;
1326  break;
1327 
1328  case F_RI_FKEY_CASCADE_DEL:
1329  case F_RI_FKEY_RESTRICT_DEL:
1330  case F_RI_FKEY_SETNULL_DEL:
1331  case F_RI_FKEY_SETDEFAULT_DEL:
1332  case F_RI_FKEY_NOACTION_DEL:
1333  funcnum = 1;
1334  break;
1335 
1336  default:
1337  funcnum = 2;
1338  break;
1339  }
1340 
1341  /* See if we have a match to this trigger */
1342  foreach(l, info_list)
1343  {
1344  info = (OldTriggerInfo *) lfirst(l);
1345  if (info->funcoids[funcnum] == InvalidOid &&
1346  equal(info->args, stmt->args))
1347  {
1348  info->funcoids[funcnum] = funcoid;
1349  break;
1350  }
1351  }
1352 
1353  if (l == NULL)
1354  {
1355  /* First trigger of set, so create a new list entry */
1356  MemoryContext oldContext;
1357 
1358  ereport(NOTICE,
1359  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1360  constr_name, buf.data),
1361  errdetail_internal("%s", _(funcdescr[funcnum]))));
1363  info = (OldTriggerInfo *) palloc0(sizeof(OldTriggerInfo));
1364  info->args = copyObject(stmt->args);
1365  info->funcoids[funcnum] = funcoid;
1366  info_list = lappend(info_list, info);
1367  MemoryContextSwitchTo(oldContext);
1368  }
1369  else if (info->funcoids[0] == InvalidOid ||
1370  info->funcoids[1] == InvalidOid ||
1371  info->funcoids[2] == InvalidOid)
1372  {
1373  /* Second trigger of set */
1374  ereport(NOTICE,
1375  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1376  constr_name, buf.data),
1377  errdetail_internal("%s", _(funcdescr[funcnum]))));
1378  }
1379  else
1380  {
1381  /* OK, we have a set, so make the FK constraint ALTER TABLE cmd */
1384  Constraint *fkcon = makeNode(Constraint);
1385  PlannedStmt *wrapper = makeNode(PlannedStmt);
1386 
1387  ereport(NOTICE,
1388  (errmsg("converting trigger group into constraint \"%s\" %s",
1389  constr_name, buf.data),
1390  errdetail_internal("%s", _(funcdescr[funcnum]))));
1391  fkcon->contype = CONSTR_FOREIGN;
1392  fkcon->location = -1;
1393  if (funcnum == 2)
1394  {
1395  /* This trigger is on the FK table */
1396  atstmt->relation = stmt->relation;
1397  if (stmt->constrrel)
1398  fkcon->pktable = stmt->constrrel;
1399  else
1400  {
1401  /* Work around ancient pg_dump bug that omitted constrrel */
1402  fkcon->pktable = makeRangeVar(NULL, pk_table_name, -1);
1403  }
1404  }
1405  else
1406  {
1407  /* This trigger is on the PK table */
1408  fkcon->pktable = stmt->relation;
1409  if (stmt->constrrel)
1410  atstmt->relation = stmt->constrrel;
1411  else
1412  {
1413  /* Work around ancient pg_dump bug that omitted constrrel */
1414  atstmt->relation = makeRangeVar(NULL, fk_table_name, -1);
1415  }
1416  }
1417  atstmt->cmds = list_make1(atcmd);
1418  atstmt->relkind = OBJECT_TABLE;
1419  atcmd->subtype = AT_AddConstraint;
1420  atcmd->def = (Node *) fkcon;
1421  if (strcmp(constr_name, "<unnamed>") == 0)
1422  fkcon->conname = NULL;
1423  else
1424  fkcon->conname = constr_name;
1425  fkcon->fk_attrs = fk_attrs;
1426  fkcon->pk_attrs = pk_attrs;
1427  fkcon->fk_matchtype = fk_matchtype;
1428  switch (info->funcoids[0])
1429  {
1430  case F_RI_FKEY_NOACTION_UPD:
1432  break;
1433  case F_RI_FKEY_CASCADE_UPD:
1435  break;
1436  case F_RI_FKEY_RESTRICT_UPD:
1438  break;
1439  case F_RI_FKEY_SETNULL_UPD:
1441  break;
1442  case F_RI_FKEY_SETDEFAULT_UPD:
1444  break;
1445  default:
1446  /* can't get here because of earlier checks */
1447  elog(ERROR, "confused about RI update function");
1448  }
1449  switch (info->funcoids[1])
1450  {
1451  case F_RI_FKEY_NOACTION_DEL:
1453  break;
1454  case F_RI_FKEY_CASCADE_DEL:
1456  break;
1457  case F_RI_FKEY_RESTRICT_DEL:
1459  break;
1460  case F_RI_FKEY_SETNULL_DEL:
1462  break;
1463  case F_RI_FKEY_SETDEFAULT_DEL:
1465  break;
1466  default:
1467  /* can't get here because of earlier checks */
1468  elog(ERROR, "confused about RI delete function");
1469  }
1470  fkcon->deferrable = stmt->deferrable;
1471  fkcon->initdeferred = stmt->initdeferred;
1472  fkcon->skip_validation = false;
1473  fkcon->initially_valid = true;
1474 
1475  /* finally, wrap it in a dummy PlannedStmt */
1476  wrapper->commandType = CMD_UTILITY;
1477  wrapper->canSetTag = false;
1478  wrapper->utilityStmt = (Node *) atstmt;
1479  wrapper->stmt_location = -1;
1480  wrapper->stmt_len = -1;
1481 
1482  /* ... and execute it */
1483  ProcessUtility(wrapper,
1484  "(generated ALTER TABLE ADD FOREIGN KEY command)",
1485  PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1486  None_Receiver, NULL);
1487 
1488  /* Remove the matched item from the list */
1489  info_list = list_delete_ptr(info_list, info);
1490  pfree(info);
1491  /* We leak the copied args ... not worth worrying about */
1492  }
1493 }
#define NIL
Definition: pg_list.h:65
#define FKCONSTR_MATCH_SIMPLE
Definition: parsenodes.h:2124
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:10628
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3008
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2115
char fk_matchtype
Definition: parsenodes.h:2166
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2119
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define gettext_noop(x)
Definition: c.h:1117
Definition: nodes.h:525
#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:338
bool initdeferred
Definition: parsenodes.h:2134
AlterTableType subtype
Definition: parsenodes.h:1840
List * pk_attrs
Definition: parsenodes.h:2165
char * conname
Definition: parsenodes.h:2132
List * list_delete_ptr(List *list, void *datum)
Definition: list.c:797
DestReceiver * None_Receiver
Definition: dest.c:96
int stmt_len
Definition: plannodes.h:94
#define lsecond(l)
Definition: pg_list.h:200
int errdetail_internal(const char *fmt,...)
Definition: elog.c:887
#define list_make1(x1)
Definition: pg_list.h:227
RangeVar * constrrel
Definition: parsenodes.h:2424
void pfree(void *pointer)
Definition: mcxt.c:1056
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define linitial(l)
Definition: pg_list.h:195
#define ERROR
Definition: elog.h:43
bool deferrable
Definition: parsenodes.h:2133
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:163
int stmt_location
Definition: plannodes.h:93
static char * buf
Definition: pg_test_fsync.c:68
Node * utilityStmt
Definition: plannodes.h:90
Oid funcoids[3]
Definition: trigger.c:1238
#define ereport(elevel, rest)
Definition: elog.h:141
ObjectType relkind
Definition: parsenodes.h:1755
MemoryContext TopMemoryContext
Definition: mcxt.c:44
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2117
List * lappend(List *list, void *datum)
Definition: list.c:322
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:175
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
#define FKCONSTR_MATCH_FULL
Definition: parsenodes.h:2122
bool canSetTag
Definition: plannodes.h:54
void * palloc0(Size size)
Definition: mcxt.c:980
CmdType commandType
Definition: plannodes.h:46
#define InvalidOid
Definition: postgres_ext.h:36
bool initially_valid
Definition: parsenodes.h:2175
#define NOTICE
Definition: elog.h:37
#define makeNode(_type_)
Definition: nodes.h:573
char fk_del_action
Definition: parsenodes.h:2168
#define lfirst(lc)
Definition: pg_list.h:190
Definition: value.h:42
List * args
Definition: trigger.c:1237
int errmsg(const char *fmt,...)
Definition: elog.c:784
RangeVar * relation
Definition: parsenodes.h:1753
#define elog(elevel,...)
Definition: elog.h:226
int i
RangeVar * relation
Definition: parsenodes.h:2408
ConstrType contype
Definition: parsenodes.h:2129
void * arg
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2116
#define lthird(l)
Definition: pg_list.h:205
#define copyObject(obj)
Definition: nodes.h:641
RangeVar * pktable
Definition: parsenodes.h:2163
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2118
Definition: pg_list.h:50
bool skip_validation
Definition: parsenodes.h:2174
#define _(x)
Definition: elog.c:84
List * fk_attrs
Definition: parsenodes.h:2164
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:420
char fk_upd_action
Definition: parsenodes.h:2167

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

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

2159 {
2160  TriggerDesc *newdesc;
2161  Trigger *trigger;
2162  int i;
2163 
2164  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2165  return NULL;
2166 
2167  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
2168  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2169 
2170  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2171  memcpy(trigger, trigdesc->triggers,
2172  trigdesc->numtriggers * sizeof(Trigger));
2173  newdesc->triggers = trigger;
2174 
2175  for (i = 0; i < trigdesc->numtriggers; i++)
2176  {
2177  trigger->tgname = pstrdup(trigger->tgname);
2178  if (trigger->tgnattr > 0)
2179  {
2180  int16 *newattr;
2181 
2182  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2183  memcpy(newattr, trigger->tgattr,
2184  trigger->tgnattr * sizeof(int16));
2185  trigger->tgattr = newattr;
2186  }
2187  if (trigger->tgnargs > 0)
2188  {
2189  char **newargs;
2190  int16 j;
2191 
2192  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2193  for (j = 0; j < trigger->tgnargs; j++)
2194  newargs[j] = pstrdup(trigger->tgargs[j]);
2195  trigger->tgargs = newargs;
2196  }
2197  if (trigger->tgqual)
2198  trigger->tgqual = pstrdup(trigger->tgqual);
2199  if (trigger->tgoldtable)
2200  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2201  if (trigger->tgnewtable)
2202  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2203  trigger++;
2204  }
2205 
2206  return newdesc;
2207 }
signed short int16
Definition: c.h:345
char * pstrdup(const char *in)
Definition: mcxt.c:1186
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:949
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,
Oid  funcoid,
Oid  parentTriggerOid,
Node whenClause,
bool  isInternal,
bool  in_partition 
)

Definition at line 162 of file trigger.c.

References AccessShareLock, ACL_EXECUTE, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addRangeTableEntryForRelation(), addRTEtoQuery(), ALLOCSET_SMALL_SIZES, AllocSetContextCreate, allowSystemTableMods, generate_unaccent_rules::args, CreateTrigStmt::args, Assert, assign_expr_collations(), attnameAttNum(), attnum, BoolGetDatum, BTEqualStrategyNumber, buildint2vector(), byteain(), CacheInvalidateRelcacheByTuple(), CatalogTupleInsert(), CatalogTupleUpdate(), CharGetDatum, ObjectAddress::classId, CreateTrigStmt::columns, CommandCounterIncrement(), CreateTrigStmt::constrrel, ConvertTriggerToFK(), copyObject, CreateConstraintEntry(), CreateTrigger(), CStringGetDatum, CStringGetTextDatum, CurrentMemoryContext, DatumGetPointer, CreateTrigStmt::deferrable, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, DirectFunctionCall1, elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errhint(), errmsg(), ERROR, CreateTrigStmt::events, EXPR_KIND_TRIGGER_WHEN, find_all_inheritors(), find_inheritance_children(), forboth, free_parsestate(), CreateTrigStmt::funcname, get_func_rettype(), get_rel_name(), get_rel_relkind(), get_relkind_objtype(), GetNewOidWithIndex(), GETSTRUCT, GetUserId(), has_superclass(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, IndexGetRelation(), CreateTrigStmt::initdeferred, Int16GetDatum, InvalidAttrNumber, InvalidObjectAddress, InvalidOid, InvokeObjectPostCreateHookArg, CreateTrigStmt::isconstraint, TriggerTransition::isNew, IsSystemRelation(), TriggerTransition::isTable, sort-test::key, lappend_oid(), lfirst, lfirst_node, lfirst_oid, list_free(), list_length(), Var::location, LockRelationOid(), LookupFuncName(), make_parsestate(), makeAlias(), map_partition_varattnos(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), name, TriggerTransition::name, NAMEDATALEN, namein(), NameListToString(), NameStr, namestrcmp(), NIL, nodeToString(), NoLock, PartitionDescData::nparts, OBJECT_FUNCTION, ObjectAddressSet, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, OidIsValid, PartitionDescData::oids, ParseState::p_rtable, ParseState::p_sourcetext, palloc(), parser_errposition(), pfree(), pg_class_aclcheck(), pg_proc_aclcheck(), PointerGetDatum, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pull_var_clause(), RangeVarGetRelid, RelationData::rd_att, RelationData::rd_id, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnExpr(), CreateTrigStmt::relation, RelationGetDescr, RelationGetNamespace, RelationGetPartitionDesc, RelationGetRelationName, RelationGetRelid, relhastriggers, 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, table_close(), table_open(), table_openrv(), CreateTrigStmt::timing, transformWhereClause(), CreateTrigStmt::transitionRels, TRIGGER_FIRES_ON_ORIGIN, TriggerOidIndexId, TriggerRelidNameIndexId, CreateTrigStmt::trigname, TupleDescAttr, values, Var::varattno, Var::varno, WARNING, and CreateTrigStmt::whenClause.

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

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