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

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3624 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 3696 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 3707 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:3685
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3659
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3637

Definition at line 3698 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:3659
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3637

Definition at line 3709 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:3624
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3622
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3623

Definition at line 3659 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3637 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3626 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3790 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3792 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3791 of file trigger.c.

◆ SetConstraintState

Definition at line 3574 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3553 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3614 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

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

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4792 of file trigger.c.

References AfterTriggersData::query_depth.

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

4793 {
4794  /* Increase the query stack depth */
4796 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

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

5061 {
5062  int my_level = GetCurrentTransactionNestLevel();
5063 
5064  /*
5065  * Allocate more space in the trans_stack if needed. (Note: because the
5066  * minimum nest level of a subtransaction is 2, we waste the first couple
5067  * entries of the array; not worth the notational effort to avoid it.)
5068  */
5069  while (my_level >= afterTriggers.maxtransdepth)
5070  {
5071  if (afterTriggers.maxtransdepth == 0)
5072  {
5073  /* Arbitrarily initialize for max of 8 subtransaction levels */
5076  8 * sizeof(AfterTriggersTransData));
5078  }
5079  else
5080  {
5081  /* repalloc will keep the stack in the same context */
5082  int new_alloc = afterTriggers.maxtransdepth * 2;
5083 
5086  new_alloc * sizeof(AfterTriggersTransData));
5087  afterTriggers.maxtransdepth = new_alloc;
5088  }
5089  }
5090 
5091  /*
5092  * Push the current information into the stack. The SET CONSTRAINTS state
5093  * is not saved until/unless changed. Likewise, we don't make a
5094  * per-subtransaction event context until needed.
5095  */
5096  afterTriggers.trans_stack[my_level].state = NULL;
5100 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3807
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggerEventList events
Definition: trigger.c:3822
CommandId firing_counter
Definition: trigger.c:3824
CommandId firing_counter
Definition: trigger.c:3796
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:841
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:3798
static AfterTriggersData afterTriggers
Definition: trigger.c:3841
SetConstraintState state
Definition: trigger.c:3821

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

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

4761 {
4762  /*
4763  * Initialize after-trigger state structure to empty
4764  */
4765  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4767 
4768  /*
4769  * Verify that there is no leftover state remaining. If these assertions
4770  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4771  * up properly.
4772  */
4773  Assert(afterTriggers.state == NULL);
4774  Assert(afterTriggers.query_stack == NULL);
4776  Assert(afterTriggers.event_cxt == NULL);
4777  Assert(afterTriggers.events.head == NULL);
4778  Assert(afterTriggers.trans_stack == NULL);
4780 }
uint32 CommandId
Definition: c.h:528
AfterTriggersTransData * trans_stack
Definition: trigger.c:3807
AfterTriggersQueryData * query_stack
Definition: trigger.c:3802
SetConstraintState state
Definition: trigger.c:3797
CommandId firing_counter
Definition: trigger.c:3796
#define Assert(condition)
Definition: c.h:739
AfterTriggerEventChunk * head
Definition: trigger.c:3690
MemoryContext event_cxt
Definition: trigger.c:3799
AfterTriggerEventList events
Definition: trigger.c:3798
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

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

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

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

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

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

