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

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3617 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 3689 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 3700 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:3678
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3652
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3630

Definition at line 3691 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:3652
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3630

Definition at line 3702 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:3617
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3615
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3616

Definition at line 3652 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3630 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3619 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

Definition at line 3783 of file trigger.c.

◆ AfterTriggersTableData

Definition at line 3785 of file trigger.c.

◆ AfterTriggersTransData

Definition at line 3784 of file trigger.c.

◆ SetConstraintState

Definition at line 3567 of file trigger.c.

◆ SetConstraintStateData

◆ SetConstraintTrigger

Definition at line 3546 of file trigger.c.

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3607 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

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

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4785 of file trigger.c.

References AfterTriggersData::query_depth.

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

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

Definition at line 5053 of file trigger.c.

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

Referenced by StartSubTransaction().

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

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

Definition at line 4753 of file trigger.c.

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

Referenced by StartTransaction().

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

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

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

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

4120 {
4121  AfterTriggerEventChunk *target = qs->events.head;
4122  ListCell *lc;
4123 
4124  Assert(target && target->next);
4125 
4126  /*
4127  * First, update any pointers in the per-table data, so that they won't be
4128  * dangling. Resetting obsoleted pointers to NULL will make
4129  * cancel_prior_stmt_triggers start from the list head, which is fine.
4130  */
4131  foreach(lc, qs->tables)
4132  {
4134 
4135  if (table->after_trig_done &&
4136  table->after_trig_events.tail == target)
4137  {
4138  table->after_trig_events.head = NULL;
4139  table->after_trig_events.tail = NULL;
4140  table->after_trig_events.tailfree = NULL;
4141  }
4142  }
4143 
4144  /* Now we can flush the head chunk */
4145  qs->events.head = target->next;
4146  pfree(target);
4147 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3684
struct AfterTriggerEventChunk * next
Definition: trigger.c:3671
void pfree(void *pointer)
Definition: mcxt.c:1056
AfterTriggerEventList after_trig_events
Definition: trigger.c:3828
#define Assert(condition)
Definition: c.h:739
#define lfirst(lc)
Definition: pg_list.h:190
AfterTriggerEventChunk * head
Definition: trigger.c:3683
AfterTriggerEventList events
Definition: trigger.c:3806

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

Definition at line 4805 of file trigger.c.

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

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

Definition at line 5101 of file trigger.c.

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

Referenced by AbortSubTransaction(), and CommitSubTransaction().

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

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

Definition at line 5005 of file trigger.c.

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

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

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

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

Definition at line 5203 of file trigger.c.

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

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

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

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

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

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

Definition at line 4949 of file trigger.c.

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

Referenced by CommitTransaction(), and PrepareTransaction().

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

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4058 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

4059 {
4060  AfterTriggerEventChunk *chunk;
4061 
4062  while ((chunk = events->head) != NULL)
4063  {
4064  events->head = chunk->next;
4065  pfree(chunk);
4066  }
4067  events->tail = NULL;
4068  events->tailfree = NULL;
4069 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3684
struct AfterTriggerEventChunk * next
Definition: trigger.c:3671
void pfree(void *pointer)
Definition: mcxt.c:1056
AfterTriggerEventChunk * head
Definition: trigger.c:3683

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

Definition at line 4896 of file trigger.c.

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

Referenced by AfterTriggerEndQuery(), and AfterTriggerEndSubXact().

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

◆ afterTriggerInvokeEvents()

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

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

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

◆ afterTriggerMarkEvents()

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

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

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

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

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

◆ afterTriggerRestoreEventList()

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

Definition at line 4079 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

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

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

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

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5325 of file trigger.c.

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

Referenced by standard_ProcessUtility().

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

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

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

◆ cancel_prior_stmt_triggers()

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

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

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

◆ ConvertTriggerToFK()

static void ConvertTriggerToFK ( CreateTrigStmt stmt,
Oid  funcoid 
)
static

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

1236 {
1237  static List *info_list = NIL;
1238 
1239  static const char *const funcdescr[3] = {
1240  gettext_noop("Found referenced table's UPDATE trigger."),
1241  gettext_noop("Found referenced table's DELETE trigger."),
1242  gettext_noop("Found referencing table's trigger.")
1243  };
1244 
1245  char *constr_name;
1246  char *fk_table_name;
1247  char *pk_table_name;
1248  char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1249  List *fk_attrs = NIL;
1250  List *pk_attrs = NIL;
1252  int funcnum;
1253  OldTriggerInfo *info = NULL;
1254  ListCell *l;
1255  int i;
1256 
1257  /* Parse out the trigger arguments */
1258  constr_name = strVal(linitial(stmt->args));
1259  fk_table_name = strVal(lsecond(stmt->args));
1260  pk_table_name = strVal(lthird(stmt->args));
1261  i = 0;
1262  foreach(l, stmt->args)
1263  {
1264  Value *arg = (Value *) lfirst(l);
1265 
1266  i++;
1267  if (i < 4) /* skip constraint and table names */
1268  continue;
1269  if (i == 4) /* handle match type */
1270  {
1271  if (strcmp(strVal(arg), "FULL") == 0)
1272  fk_matchtype = FKCONSTR_MATCH_FULL;
1273  else
1274  fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1275  continue;
1276  }
1277  if (i % 2)
1278  fk_attrs = lappend(fk_attrs, arg);
1279  else
1280  pk_attrs = lappend(pk_attrs, arg);
1281  }
1282 
1283  /* Prepare description of constraint for use in messages */
1284  initStringInfo(&buf);
1285  appendStringInfo(&buf, "FOREIGN KEY %s(",
1286  quote_identifier(fk_table_name));
1287  i = 0;
1288  foreach(l, fk_attrs)
1289  {
1290  Value *arg = (Value *) lfirst(l);
1291 
1292  if (i++ > 0)
1293  appendStringInfoChar(&buf, ',');
1295  }
1296  appendStringInfo(&buf, ") REFERENCES %s(",
1297  quote_identifier(pk_table_name));
1298  i = 0;
1299  foreach(l, pk_attrs)
1300  {
1301  Value *arg = (Value *) lfirst(l);
1302 
1303  if (i++ > 0)
1304  appendStringInfoChar(&buf, ',');
1306  }
1307  appendStringInfoChar(&buf, ')');
1308 
1309  /* Identify class of trigger --- update, delete, or referencing-table */
1310  switch (funcoid)
1311  {
1312  case F_RI_FKEY_CASCADE_UPD:
1313  case F_RI_FKEY_RESTRICT_UPD:
1314  case F_RI_FKEY_SETNULL_UPD:
1315  case F_RI_FKEY_SETDEFAULT_UPD:
1316  case F_RI_FKEY_NOACTION_UPD:
1317  funcnum = 0;
1318  break;
1319 
1320  case F_RI_FKEY_CASCADE_DEL:
1321  case F_RI_FKEY_RESTRICT_DEL:
1322  case F_RI_FKEY_SETNULL_DEL:
1323  case F_RI_FKEY_SETDEFAULT_DEL:
1324  case F_RI_FKEY_NOACTION_DEL:
1325  funcnum = 1;
1326  break;
1327 
1328  default:
1329  funcnum = 2;
1330  break;
1331  }
1332 
1333  /* See if we have a match to this trigger */
1334  foreach(l, info_list)
1335  {
1336  info = (OldTriggerInfo *) lfirst(l);
1337  if (info->funcoids[funcnum] == InvalidOid &&
1338  equal(info->args, stmt->args))
1339  {
1340  info->funcoids[funcnum] = funcoid;
1341  break;
1342  }
1343  }
1344 
1345  if (l == NULL)
1346  {
1347  /* First trigger of set, so create a new list entry */
1348  MemoryContext oldContext;
1349 
1350  ereport(NOTICE,
1351  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1352  constr_name, buf.data),
1353  errdetail_internal("%s", _(funcdescr[funcnum]))));
1355  info = (OldTriggerInfo *) palloc0(sizeof(OldTriggerInfo));
1356  info->args = copyObject(stmt->args);
1357  info->funcoids[funcnum] = funcoid;
1358  info_list = lappend(info_list, info);
1359  MemoryContextSwitchTo(oldContext);
1360  }
1361  else if (info->funcoids[0] == InvalidOid ||
1362  info->funcoids[1] == InvalidOid ||
1363  info->funcoids[2] == InvalidOid)
1364  {
1365  /* Second trigger of set */
1366  ereport(NOTICE,
1367  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1368  constr_name, buf.data),
1369  errdetail_internal("%s", _(funcdescr[funcnum]))));
1370  }
1371  else
1372  {
1373  /* OK, we have a set, so make the FK constraint ALTER TABLE cmd */
1376  Constraint *fkcon = makeNode(Constraint);
1377  PlannedStmt *wrapper = makeNode(PlannedStmt);
1378 
1379  ereport(NOTICE,
1380  (errmsg("converting trigger group into constraint \"%s\" %s",
1381  constr_name, buf.data),
1382  errdetail_internal("%s", _(funcdescr[funcnum]))));
1383  fkcon->contype = CONSTR_FOREIGN;
1384  fkcon->location = -1;
1385  if (funcnum == 2)
1386  {
1387  /* This trigger is on the FK table */
1388  atstmt->relation = stmt->relation;
1389  if (stmt->constrrel)
1390  fkcon->pktable = stmt->constrrel;
1391  else
1392  {
1393  /* Work around ancient pg_dump bug that omitted constrrel */
1394  fkcon->pktable = makeRangeVar(NULL, pk_table_name, -1);
1395  }
1396  }
1397  else
1398  {
1399  /* This trigger is on the PK table */
1400  fkcon->pktable = stmt->relation;
1401  if (stmt->constrrel)
1402  atstmt->relation = stmt->constrrel;
1403  else
1404  {
1405  /* Work around ancient pg_dump bug that omitted constrrel */
1406  atstmt->relation = makeRangeVar(NULL, fk_table_name, -1);
1407  }
1408  }
1409  atstmt->cmds = list_make1(atcmd);
1410  atstmt->relkind = OBJECT_TABLE;
1411  atcmd->subtype = AT_AddConstraint;
1412  atcmd->def = (Node *) fkcon;
1413  if (strcmp(constr_name, "<unnamed>") == 0)
1414  fkcon->conname = NULL;
1415  else
1416  fkcon->conname = constr_name;
1417  fkcon->fk_attrs = fk_attrs;
1418  fkcon->pk_attrs = pk_attrs;
1419  fkcon->fk_matchtype = fk_matchtype;
1420  switch (info->funcoids[0])
1421  {
1422  case F_RI_FKEY_NOACTION_UPD:
1424  break;
1425  case F_RI_FKEY_CASCADE_UPD:
1427  break;
1428  case F_RI_FKEY_RESTRICT_UPD:
1430  break;
1431  case F_RI_FKEY_SETNULL_UPD:
1433  break;
1434  case F_RI_FKEY_SETDEFAULT_UPD:
1436  break;
1437  default:
1438  /* can't get here because of earlier checks */
1439  elog(ERROR, "confused about RI update function");
1440  }
1441  switch (info->funcoids[1])
1442  {
1443  case F_RI_FKEY_NOACTION_DEL:
1445  break;
1446  case F_RI_FKEY_CASCADE_DEL:
1448  break;
1449  case F_RI_FKEY_RESTRICT_DEL:
1451  break;
1452  case F_RI_FKEY_SETNULL_DEL:
1454  break;
1455  case F_RI_FKEY_SETDEFAULT_DEL:
1457  break;
1458  default:
1459  /* can't get here because of earlier checks */
1460  elog(ERROR, "confused about RI delete function");
1461  }
1462  fkcon->deferrable = stmt->deferrable;
1463  fkcon->initdeferred = stmt->initdeferred;
1464  fkcon->skip_validation = false;
1465  fkcon->initially_valid = true;
1466 
1467  /* finally, wrap it in a dummy PlannedStmt */
1468  wrapper->commandType = CMD_UTILITY;
1469  wrapper->canSetTag = false;
1470  wrapper->utilityStmt = (Node *) atstmt;
1471  wrapper->stmt_location = -1;
1472  wrapper->stmt_len = -1;
1473 
1474  /* ... and execute it */
1475  ProcessUtility(wrapper,
1476  "(generated ALTER TABLE ADD FOREIGN KEY command)",
1477  PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1478  None_Receiver, NULL);
1479 
1480  /* Remove the matched item from the list */
1481  info_list = list_delete_ptr(info_list, info);
1482  pfree(info);
1483  /* We leak the copied args ... not worth worrying about */
1484  }
1485 }
#define NIL
Definition: pg_list.h:65
#define FKCONSTR_MATCH_SIMPLE
Definition: parsenodes.h:2144
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:10697
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3018
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2135
char fk_matchtype
Definition: parsenodes.h:2186
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2139
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define gettext_noop(x)
Definition: c.h:1147
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:501
bool initdeferred
Definition: parsenodes.h:2154
AlterTableType subtype
Definition: parsenodes.h:1860
List * pk_attrs
Definition: parsenodes.h:2185
char * conname
Definition: parsenodes.h:2152
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:2444
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:2153
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:1230
#define ereport(elevel, rest)
Definition: elog.h:141
ObjectType relkind
Definition: parsenodes.h:1776
MemoryContext TopMemoryContext
Definition: mcxt.c:44
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2137
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:2142
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:2195
#define NOTICE
Definition: elog.h:37
#define makeNode(_type_)
Definition: nodes.h:573
char fk_del_action
Definition: parsenodes.h:2188
#define lfirst(lc)
Definition: pg_list.h:190
Definition: value.h:42
List * args
Definition: trigger.c:1229
int errmsg(const char *fmt,...)
Definition: elog.c:822
RangeVar * relation
Definition: parsenodes.h:1774
#define elog(elevel,...)
Definition: elog.h:228
int i
RangeVar * relation
Definition: parsenodes.h:2428
ConstrType contype
Definition: parsenodes.h:2149
void * arg
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2136
#define lthird(l)
Definition: pg_list.h:205
#define copyObject(obj)
Definition: nodes.h:641
RangeVar * pktable
Definition: parsenodes.h:2183
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2138
Definition: pg_list.h:50
bool skip_validation
Definition: parsenodes.h:2194
#define _(x)
Definition: elog.c:87
List * fk_attrs
Definition: parsenodes.h:2184
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:420
char fk_upd_action
Definition: parsenodes.h:2187

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

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

