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

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3626 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 3698 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 3709 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:3687
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3661
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3639

Definition at line 3700 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:3661
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3639

Definition at line 3711 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:3626
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3624
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3625

Definition at line 3661 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3639 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3628 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

◆ AfterTriggersTableData

◆ AfterTriggersTransData

◆ SetConstraintState

◆ SetConstraintStateData

◆ SetConstraintTrigger

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3616 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

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

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4784 of file trigger.c.

References AfterTriggersData::query_depth.

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

4785 {
4786  /* Increase the query stack depth */
4788 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3843

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

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

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

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

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

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

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

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

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

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

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

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

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

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

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

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

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

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

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

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

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

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

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

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

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4067 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

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

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

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

4896 {
4897  Tuplestorestate *ts;
4898  List *tables;
4899  ListCell *lc;
4900 
4901  /* Drop the trigger events */
4903 
4904  /* Drop FDW tuplestore if any */
4905  ts = qs->fdw_tuplestore;
4906  qs->fdw_tuplestore = NULL;
4907  if (ts)
4908  tuplestore_end(ts);
4909 
4910  /* Release per-table subsidiary storage */
4911  tables = qs->tables;
4912  foreach(lc, tables)
4913  {
4915 
4916  ts = table->old_tuplestore;
4917  table->old_tuplestore = NULL;
4918  if (ts)
4919  tuplestore_end(ts);
4920  ts = table->new_tuplestore;
4921  table->new_tuplestore = NULL;
4922  if (ts)
4923  tuplestore_end(ts);
4924  }
4925 
4926  /*
4927  * Now free the AfterTriggersTableData structs and list cells. Reset list
4928  * pointer first; if list_free_deep somehow gets an error, better to leak
4929  * that storage than have an infinite loop.
4930  */
4931  qs->tables = NIL;
4932  list_free_deep(tables);
4933 }
#define NIL
Definition: pg_list.h:65
Tuplestorestate * old_tuplestore
Definition: trigger.c:3838
void list_free_deep(List *list)
Definition: list.c:1387
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4067
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3816
Tuplestorestate * new_tuplestore
Definition: trigger.c:3839
#define lfirst(lc)
Definition: pg_list.h:190
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3815
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 (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4518  {
4519  if (slot1 != NULL)
4520  {
4523  }
4524  slot1 = MakeSingleTupleTableSlot(rel->rd_att,
4526  slot2 = MakeSingleTupleTableSlot(rel->rd_att,
4528  }
4529  if (trigdesc == NULL) /* should not happen */
4530  elog(ERROR, "relation %u has no triggers",
4531  evtshared->ats_relid);
4532  }
4533 
4534  /*
4535  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4536  * still set, so recursive examinations of the event list
4537  * won't try to re-fire it.
4538  */
4539  AfterTriggerExecute(estate, event, rInfo, trigdesc, finfo, instr,
4540  per_tuple_context, slot1, slot2);
4541 
4542  /*
4543  * Mark the event as done.
4544  */
4545  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4546  event->ate_flags |= AFTER_TRIGGER_DONE;
4547  }
4548  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4549  {
4550  /* something remains to be done */
4551  all_fired = all_fired_in_chunk = false;
4552  }
4553  }
4554 
4555  /* Clear the chunk if delete_ok and nothing left of interest */
4556  if (delete_ok && all_fired_in_chunk)
4557  {
4558  chunk->freeptr = CHUNK_DATA_START(chunk);
4559  chunk->endfree = chunk->endptr;
4560 
4561  /*
4562  * If it's last chunk, must sync event list's tailfree too. Note
4563  * that delete_ok must NOT be passed as true if there could be
4564  * additional AfterTriggerEventList values pointing at this event
4565  * list, since we'd fail to fix their copies of tailfree.
4566  */
4567  if (chunk == events->tail)
4568  events->tailfree = chunk->freeptr;
4569  }
4570  }
4571  if (slot1 != NULL)
4572  {
4575  }
4576 
4577  /* Release working resources */
4578  MemoryContextDelete(per_tuple_context);
4579 
4580  if (local_estate)
4581  {
4582  ExecCleanUpTriggerState(estate);
4583  ExecResetTupleTable(estate->es_tupleTable, false);
4584  FreeExecutorState(estate);
4585  }
4586 
4587  return all_fired;
4588 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:411
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define AllocSetContextCreate
Definition: memutils.h:169
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1203
TriggerFlags ate_flags
Definition: trigger.c:3643
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3619
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3687
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:432
AfterTriggerEventChunk * tail
Definition: trigger.c:3693
Form_pg_class rd_rel
Definition: rel.h:83
#define GetTriggerSharedData(evt)
Definition: trigger.c:3668
#define for_each_event(eptr, cptr)
Definition: trigger.c:3700
void FreeExecutorState(EState *estate)
Definition: execUtils.c:192
#define ERROR
Definition: elog.h:43
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:191
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1219
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:423
EState * CreateExecutorState(void)
Definition: execUtils.c:88
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1373
List * es_tupleTable
Definition: execnodes.h:552
void ExecResetTupleTable(List *tupleTable, bool shouldFree)
Definition: execTuples.c:1156
TupleDesc rd_att
Definition: rel.h:84
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3698
CommandId ats_firing_id
Definition: trigger.c:3635
void ExecCleanUpTriggerState(EState *estate)
Definition: execMain.c:1456
#define elog(elevel,...)
Definition: elog.h:226
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3620
#define RelationGetRelid(relation)
Definition: rel.h:416
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:86
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:426
static void AfterTriggerExecute(EState *estate, AfterTriggerEvent event, ResultRelInfo *relInfo, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
Definition: trigger.c:4182

◆ 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:3643
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3619
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3705
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3905
#define GetTriggerSharedData(evt)
Definition: trigger.c:3668
CommandId firing_counter
Definition: trigger.c:3798
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3951
CommandId ats_firing_id
Definition: trigger.c:3635
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3620
static AfterTriggersData afterTriggers
Definition: trigger.c:3843

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

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

◆ afterTriggerRestoreEventList()

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

Definition at line 4088 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

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

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

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

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

Referenced by standard_ProcessUtility().

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

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

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

◆ cancel_prior_stmt_triggers()

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

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

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

◆ ConvertTriggerToFK()

static void ConvertTriggerToFK ( CreateTrigStmt stmt,
Oid  funcoid 
)
static

Definition at line 1243 of file trigger.c.

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

Referenced by CreateTrigger().

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

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

Definition at line 2158 of file trigger.c.

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

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

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

◆ CreateTrigger()

ObjectAddress CreateTrigger ( CreateTrigStmt stmt,
const char *  queryString,
Oid  relOid,
Oid  refRelOid,
Oid  constraintOid,
Oid  indexOid,
Oid  funcoid,
Oid  parentTriggerOid,
Node whenClause,
bool  isInternal,
bool  in_partition 
)

Definition at line 162 of file trigger.c.

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

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

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