5109 {
5110  int my_level = GetCurrentTransactionNestLevel();
5112  AfterTriggerEvent event;
5113  AfterTriggerEventChunk *chunk;
5114  CommandId subxact_firing_id;
5115 
5116  /*
5117  * Pop the prior state if needed.
5118  */
5119  if (isCommit)
5120  {
5121  Assert(my_level < afterTriggers.maxtransdepth);
5122  /* If we saved a prior state, we don't need it anymore */
5123  state = afterTriggers.trans_stack[my_level].state;
5124  if (state != NULL)
5125  pfree(state);
5126  /* this avoids double pfree if error later: */
5127  afterTriggers.trans_stack[my_level].state = NULL;
5130  }
5131  else
5132  {
5133  /*
5134  * Aborting. It is possible subxact start failed before calling
5135  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5136  * trans_stack levels that aren't there.
5137  */
5138  if (my_level >= afterTriggers.maxtransdepth)
5139  return;
5140 
5141  /*
5142  * Release query-level storage for queries being aborted, and restore
5143  * query_depth to its pre-subxact value. This assumes that a
5144  * subtransaction will not add events to query levels started in a
5145  * earlier transaction state.
5146  */
5148  {
5152  }
5155 
5156  /*
5157  * Restore the global deferred-event list to its former length,
5158  * discarding any events queued by the subxact.
5159  */
5161  &afterTriggers.trans_stack[my_level].events);
5162 
5163  /*
5164  * Restore the trigger state. If the saved state is NULL, then this
5165  * subxact didn't save it, so it doesn't need restoring.
5166  */
5167  state = afterTriggers.trans_stack[my_level].state;
5168  if (state != NULL)
5169  {
5171  afterTriggers.state = state;
5172  }
5173  /* this avoids double pfree if error later: */
5174  afterTriggers.trans_stack[my_level].state = NULL;
5175 
5176  /*
5177  * Scan for any remaining deferred events that were marked DONE or IN
5178  * PROGRESS by this subxact or a child, and un-mark them. We can
5179  * recognize such events because they have a firing ID greater than or
5180  * equal to the firing_counter value we saved at subtransaction start.
5181  * (This essentially assumes that the current subxact includes all
5182  * subxacts started after it.)
5183  */
5184  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5186  {
5187  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5188 
5189  if (event->ate_flags &
5191  {
5192  if (evtshared->ats_firing_id >= subxact_firing_id)
5193  event->ate_flags &=
5195  }
5196  }
5197  }
5198 }
uint32 CommandId
Definition: c.h:528
AfterTriggersTransData * trans_stack
Definition: trigger.c:3807
TriggerFlags ate_flags
Definition: trigger.c:3641
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3617
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3703
AfterTriggerEventList events
Definition: trigger.c:3822
AfterTriggersQueryData * query_stack
Definition: trigger.c:3802
CommandId firing_counter
Definition: trigger.c:3824
#define GetTriggerSharedData(evt)
Definition: trigger.c:3666
void pfree(void *pointer)
Definition: mcxt.c:1056
SetConstraintState state
Definition: trigger.c:3797
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:841
#define Assert(condition)
Definition: c.h:739
Definition: regguts.h:298
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:4086
CommandId ats_firing_id
Definition: trigger.c:3633
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3618
AfterTriggerEventList events
Definition: trigger.c:3798
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4903
static AfterTriggersData afterTriggers
Definition: trigger.c:3841
SetConstraintState state
Definition: trigger.c:3821

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

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

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

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

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

5211 {
5212  int init_depth = afterTriggers.maxquerydepth;
5213 
5215 
5216  if (afterTriggers.maxquerydepth == 0)
5217  {
5218  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5219 
5222  new_alloc * sizeof(AfterTriggersQueryData));
5223  afterTriggers.maxquerydepth = new_alloc;
5224  }
5225  else
5226  {
5227  /* repalloc will keep the stack in the same context */
5228  int old_alloc = afterTriggers.maxquerydepth;
5229  int new_alloc = Max(afterTriggers.query_depth + 1,
5230  old_alloc * 2);
5231 
5234  new_alloc * sizeof(AfterTriggersQueryData));
5235  afterTriggers.maxquerydepth = new_alloc;
5236  }
5237 
5238  /* Initialize new array entries to empty */
5239  while (init_depth < afterTriggers.maxquerydepth)
5240  {
5242 
5243  qs->events.head = NULL;
5244  qs->events.tail = NULL;
5245  qs->events.tailfree = NULL;
5246  qs->fdw_tuplestore = NULL;
5247  qs->tables = NIL;
5248 
5249  ++init_depth;
5250  }
5251 }
#define NIL
Definition: pg_list.h:65
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggersQueryData * query_stack
Definition: trigger.c:3802
AfterTriggerEventChunk * tail
Definition: trigger.c:3691
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3814
#define Max(x, y)
Definition: c.h:905
#define Assert(condition)
Definition: c.h:739
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069
AfterTriggerEventChunk * head
Definition: trigger.c:3690
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
AfterTriggerEventList events
Definition: trigger.c:3813
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

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

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

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

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