2151 {
2152  TriggerDesc *newdesc;
2153  Trigger *trigger;
2154  int i;
2155 
2156  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2157  return NULL;
2158 
2159  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
2160  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2161 
2162  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2163  memcpy(trigger, trigdesc->triggers,
2164  trigdesc->numtriggers * sizeof(Trigger));
2165  newdesc->triggers = trigger;
2166 
2167  for (i = 0; i < trigdesc->numtriggers; i++)
2168  {
2169  trigger->tgname = pstrdup(trigger->tgname);
2170  if (trigger->tgnattr > 0)
2171  {
2172  int16 *newattr;
2173 
2174  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2175  memcpy(newattr, trigger->tgattr,
2176  trigger->tgnattr * sizeof(int16));
2177  trigger->tgattr = newattr;
2178  }
2179  if (trigger->tgnargs > 0)
2180  {
2181  char **newargs;
2182  int16 j;
2183 
2184  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2185  for (j = 0; j < trigger->tgnargs; j++)
2186  newargs[j] = pstrdup(trigger->tgargs[j]);
2187  trigger->tgargs = newargs;
2188  }
2189  if (trigger->tgqual)
2190  trigger->tgqual = pstrdup(trigger->tgqual);
2191  if (trigger->tgoldtable)
2192  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2193  if (trigger->tgnewtable)
2194  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2195  trigger++;
2196  }
2197 
2198  return newdesc;
2199 }
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, addNSItemToQuery(), addRangeTableEntryForRelation(), 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  ParseNamespaceItem *nsitem;
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 nsitems for OLD and NEW references.
578  *
579  * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
580  */
581  nsitem = addRangeTableEntryForRelation(pstate, rel,
583  makeAlias("old", NIL),
584  false, false);
585  addNSItemToQuery(pstate, nsitem, false, true, true);
586  nsitem = addRangeTableEntryForRelation(pstate, rel,
588  makeAlias("new", NIL),
589  false, false);
590  addNSItemToQuery(pstate, nsitem, 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 
1148  childTbl = table_open(partdesc->oids[i], ShareRowExclusiveLock);
1149 
1150  /* Find which of the child indexes is the one on this partition */
1151  if (OidIsValid(indexOid))
1152  {
1153  forboth(l, idxs, l2, childTbls)
1154  {
1155  if (lfirst_oid(l2) == partdesc->oids[i])
1156  {
1157  indexOnChild = lfirst_oid(l);
1158  break;
1159  }
1160  }
1161  if (!OidIsValid(indexOnChild))
1162  elog(ERROR, "failed to find index matching index \"%s\" in partition \"%s\"",
1163  get_rel_name(indexOid),
1164  get_rel_name(partdesc->oids[i]));
1165  }
1166 
1167  /*
1168  * Initialize our fabricated parse node by copying the original
1169  * one, then resetting fields that we pass separately.
1170  */
1171  childStmt = (CreateTrigStmt *) copyObject(stmt);
1172  childStmt->funcname = NIL;
1173  childStmt->whenClause = NULL;
1174 
1175  /* If there is a WHEN clause, create a modified copy of it */
1176  qual = copyObject(whenClause);
1177  qual = (Node *)
1179  childTbl, rel);
1180  qual = (Node *)
1182  childTbl, rel);
1183 
1184  CreateTrigger(childStmt, queryString,
1185  partdesc->oids[i], refRelOid,
1186  InvalidOid, indexOnChild,
1187  funcoid, trigoid, qual,
1188  isInternal, true);
1189 
1190  table_close(childTbl, NoLock);
1191 
1192  MemoryContextReset(perChildCxt);
1193  }
1194 
1195  MemoryContextSwitchTo(oldcxt);
1196  MemoryContextDelete(perChildCxt);
1197  list_free(idxs);
1198  list_free(childTbls);
1199  }
1200 
1201  /* Keep lock on target rel until end of xact */
1202  table_close(rel, NoLock);
1203 
1204  return myself;
1205 }
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:3379
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:454
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:2437
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:50
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
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
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:186
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:84
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
Definition: primnodes.h:181
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
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
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:2444
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
#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:2103
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:196
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:955
#define CStringGetDatum(X)
Definition: postgres.h:578
void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior)
Definition: dependency.c:1586
PartitionDesc RelationGetPartitionDesc(Relation rel)
Definition: partdesc.c:65
#define RelationGetRelationName(relation)
Definition: rel.h:462
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
const char * p_sourcetext
Definition: parse_node.h:179
static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
Definition: trigger.c:1235
List * transitionRels
Definition: parsenodes.h:2440
#define ereport(elevel, rest)
Definition: elog.h:141