4957 {
4958  AfterTriggerEventList *events;
4959  bool snap_pushed = false;
4960 
4961  /* Must not be inside a query */
4963 
4964  /*
4965  * If there are any triggers to fire, make sure we have set a snapshot for
4966  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4967  * can't assume ActiveSnapshot is valid on entry.)
4968  */
4969  events = &afterTriggers.events;
4970  if (events->head != NULL)
4971  {
4973  snap_pushed = true;
4974  }
4975 
4976  /*
4977  * Run all the remaining triggers. Loop until they are all gone, in case
4978  * some trigger queues more for us to do.
4979  */
4980  while (afterTriggerMarkEvents(events, NULL, false))
4981  {
4982  CommandId firing_id = afterTriggers.firing_counter++;
4983 
4984  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4985  break; /* all fired */
4986  }
4987 
4988  /*
4989  * We don't bother freeing the event list, since it will go away anyway
4990  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4991  */
4992 
4993  if (snap_pushed)
4995 }
uint32 CommandId
Definition: c.h:528
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4468
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:3796
#define Assert(condition)
Definition: c.h:739
AfterTriggerEventChunk * head
Definition: trigger.c:3690
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4396
AfterTriggerEventList events
Definition: trigger.c:3798
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4065 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

4066 {
4067  AfterTriggerEventChunk *chunk;
4068 
4069  while ((chunk = events->head) != NULL)
4070  {
4071  events->head = chunk->next;
4072  pfree(chunk);
4073  }
4074  events->tail = NULL;
4075  events->tailfree = NULL;
4076 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3691
struct AfterTriggerEventChunk * next
Definition: trigger.c:3678
void pfree(void *pointer)
Definition: mcxt.c:1056
AfterTriggerEventChunk * head
Definition: trigger.c:3690

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

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

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

◆ afterTriggerInvokeEvents()

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

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

4472 {
4473  bool all_fired = true;
4474  AfterTriggerEventChunk *chunk;
4475  MemoryContext per_tuple_context;
4476  bool local_estate = false;
4477  ResultRelInfo *rInfo = NULL;
4478  Relation rel = NULL;
4479  TriggerDesc *trigdesc = NULL;
4480  FmgrInfo *finfo = NULL;
4481  Instrumentation *instr = NULL;
4482  TupleTableSlot *slot1 = NULL,
4483  *slot2 = NULL;
4484 
4485  /* Make a local EState if need be */
4486  if (estate == NULL)
4487  {
4488  estate = CreateExecutorState();
4489  local_estate = true;
4490  }
4491 
4492  /* Make a per-tuple memory context for trigger function calls */
4493  per_tuple_context =
4495  "AfterTriggerTupleContext",
4497 
4498  for_each_chunk(chunk, *events)
4499  {
4500  AfterTriggerEvent event;
4501  bool all_fired_in_chunk = true;
4502 
4503  for_each_event(event, chunk)
4504  {
4505  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4506 
4507  /*
4508  * Is it one for me to fire?
4509  */
4510  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4511  evtshared->ats_firing_id == firing_id)
4512  {
4513  /*
4514  * So let's fire it... but first, find the correct relation if
4515  * this is not the same relation as before.
4516  */
4517  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4518  {
4519  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4520  rel = rInfo->ri_RelationDesc;
4521  trigdesc = rInfo->ri_TrigDesc;
4522  finfo = rInfo->ri_TrigFunctions;
4523  instr = rInfo->ri_TrigInstrument;
4524  if (slot1 != NULL)
4525  {
4528  slot1 = slot2 = NULL;
4529  }
4530  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4531  {
4532  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4534  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4536  }
4537  if (trigdesc == NULL) /* should not happen */
4538  elog(ERROR, "relation %u has no triggers",
4539  evtshared->ats_relid);
4540  }
4541 
4542  /*
4543  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4544  * still set, so recursive examinations of the event list
4545  * won't try to re-fire it.
4546  */
4547  AfterTriggerExecute(estate, event, rInfo, trigdesc, finfo, instr,
4548  per_tuple_context, slot1, slot2);
4549 
4550  /*
4551  * Mark the event as done.
4552  */
4553  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4554  event->ate_flags |= AFTER_TRIGGER_DONE;
4555  }
4556  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4557  {
4558  /* something remains to be done */
4559  all_fired = all_fired_in_chunk = false;
4560  }
4561  }
4562 
4563  /* Clear the chunk if delete_ok and nothing left of interest */
4564  if (delete_ok && all_fired_in_chunk)
4565  {
4566  chunk->freeptr = CHUNK_DATA_START(chunk);
4567  chunk->endfree = chunk->endptr;
4568 
4569  /*
4570  * If it's last chunk, must sync event list's tailfree too. Note
4571  * that delete_ok must NOT be passed as true if there could be
4572  * additional AfterTriggerEventList values pointing at this event
4573  * list, since we'd fail to fix their copies of tailfree.
4574  */
4575  if (chunk == events->tail)
4576  events->tailfree = chunk->freeptr;
4577  }
4578  }
4579  if (slot1 != NULL)
4580  {
4583  }
4584 
4585  /* Release working resources */
4586  MemoryContextDelete(per_tuple_context);
4587 
4588  if (local_estate)
4589  {
4590  ExecCleanUpTriggerState(estate);
4591  ExecResetTupleTable(estate->es_tupleTable, false);
4592  FreeExecutorState(estate);
4593  }
4594 
4595  return all_fired;
4596 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:410
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:1208
TriggerFlags ate_flags
Definition: trigger.c:3641
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3617
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3685
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:431
AfterTriggerEventChunk * tail
Definition: trigger.c:3691
Form_pg_class rd_rel
Definition: rel.h:83
#define GetTriggerSharedData(evt)
Definition: trigger.c:3666
#define for_each_event(eptr, cptr)
Definition: trigger.c:3698
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:1224
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:422
EState * CreateExecutorState(void)
Definition: execUtils.c:88
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1371
List * es_tupleTable
Definition: execnodes.h:551
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1161
TupleDesc rd_att
Definition: rel.h:84
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3696
CommandId ats_firing_id
Definition: trigger.c:3633
void ExecCleanUpTriggerState(EState *estate)
Definition: execMain.c:1454
#define elog(elevel,...)
Definition: elog.h:228
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3618
#define RelationGetRelid(relation)
Definition: rel.h:422
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:425
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:4180

◆ afterTriggerMarkEvents()

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

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

4399 {
4400  bool found = false;
4401  AfterTriggerEvent event;
4402  AfterTriggerEventChunk *chunk;
4403 
4404  for_each_event_chunk(event, chunk, *events)
4405  {
4406  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4407  bool defer_it = false;
4408 
4409  if (!(event->ate_flags &
4411  {
4412  /*
4413  * This trigger hasn't been called or scheduled yet. Check if we
4414  * should call it now.
4415  */
4416  if (immediate_only && afterTriggerCheckState(evtshared))
4417  {
4418  defer_it = true;
4419  }
4420  else
4421  {
4422  /*
4423  * Mark it as to be fired in this firing cycle.
4424  */
4426  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4427  found = true;
4428  }
4429  }
4430 
4431  /*
4432  * If it's deferred, move it to move_list, if requested.
4433  */
4434  if (defer_it && move_list != NULL)
4435  {
4436  /* add it to move_list */
4437  afterTriggerAddEvent(move_list, event, evtshared);
4438  /* mark original copy "done" so we don't do it again */
4439  event->ate_flags |= AFTER_TRIGGER_DONE;
4440  }
4441  }
4442 
4443  return found;
4444 }
TriggerFlags ate_flags
Definition: trigger.c:3641
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3617
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3703
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3903
#define GetTriggerSharedData(evt)
Definition: trigger.c:3666
CommandId firing_counter
Definition: trigger.c:3796
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3949
CommandId ats_firing_id
Definition: trigger.c:3633
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3618
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

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

◆ afterTriggerRestoreEventList()

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

Definition at line 4086 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

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

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

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

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, BTEqualStrategyNumber, RangeVar::catalogname, ConstraintNameNspIndexId, ConstraintParentIndexId, ConstraintsSetStmt::constraints, CStringGetDatum, ConstraintsSetStmt::deferred, ereport, errcode(), errmsg(), ERROR, AfterTriggersData::events, fetch_search_path(), AfterTriggersData::firing_counter, get_database_name(), GetCurrentTransactionNestLevel(), GETSTRUCT, GetTransactionSnapshot(), HeapTupleIsValid, i, IsSubTransaction(), sort-test::key, lappend_oid(), lfirst, lfirst_oid, list_free(), list_make1_oid, LookupExplicitNamespace(), MyDatabaseId, NIL, SetConstraintStateData::numstates, ObjectIdGetDatum, PopActiveSnapshot(), PushActiveSnapshot(), RangeVar::relname, ScanKeyInit(), RangeVar::schemaname, 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().

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

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

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

◆ cancel_prior_stmt_triggers()

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

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

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

◆ ConvertTriggerToFK()

static void ConvertTriggerToFK ( CreateTrigStmt stmt,
Oid  funcoid 
)
static

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

1243 {
1244  static List *info_list = NIL;
1245 
1246  static const char *const funcdescr[3] = {
1247  gettext_noop("Found referenced table's UPDATE trigger."),
1248  gettext_noop("Found referenced table's DELETE trigger."),
1249  gettext_noop("Found referencing table's trigger.")
1250  };
1251 
1252  char *constr_name;
1253  char *fk_table_name;
1254  char *pk_table_name;
1255  char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1256  List *fk_attrs = NIL;
1257  List *pk_attrs = NIL;
1259  int funcnum;
1260  OldTriggerInfo *info = NULL;
1261  ListCell *l;
1262  int i;
1263 
1264  /* Parse out the trigger arguments */
1265  constr_name = strVal(linitial(stmt->args));
1266  fk_table_name = strVal(lsecond(stmt->args));
1267  pk_table_name = strVal(lthird(stmt->args));
1268  i = 0;
1269  foreach(l, stmt->args)
1270  {
1271  Value *arg = (Value *) lfirst(l);
1272 
1273  i++;
1274  if (i < 4) /* skip constraint and table names */
1275  continue;
1276  if (i == 4) /* handle match type */
1277  {
1278  if (strcmp(strVal(arg), "FULL") == 0)
1279  fk_matchtype = FKCONSTR_MATCH_FULL;
1280  else
1281  fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1282  continue;
1283  }
1284  if (i % 2)
1285  fk_attrs = lappend(fk_attrs, arg);
1286  else
1287  pk_attrs = lappend(pk_attrs, arg);
1288  }
1289 
1290  /* Prepare description of constraint for use in messages */
1291  initStringInfo(&buf);
1292  appendStringInfo(&buf, "FOREIGN KEY %s(",
1293  quote_identifier(fk_table_name));
1294  i = 0;
1295  foreach(l, fk_attrs)
1296  {
1297  Value *arg = (Value *) lfirst(l);
1298 
1299  if (i++ > 0)
1300  appendStringInfoChar(&buf, ',');
1302  }
1303  appendStringInfo(&buf, ") REFERENCES %s(",
1304  quote_identifier(pk_table_name));
1305  i = 0;
1306  foreach(l, pk_attrs)
1307  {
1308  Value *arg = (Value *) lfirst(l);
1309 
1310  if (i++ > 0)
1311  appendStringInfoChar(&buf, ',');
1313  }
1314  appendStringInfoChar(&buf, ')');
1315 
1316  /* Identify class of trigger --- update, delete, or referencing-table */
1317  switch (funcoid)
1318  {
1319  case F_RI_FKEY_CASCADE_UPD:
1320  case F_RI_FKEY_RESTRICT_UPD:
1321  case F_RI_FKEY_SETNULL_UPD:
1322  case F_RI_FKEY_SETDEFAULT_UPD:
1323  case F_RI_FKEY_NOACTION_UPD:
1324  funcnum = 0;
1325  break;
1326 
1327  case F_RI_FKEY_CASCADE_DEL:
1328  case F_RI_FKEY_RESTRICT_DEL:
1329  case F_RI_FKEY_SETNULL_DEL:
1330  case F_RI_FKEY_SETDEFAULT_DEL:
1331  case F_RI_FKEY_NOACTION_DEL:
1332  funcnum = 1;
1333  break;
1334 
1335  default:
1336  funcnum = 2;
1337  break;
1338  }
1339 
1340  /* See if we have a match to this trigger */
1341  foreach(l, info_list)
1342  {
1343  info = (OldTriggerInfo *) lfirst(l);
1344  if (info->funcoids[funcnum] == InvalidOid &&
1345  equal(info->args, stmt->args))
1346  {
1347  info->funcoids[funcnum] = funcoid;
1348  break;
1349  }
1350  }
1351 
1352  if (l == NULL)
1353  {
1354  /* First trigger of set, so create a new list entry */
1355  MemoryContext oldContext;
1356 
1357  ereport(NOTICE,
1358  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1359  constr_name, buf.data),
1360  errdetail_internal("%s", _(funcdescr[funcnum]))));
1362  info = (OldTriggerInfo *) palloc0(sizeof(OldTriggerInfo));
1363  info->args = copyObject(stmt->args);
1364  info->funcoids[funcnum] = funcoid;
1365  info_list = lappend(info_list, info);
1366  MemoryContextSwitchTo(oldContext);
1367  }
1368  else if (info->funcoids[0] == InvalidOid ||
1369  info->funcoids[1] == InvalidOid ||
1370  info->funcoids[2] == InvalidOid)
1371  {
1372  /* Second trigger of set */
1373  ereport(NOTICE,
1374  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1375  constr_name, buf.data),
1376  errdetail_internal("%s", _(funcdescr[funcnum]))));
1377  }
1378  else
1379  {
1380  /* OK, we have a set, so make the FK constraint ALTER TABLE cmd */
1383  Constraint *fkcon = makeNode(Constraint);
1384  PlannedStmt *wrapper = makeNode(PlannedStmt);
1385 
1386  ereport(NOTICE,
1387  (errmsg("converting trigger group into constraint \"%s\" %s",
1388  constr_name, buf.data),
1389  errdetail_internal("%s", _(funcdescr[funcnum]))));
1390  fkcon->contype = CONSTR_FOREIGN;
1391  fkcon->location = -1;
1392  if (funcnum == 2)
1393  {
1394  /* This trigger is on the FK table */
1395  atstmt->relation = stmt->relation;
1396  if (stmt->constrrel)
1397  fkcon->pktable = stmt->constrrel;
1398  else
1399  {
1400  /* Work around ancient pg_dump bug that omitted constrrel */
1401  fkcon->pktable = makeRangeVar(NULL, pk_table_name, -1);
1402  }
1403  }
1404  else
1405  {
1406  /* This trigger is on the PK table */
1407  fkcon->pktable = stmt->relation;
1408  if (stmt->constrrel)
1409  atstmt->relation = stmt->constrrel;
1410  else
1411  {
1412  /* Work around ancient pg_dump bug that omitted constrrel */
1413  atstmt->relation = makeRangeVar(NULL, fk_table_name, -1);
1414  }
1415  }
1416  atstmt->cmds = list_make1(atcmd);
1417  atstmt->relkind = OBJECT_TABLE;
1418  atcmd->subtype = AT_AddConstraint;
1419  atcmd->def = (Node *) fkcon;
1420  if (strcmp(constr_name, "<unnamed>") == 0)
1421  fkcon->conname = NULL;
1422  else
1423  fkcon->conname = constr_name;
1424  fkcon->fk_attrs = fk_attrs;
1425  fkcon->pk_attrs = pk_attrs;
1426  fkcon->fk_matchtype = fk_matchtype;
1427  switch (info->funcoids[0])
1428  {
1429  case F_RI_FKEY_NOACTION_UPD:
1431  break;
1432  case F_RI_FKEY_CASCADE_UPD:
1434  break;
1435  case F_RI_FKEY_RESTRICT_UPD:
1437  break;
1438  case F_RI_FKEY_SETNULL_UPD:
1440  break;
1441  case F_RI_FKEY_SETDEFAULT_UPD:
1443  break;
1444  default:
1445  /* can't get here because of earlier checks */
1446  elog(ERROR, "confused about RI update function");
1447  }
1448  switch (info->funcoids[1])
1449  {
1450  case F_RI_FKEY_NOACTION_DEL:
1452  break;
1453  case F_RI_FKEY_CASCADE_DEL:
1455  break;
1456  case F_RI_FKEY_RESTRICT_DEL:
1458  break;
1459  case F_RI_FKEY_SETNULL_DEL:
1461  break;
1462  case F_RI_FKEY_SETDEFAULT_DEL:
1464  break;
1465  default:
1466  /* can't get here because of earlier checks */
1467  elog(ERROR, "confused about RI delete function");
1468  }
1469  fkcon->deferrable = stmt->deferrable;
1470  fkcon->initdeferred = stmt->initdeferred;
1471  fkcon->skip_validation = false;
1472  fkcon->initially_valid = true;
1473 
1474  /* finally, wrap it in a dummy PlannedStmt */
1475  wrapper->commandType = CMD_UTILITY;
1476  wrapper->canSetTag = false;
1477  wrapper->utilityStmt = (Node *) atstmt;
1478  wrapper->stmt_location = -1;
1479  wrapper->stmt_len = -1;
1480 
1481  /* ... and execute it */
1482  ProcessUtility(wrapper,
1483  "(generated ALTER TABLE ADD FOREIGN KEY command)",
1484  PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1485  None_Receiver, NULL);
1486 
1487  /* Remove the matched item from the list */
1488  info_list = list_delete_ptr(info_list, info);
1489  pfree(info);
1490  /* We leak the copied args ... not worth worrying about */
1491  }
1492 }
#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:10772
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3011
#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:1148
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:337
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:96
#define lsecond(l)
Definition: pg_list.h:200
int errdetail_internal(const char *fmt,...)
Definition: elog.c:982
#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:91
#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:176
int stmt_location
Definition: plannodes.h:95
static char * buf
Definition: pg_test_fsync.c:67
Node * utilityStmt
Definition: plannodes.h:92
Oid funcoids[3]
Definition: trigger.c:1237
#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:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
#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:1236
int errmsg(const char *fmt,...)
Definition: elog.c:822
RangeVar * relation
Definition: parsenodes.h:1753
#define elog(elevel,...)
Definition: elog.h:228
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:87
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 2157 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().

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