PostgreSQL Source Code  git master
trigger.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/sysattr.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/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/clauses.h"
#include "optimizer/var.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "pgstat.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
#include "utils/tuplestore.h"
Include dependency graph for trigger.c:

Go to the source code of this file.

Data Structures

struct  OldTriggerInfo
 
struct  SetConstraintTriggerData
 
struct  SetConstraintStateData
 
struct  AfterTriggerSharedData
 
struct  AfterTriggerEventData
 
struct  AfterTriggerEventDataOneCtid
 
struct  AfterTriggerEventDataZeroCtids
 
struct  AfterTriggerEventChunk
 
struct  AfterTriggerEventList
 
struct  AfterTriggersData
 
struct  AfterTriggersQueryData
 
struct  AfterTriggersTransData
 
struct  AfterTriggersTableData
 

Macros

#define GetUpdatedColumns(relinfo, estate)   (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
 
#define AFTER_TRIGGER_OFFSET   0x0FFFFFFF /* must be low-order bits */
 
#define AFTER_TRIGGER_DONE   0x10000000
 
#define AFTER_TRIGGER_IN_PROGRESS   0x20000000
 
#define AFTER_TRIGGER_FDW_REUSE   0x00000000
 
#define AFTER_TRIGGER_FDW_FETCH   0x80000000
 
#define AFTER_TRIGGER_1CTID   0x40000000
 
#define AFTER_TRIGGER_2CTID   0xC0000000
 
#define AFTER_TRIGGER_TUP_BITS   0xC0000000
 
#define SizeofTriggerEvent(evt)
 
#define GetTriggerSharedData(evt)   ((AfterTriggerShared) ((char *) (evt) + ((evt)->ate_flags & AFTER_TRIGGER_OFFSET)))
 
#define CHUNK_DATA_START(cptr)   ((char *) (cptr) + MAXALIGN(sizeof(AfterTriggerEventChunk)))
 
#define for_each_chunk(cptr, evtlist)   for (cptr = (evtlist).head; cptr != NULL; cptr = cptr->next)
 
#define for_each_event(eptr, cptr)
 
#define for_each_event_chunk(eptr, cptr, evtlist)   for_each_chunk(cptr, evtlist) for_each_event(eptr, cptr)
 
#define for_each_chunk_from(cptr)   for (; cptr != NULL; cptr = cptr->next)
 
#define for_each_event_from(eptr, cptr)
 
#define MIN_CHUNK_SIZE   1024
 
#define MAX_CHUNK_SIZE   (1024*1024)
 

Typedefs

typedef struct SetConstraintTriggerData SetConstraintTriggerData
 
typedef struct SetConstraintTriggerDataSetConstraintTrigger
 
typedef struct SetConstraintStateData SetConstraintStateData
 
typedef SetConstraintStateDataSetConstraintState
 
typedef uint32 TriggerFlags
 
typedef struct AfterTriggerSharedDataAfterTriggerShared
 
typedef struct AfterTriggerSharedData AfterTriggerSharedData
 
typedef struct AfterTriggerEventDataAfterTriggerEvent
 
typedef struct AfterTriggerEventData AfterTriggerEventData
 
typedef struct AfterTriggerEventDataOneCtid AfterTriggerEventDataOneCtid
 
typedef struct AfterTriggerEventDataZeroCtids AfterTriggerEventDataZeroCtids
 
typedef struct AfterTriggerEventChunk AfterTriggerEventChunk
 
typedef struct AfterTriggerEventList AfterTriggerEventList
 
typedef struct AfterTriggersQueryData AfterTriggersQueryData
 
typedef struct AfterTriggersTransData AfterTriggersTransData
 
typedef struct AfterTriggersTableData AfterTriggersTableData
 
typedef struct AfterTriggersData AfterTriggersData
 

Functions

static void ConvertTriggerToFK (CreateTrigStmt *stmt, Oid funcoid)
 
static void SetTriggerFlags (TriggerDesc *trigdesc, Trigger *trigger)
 
static HeapTuple GetTupleForTrigger (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot **newSlot)
 
static bool TriggerEnabled (EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, HeapTuple oldtup, HeapTuple newtup)
 
static HeapTuple ExecCallTriggerFunc (TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
 
static void AfterTriggerSaveEvent (EState *estate, ResultRelInfo *relinfo, int event, bool row_trigger, HeapTuple oldtup, HeapTuple newtup, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture)
 
static void AfterTriggerEnlargeQueryState (void)
 
static bool before_stmt_triggers_fired (Oid relid, CmdType cmdType)
 
ObjectAddress CreateTrigger (CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, 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)
 
TupleTableSlotExecBRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecARInsertTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
TupleTableSlotExecIRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecBSDeleteTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASDeleteTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRDeleteTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple)
 
void ExecARDeleteTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
 
bool ExecIRDeleteTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
 
void ExecBSUpdateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASUpdateTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
TupleTableSlotExecBRUpdateTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *slot)
 
void ExecARUpdateTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, HeapTuple newtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
TupleTableSlotExecIRUpdateTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *slot)
 
void ExecBSTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
static void AfterTriggerExecute (AfterTriggerEvent event, Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
 
static AfterTriggersTableDataGetAfterTriggersTableData (Oid relid, CmdType cmdType)
 
static void AfterTriggerFreeQuery (AfterTriggersQueryData *qs)
 
static SetConstraintState SetConstraintStateCreate (int numalloc)
 
static SetConstraintState SetConstraintStateCopy (SetConstraintState state)
 
static SetConstraintState SetConstraintStateAddItem (SetConstraintState state, Oid tgoid, bool tgisdeferred)
 
static void cancel_prior_stmt_triggers (Oid relid, CmdType cmdType, int tgevent)
 
static TuplestorestateGetCurrentFDWTuplestore (void)
 
static bool afterTriggerCheckState (AfterTriggerShared evtshared)
 
static void afterTriggerAddEvent (AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
 
static void afterTriggerFreeEventList (AfterTriggerEventList *events)
 
static void afterTriggerRestoreEventList (AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
 
static void afterTriggerDeleteHeadEventChunk (AfterTriggersQueryData *qs)
 
static bool afterTriggerMarkEvents (AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
 
static bool afterTriggerInvokeEvents (AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
 
TransitionCaptureStateMakeTransitionCaptureState (TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
 
void AfterTriggerBeginXact (void)
 
void AfterTriggerBeginQuery (void)
 
void AfterTriggerEndQuery (EState *estate)
 
void AfterTriggerFireDeferred (void)
 
void AfterTriggerEndXact (bool isCommit)
 
void AfterTriggerBeginSubXact (void)
 
void AfterTriggerEndSubXact (bool isCommit)
 
void AfterTriggerSetState (ConstraintsSetStmt *stmt)
 
bool AfterTriggerPendingOnRel (Oid relid)
 
Datum pg_trigger_depth (PG_FUNCTION_ARGS)
 

Variables

int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN
 
static int MyTriggerDepth = 0
 
static AfterTriggersData afterTriggers
 

Macro Definition Documentation

◆ AFTER_TRIGGER_1CTID

#define AFTER_TRIGGER_1CTID   0x40000000

Definition at line 3623 of file trigger.c.

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

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

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

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

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3625 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 3697 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 3708 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:3686
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3660
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3638

Definition at line 3699 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:3660
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3638

Definition at line 3710 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

◆ GetTriggerSharedData

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

◆ GetUpdatedColumns

#define GetUpdatedColumns (   relinfo,
  estate 
)    (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)

◆ MAX_CHUNK_SIZE

#define MAX_CHUNK_SIZE   (1024*1024)

Referenced by afterTriggerAddEvent().

◆ MIN_CHUNK_SIZE

#define MIN_CHUNK_SIZE   1024

Referenced by afterTriggerAddEvent().

◆ SizeofTriggerEvent

#define SizeofTriggerEvent (   evt)
Value:
(((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_2CTID ? \
((evt)->ate_flags & AFTER_TRIGGER_TUP_BITS) == AFTER_TRIGGER_1CTID ? \
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3625
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3623
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3624

Definition at line 3660 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3638 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3627 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

◆ AfterTriggersTableData

◆ AfterTriggersTransData

◆ SetConstraintState

◆ SetConstraintStateData

◆ SetConstraintTrigger

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3615 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

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

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4765 of file trigger.c.

References AfterTriggersData::query_depth.

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

4766 {
4767  /* Increase the query stack depth */
4769 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

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

5034 {
5035  int my_level = GetCurrentTransactionNestLevel();
5036 
5037  /*
5038  * Allocate more space in the trans_stack if needed. (Note: because the
5039  * minimum nest level of a subtransaction is 2, we waste the first couple
5040  * entries of the array; not worth the notational effort to avoid it.)
5041  */
5042  while (my_level >= afterTriggers.maxtransdepth)
5043  {
5044  if (afterTriggers.maxtransdepth == 0)
5045  {
5046  /* Arbitrarily initialize for max of 8 subtransaction levels */
5049  8 * sizeof(AfterTriggersTransData));
5051  }
5052  else
5053  {
5054  /* repalloc will keep the stack in the same context */
5055  int new_alloc = afterTriggers.maxtransdepth * 2;
5056 
5059  new_alloc * sizeof(AfterTriggersTransData));
5060  afterTriggers.maxtransdepth = new_alloc;
5061  }
5062  }
5063 
5064  /*
5065  * Push the current information into the stack. The SET CONSTRAINTS state
5066  * is not saved until/unless changed. Likewise, we don't make a
5067  * per-subtransaction event context until needed.
5068  */
5069  afterTriggers.trans_stack[my_level].state = NULL;
5073 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3808
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggerEventList events
Definition: trigger.c:3823
CommandId firing_counter
Definition: trigger.c:3825
CommandId firing_counter
Definition: trigger.c:3797
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:753
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:3799
static AfterTriggersData afterTriggers
Definition: trigger.c:3841
SetConstraintState state
Definition: trigger.c:3822

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

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

4734 {
4735  /*
4736  * Initialize after-trigger state structure to empty
4737  */
4738  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4740 
4741  /*
4742  * Verify that there is no leftover state remaining. If these assertions
4743  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4744  * up properly.
4745  */
4746  Assert(afterTriggers.state == NULL);
4747  Assert(afterTriggers.query_stack == NULL);
4749  Assert(afterTriggers.event_cxt == NULL);
4750  Assert(afterTriggers.events.head == NULL);
4751  Assert(afterTriggers.trans_stack == NULL);
4753 }
uint32 CommandId
Definition: c.h:488
AfterTriggersTransData * trans_stack
Definition: trigger.c:3808
AfterTriggersQueryData * query_stack
Definition: trigger.c:3803
SetConstraintState state
Definition: trigger.c:3798
CommandId firing_counter
Definition: trigger.c:3797
#define Assert(condition)
Definition: c.h:699
AfterTriggerEventChunk * head
Definition: trigger.c:3691
MemoryContext event_cxt
Definition: trigger.c:3800
AfterTriggerEventList events
Definition: trigger.c:3799
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

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

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

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

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

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

5082 {
5083  int my_level = GetCurrentTransactionNestLevel();
5085  AfterTriggerEvent event;
5086  AfterTriggerEventChunk *chunk;
5087  CommandId subxact_firing_id;
5088 
5089  /*
5090  * Pop the prior state if needed.
5091  */
5092  if (isCommit)
5093  {
5094  Assert(my_level < afterTriggers.maxtransdepth);
5095  /* If we saved a prior state, we don't need it anymore */
5096  state = afterTriggers.trans_stack[my_level].state;
5097  if (state != NULL)
5098  pfree(state);
5099  /* this avoids double pfree if error later: */
5100  afterTriggers.trans_stack[my_level].state = NULL;
5103  }
5104  else
5105  {
5106  /*
5107  * Aborting. It is possible subxact start failed before calling
5108  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5109  * trans_stack levels that aren't there.
5110  */
5111  if (my_level >= afterTriggers.maxtransdepth)
5112  return;
5113 
5114  /*
5115  * Release query-level storage for queries being aborted, and restore
5116  * query_depth to its pre-subxact value. This assumes that a
5117  * subtransaction will not add events to query levels started in a
5118  * earlier transaction state.
5119  */
5121  {
5125  }
5128 
5129  /*
5130  * Restore the global deferred-event list to its former length,
5131  * discarding any events queued by the subxact.
5132  */
5134  &afterTriggers.trans_stack[my_level].events);
5135 
5136  /*
5137  * Restore the trigger state. If the saved state is NULL, then this
5138  * subxact didn't save it, so it doesn't need restoring.
5139  */
5140  state = afterTriggers.trans_stack[my_level].state;
5141  if (state != NULL)
5142  {
5144  afterTriggers.state = state;
5145  }
5146  /* this avoids double pfree if error later: */
5147  afterTriggers.trans_stack[my_level].state = NULL;
5148 
5149  /*
5150  * Scan for any remaining deferred events that were marked DONE or IN
5151  * PROGRESS by this subxact or a child, and un-mark them. We can
5152  * recognize such events because they have a firing ID greater than or
5153  * equal to the firing_counter value we saved at subtransaction start.
5154  * (This essentially assumes that the current subxact includes all
5155  * subxacts started after it.)
5156  */
5157  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
5159  {
5160  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5161 
5162  if (event->ate_flags &
5164  {
5165  if (evtshared->ats_firing_id >= subxact_firing_id)
5166  event->ate_flags &=
5168  }
5169  }
5170  }
5171 }
uint32 CommandId
Definition: c.h:488
AfterTriggersTransData * trans_stack
Definition: trigger.c:3808
TriggerFlags ate_flags
Definition: trigger.c:3642
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3618
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3704
AfterTriggerEventList events
Definition: trigger.c:3823
AfterTriggersQueryData * query_stack
Definition: trigger.c:3803
CommandId firing_counter
Definition: trigger.c:3825
#define GetTriggerSharedData(evt)
Definition: trigger.c:3667
void pfree(void *pointer)
Definition: mcxt.c:1031
SetConstraintState state
Definition: trigger.c:3798
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:753
#define Assert(condition)
Definition: c.h:699
Definition: regguts.h:298
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:4084
CommandId ats_firing_id
Definition: trigger.c:3634
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3619
AfterTriggerEventList events
Definition: trigger.c:3799
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4876
static AfterTriggersData afterTriggers
Definition: trigger.c:3841
SetConstraintState state
Definition: trigger.c:3822

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

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

4986 {
4987  /*
4988  * Forget the pending-events list.
4989  *
4990  * Since all the info is in TopTransactionContext or children thereof, we
4991  * don't really need to do anything to reclaim memory. However, the
4992  * pending-events list could be large, and so it's useful to discard it as
4993  * soon as possible --- especially if we are aborting because we ran out
4994  * of memory for the list!
4995  */
4997  {
4999  afterTriggers.event_cxt = NULL;
5000  afterTriggers.events.head = NULL;
5001  afterTriggers.events.tail = NULL;
5002  afterTriggers.events.tailfree = NULL;
5003  }
5004 
5005  /*
5006  * Forget any subtransaction state as well. Since this can't be very
5007  * large, we let the eventual reset of TopTransactionContext free the
5008  * memory instead of doing it here.
5009  */
5010  afterTriggers.trans_stack = NULL;
5012 
5013 
5014  /*
5015  * Forget the query stack and constraint-related state information. As
5016  * with the subtransaction state information, we don't bother freeing the
5017  * memory here.
5018  */
5019  afterTriggers.query_stack = NULL;
5021  afterTriggers.state = NULL;
5022 
5023  /* No more afterTriggers manipulation until next transaction starts. */
5025 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
AfterTriggersTransData * trans_stack
Definition: trigger.c:3808
AfterTriggersQueryData * query_stack
Definition: trigger.c:3803
AfterTriggerEventChunk * tail
Definition: trigger.c:3692
SetConstraintState state
Definition: trigger.c:3798
AfterTriggerEventChunk * head
Definition: trigger.c:3691
MemoryContext event_cxt
Definition: trigger.c:3800
AfterTriggerEventList events
Definition: trigger.c:3799
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

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

5184 {
5185  int init_depth = afterTriggers.maxquerydepth;
5186 
5188 
5189  if (afterTriggers.maxquerydepth == 0)
5190  {
5191  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
5192 
5195  new_alloc * sizeof(AfterTriggersQueryData));
5196  afterTriggers.maxquerydepth = new_alloc;
5197  }
5198  else
5199  {
5200  /* repalloc will keep the stack in the same context */
5201  int old_alloc = afterTriggers.maxquerydepth;
5202  int new_alloc = Max(afterTriggers.query_depth + 1,
5203  old_alloc * 2);
5204 
5207  new_alloc * sizeof(AfterTriggersQueryData));
5208  afterTriggers.maxquerydepth = new_alloc;
5209  }
5210 
5211  /* Initialize new array entries to empty */
5212  while (init_depth < afterTriggers.maxquerydepth)
5213  {
5215 
5216  qs->events.head = NULL;
5217  qs->events.tail = NULL;
5218  qs->events.tailfree = NULL;
5219  qs->fdw_tuplestore = NULL;
5220  qs->tables = NIL;
5221 
5222  ++init_depth;
5223  }
5224 }
#define NIL
Definition: pg_list.h:69
MemoryContext TopTransactionContext
Definition: mcxt.c:49
AfterTriggersQueryData * query_stack
Definition: trigger.c:3803
AfterTriggerEventChunk * tail
Definition: trigger.c:3692
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3815
#define Max(x, y)
Definition: c.h:851
#define Assert(condition)
Definition: c.h:699
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1044
AfterTriggerEventChunk * head
Definition: trigger.c:3691
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:771
AfterTriggerEventList events
Definition: trigger.c:3814
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ AfterTriggerExecute()

static void AfterTriggerExecute ( AfterTriggerEvent  event,
Relation  rel,
TriggerDesc trigdesc,
FmgrInfo finfo,
Instrumentation instr,
MemoryContext  per_tuple_context,
TupleTableSlot trig_tuple_slot1,
TupleTableSlot trig_tuple_slot2 
)
static

Definition at line 4178 of file trigger.c.

References AFTER_TRIGGER_2CTID, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_TUP_BITS, AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, AfterTriggersTableData::closed, elog, ERROR, ExecCallTriggerFunc(), ExecMaterializeSlot(), GetCurrentFDWTuplestore(), GetTriggerSharedData, heap_fetch(), heap_freetuple(), InstrStartNode(), InstrStopNode(), InvalidBuffer, ItemPointerCopy, ItemPointerIsValid, MemoryContextReset(), AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, ReleaseBuffer(), SnapshotAny, HeapTupleData::t_self, T_TriggerData, TriggerData::tg_event, TriggerData::tg_newtable, TriggerData::tg_newtuple, TriggerData::tg_newtuplebuf, TriggerData::tg_oldtable, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, TriggerData::tg_trigtuplebuf, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerDesc::triggers, tuplestore_gettupleslot(), and TriggerData::type.

Referenced by afterTriggerInvokeEvents().

4184 {
4185  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4186  Oid tgoid = evtshared->ats_tgoid;
4187  TriggerData LocTriggerData;
4188  HeapTupleData tuple1;
4189  HeapTupleData tuple2;
4190  HeapTuple rettuple;
4191  Buffer buffer1 = InvalidBuffer;
4192  Buffer buffer2 = InvalidBuffer;
4193  int tgindx;
4194 
4195  /*
4196  * Locate trigger in trigdesc.
4197  */
4198  LocTriggerData.tg_trigger = NULL;
4199  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
4200  {
4201  if (trigdesc->triggers[tgindx].tgoid == tgoid)
4202  {
4203  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
4204  break;
4205  }
4206  }
4207  if (LocTriggerData.tg_trigger == NULL)
4208  elog(ERROR, "could not find trigger %u", tgoid);
4209 
4210  /*
4211  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
4212  * to include time spent re-fetching tuples in the trigger cost.
4213  */
4214  if (instr)
4215  InstrStartNode(instr + tgindx);
4216 
4217  /*
4218  * Fetch the required tuple(s).
4219  */
4220  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
4221  {
4223  {
4224  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
4225 
4226  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
4227  trig_tuple_slot1))
4228  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4229 
4230  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4232  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4233  trig_tuple_slot2))
4234  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4235  }
4236  /* fall through */
4238 
4239  /*
4240  * Using ExecMaterializeSlot() rather than ExecFetchSlotTuple()
4241  * ensures that tg_trigtuple does not reference tuplestore memory.
4242  * (It is formally possible for the trigger function to queue
4243  * trigger events that add to the same tuplestore, which can push
4244  * other tuples out of memory.) The distinction is academic,
4245  * because we start with a minimal tuple that ExecFetchSlotTuple()
4246  * must materialize anyway.
4247  */
4248  LocTriggerData.tg_trigtuple =
4249  ExecMaterializeSlot(trig_tuple_slot1);
4250  LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
4251 
4252  LocTriggerData.tg_newtuple =
4253  ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4255  ExecMaterializeSlot(trig_tuple_slot2) : NULL;
4256  LocTriggerData.tg_newtuplebuf = InvalidBuffer;
4257 
4258  break;
4259 
4260  default:
4261  if (ItemPointerIsValid(&(event->ate_ctid1)))
4262  {
4263  ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
4264  if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
4265  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4266  LocTriggerData.tg_trigtuple = &tuple1;
4267  LocTriggerData.tg_trigtuplebuf = buffer1;
4268  }
4269  else
4270  {
4271  LocTriggerData.tg_trigtuple = NULL;
4272  LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
4273  }
4274 
4275  /* don't touch ctid2 if not there */
4276  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4278  ItemPointerIsValid(&(event->ate_ctid2)))
4279  {
4280  ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
4281  if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
4282  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4283  LocTriggerData.tg_newtuple = &tuple2;
4284  LocTriggerData.tg_newtuplebuf = buffer2;
4285  }
4286  else
4287  {
4288  LocTriggerData.tg_newtuple = NULL;
4289  LocTriggerData.tg_newtuplebuf = InvalidBuffer;
4290  }
4291  }
4292 
4293  /*
4294  * Set up the tuplestore information to let the trigger have access to
4295  * transition tables. When we first make a transition table available to
4296  * a trigger, mark it "closed" so that it cannot change anymore. If any
4297  * additional events of the same type get queued in the current trigger
4298  * query level, they'll go into new transition tables.
4299  */
4300  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4301  if (evtshared->ats_table)
4302  {
4303  if (LocTriggerData.tg_trigger->tgoldtable)
4304  {
4305  LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4306  evtshared->ats_table->closed = true;
4307  }
4308 
4309  if (LocTriggerData.tg_trigger->tgnewtable)
4310  {
4311  LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4312  evtshared->ats_table->closed = true;
4313  }
4314  }
4315 
4316  /*
4317  * Setup the remaining trigger information
4318  */
4319  LocTriggerData.type = T_TriggerData;
4320  LocTriggerData.tg_event =
4322  LocTriggerData.tg_relation = rel;
4323 
4324  MemoryContextReset(per_tuple_context);
4325 
4326  /*
4327  * Call the trigger and throw away any possibly returned updated tuple.
4328  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4329  */
4330  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4331  tgindx,
4332  finfo,
4333  NULL,
4334  per_tuple_context);
4335  if (rettuple != NULL &&
4336  rettuple != LocTriggerData.tg_trigtuple &&
4337  rettuple != LocTriggerData.tg_newtuple)
4338  heap_freetuple(rettuple);
4339 
4340  /*
4341  * Release buffers
4342  */
4343  if (buffer1 != InvalidBuffer)
4344  ReleaseBuffer(buffer1);
4345  if (buffer2 != InvalidBuffer)
4346  ReleaseBuffer(buffer2);
4347 
4348  /*
4349  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4350  * one "tuple returned" (really the number of firings).
4351  */
4352  if (instr)
4353  InstrStopNode(instr + tgindx, 1);
4354 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:60
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
TriggerEvent ats_event
Definition: trigger.c:3631
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:80
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3621
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3865
ItemPointerData ate_ctid2
Definition: trigger.c:3644
TriggerFlags ate_flags
Definition: trigger.c:3642
Buffer tg_newtuplebuf
Definition: trigger.h:39
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, Relation stats_relation)
Definition: heapam.c:1903
Oid tgoid
Definition: reltrigger.h:25
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3625
#define InvalidBuffer
Definition: buf.h:25
Tuplestorestate * old_tuplestore
Definition: trigger.c:3837
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1773
unsigned int Oid
Definition: postgres_ext.h:31
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:102
HeapTuple tg_trigtuple
Definition: trigger.h:35
#define GetTriggerSharedData(evt)
Definition: trigger.c:3667
#define ERROR
Definition: elog.h:43
void InstrStartNode(Instrumentation *instr)
Definition: instrument.c:63
ItemPointerData t_self
Definition: htup.h:65
Trigger * triggers
Definition: reltrigger.h:48
Buffer tg_trigtuplebuf
Definition: trigger.h:38
Tuplestorestate * new_tuplestore
Definition: trigger.c:3838
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3624
int numtriggers
Definition: reltrigger.h:49
#define SnapshotAny
Definition: tqual.h:28
char * tgnewtable
Definition: reltrigger.h:43
Trigger * tg_trigger
Definition: trigger.h:37
HeapTuple tg_newtuple
Definition: trigger.h:36
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3635
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:3622
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:781
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:100
Tuplestorestate * tg_oldtable
Definition: trigger.h:40
NodeTag type
Definition: trigger.h:32
Tuplestorestate * tg_newtable
Definition: trigger.h:41
#define elog
Definition: elog.h:219
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition: trigger.c:2353
ItemPointerData ate_ctid1
Definition: trigger.c:3643
char * tgoldtable
Definition: reltrigger.h:42
int Buffer
Definition: buf.h:23
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:139
Relation tg_relation
Definition: trigger.h:34

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

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

4930 {
4931  AfterTriggerEventList *events;
4932  bool snap_pushed = false;
4933 
4934  /* Must not be inside a query */
4936 
4937  /*
4938  * If there are any triggers to fire, make sure we have set a snapshot for
4939  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4940  * can't assume ActiveSnapshot is valid on entry.)
4941  */
4942  events = &afterTriggers.events;
4943  if (events->head != NULL)
4944  {
4946  snap_pushed = true;
4947  }
4948 
4949  /*
4950  * Run all the remaining triggers. Loop until they are all gone, in case
4951  * some trigger queues more for us to do.
4952  */
4953  while (afterTriggerMarkEvents(events, NULL, false))
4954  {
4955  CommandId firing_id = afterTriggers.firing_counter++;
4956 
4957  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4958  break; /* all fired */
4959  }
4960 
4961  /*
4962  * We don't bother freeing the event list, since it will go away anyway
4963  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4964  */
4965 
4966  if (snap_pushed)
4968 }
uint32 CommandId
Definition: c.h:488
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4444
void PopActiveSnapshot(void)
Definition: snapmgr.c:812
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:304
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:733
CommandId firing_counter
Definition: trigger.c:3797
#define Assert(condition)
Definition: c.h:699
AfterTriggerEventChunk * head
Definition: trigger.c:3691
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4372
AfterTriggerEventList events
Definition: trigger.c:3799
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4063 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

4064 {
4065  AfterTriggerEventChunk *chunk;
4066 
4067  while ((chunk = events->head) != NULL)
4068  {
4069  events->head = chunk->next;
4070  pfree(chunk);
4071  }
4072  events->tail = NULL;
4073  events->tailfree = NULL;
4074 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3692
struct AfterTriggerEventChunk * next
Definition: trigger.c:3679
void pfree(void *pointer)
Definition: mcxt.c:1031
AfterTriggerEventChunk * head
Definition: trigger.c:3691

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

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

4877 {
4878  Tuplestorestate *ts;
4879  List *tables;
4880  ListCell *lc;
4881 
4882  /* Drop the trigger events */
4884 
4885  /* Drop FDW tuplestore if any */
4886  ts = qs->fdw_tuplestore;
4887  qs->fdw_tuplestore = NULL;
4888  if (ts)
4889  tuplestore_end(ts);
4890 
4891  /* Release per-table subsidiary storage */
4892  tables = qs->tables;
4893  foreach(lc, tables)
4894  {
4896 
4897  ts = table->old_tuplestore;
4898  table->old_tuplestore = NULL;
4899  if (ts)
4900  tuplestore_end(ts);
4901  ts = table->new_tuplestore;
4902  table->new_tuplestore = NULL;
4903  if (ts)
4904  tuplestore_end(ts);
4905  }
4906 
4907  /*
4908  * Now free the AfterTriggersTableData structs and list cells. Reset list
4909  * pointer first; if list_free_deep somehow gets an error, better to leak
4910  * that storage than have an infinite loop.
4911  */
4912  qs->tables = NIL;
4913  list_free_deep(tables);
4914 }
#define NIL
Definition: pg_list.h:69
Tuplestorestate * old_tuplestore
Definition: trigger.c:3837
void list_free_deep(List *list)
Definition: list.c:1147
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:4063
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3815
Tuplestorestate * new_tuplestore
Definition: trigger.c:3838
#define lfirst(lc)
Definition: pg_list.h:106
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3814
Definition: pg_list.h:45

◆ afterTriggerInvokeEvents()

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

Definition at line 4444 of file trigger.c.

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AfterTriggerExecute(), ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_relid, CHUNK_DATA_START, CreateExecutorState(), CurrentMemoryContext, elog, AfterTriggerEventChunk::endfree, AfterTriggerEventChunk::endptr, ERROR, ExecCleanUpTriggerState(), ExecDropSingleTupleTableSlot(), ExecGetTriggerResultRel(), for_each_chunk, for_each_event, FreeExecutorState(), AfterTriggerEventChunk::freeptr, GetTriggerSharedData, MakeSingleTupleTableSlot(), MemoryContextDelete(), RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.

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

4448 {
4449  bool all_fired = true;
4450  AfterTriggerEventChunk *chunk;
4451  MemoryContext per_tuple_context;
4452  bool local_estate = false;
4453  Relation rel = NULL;
4454  TriggerDesc *trigdesc = NULL;
4455  FmgrInfo *finfo = NULL;
4456  Instrumentation *instr = NULL;
4457  TupleTableSlot *slot1 = NULL,
4458  *slot2 = NULL;
4459 
4460  /* Make a local EState if need be */
4461  if (estate == NULL)
4462  {
4463  estate = CreateExecutorState();
4464  local_estate = true;
4465  }
4466 
4467  /* Make a per-tuple memory context for trigger function calls */
4468  per_tuple_context =
4470  "AfterTriggerTupleContext",
4472 
4473  for_each_chunk(chunk, *events)
4474  {
4475  AfterTriggerEvent event;
4476  bool all_fired_in_chunk = true;
4477 
4478  for_each_event(event, chunk)
4479  {
4480  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4481 
4482  /*
4483  * Is it one for me to fire?
4484  */
4485  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4486  evtshared->ats_firing_id == firing_id)
4487  {
4488  /*
4489  * So let's fire it... but first, find the correct relation if
4490  * this is not the same relation as before.
4491  */
4492  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4493  {
4494  ResultRelInfo *rInfo;
4495 
4496  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4497  rel = rInfo->ri_RelationDesc;
4498  trigdesc = rInfo->ri_TrigDesc;
4499  finfo = rInfo->ri_TrigFunctions;
4500  instr = rInfo->ri_TrigInstrument;
4501  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4502  {
4503  if (slot1 != NULL)
4504  {
4507  }
4508  slot1 = MakeSingleTupleTableSlot(rel->rd_att);
4509  slot2 = MakeSingleTupleTableSlot(rel->rd_att);
4510  }
4511  if (trigdesc == NULL) /* should not happen */
4512  elog(ERROR, "relation %u has no triggers",
4513  evtshared->ats_relid);
4514  }
4515 
4516  /*
4517  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4518  * still set, so recursive examinations of the event list
4519  * won't try to re-fire it.
4520  */
4521  AfterTriggerExecute(event, rel, trigdesc, finfo, instr,
4522  per_tuple_context, slot1, slot2);
4523 
4524  /*
4525  * Mark the event as done.
4526  */
4527  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4528  event->ate_flags |= AFTER_TRIGGER_DONE;
4529  }
4530  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4531  {
4532  /* something remains to be done */
4533  all_fired = all_fired_in_chunk = false;
4534  }
4535  }
4536 
4537  /* Clear the chunk if delete_ok and nothing left of interest */
4538  if (delete_ok && all_fired_in_chunk)
4539  {
4540  chunk->freeptr = CHUNK_DATA_START(chunk);
4541  chunk->endfree = chunk->endptr;
4542 
4543  /*
4544  * If it's last chunk, must sync event list's tailfree too. Note
4545  * that delete_ok must NOT be passed as true if there could be
4546  * additional AfterTriggerEventList values pointing at this event
4547  * list, since we'd fail to fix their copies of tailfree.
4548  */
4549  if (chunk == events->tail)
4550  events->tailfree = chunk->freeptr;
4551  }
4552  }
4553  if (slot1 != NULL)
4554  {
4557  }
4558 
4559  /* Release working resources */
4560  MemoryContextDelete(per_tuple_context);
4561 
4562  if (local_estate)
4563  {
4564  ExecCleanUpTriggerState(estate);
4565  FreeExecutorState(estate);
4566  }
4567 
4568  return all_fired;
4569 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:397
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
TriggerFlags ate_flags
Definition: trigger.c:3642
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3618
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3686
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:418
AfterTriggerEventChunk * tail
Definition: trigger.c:3692
Form_pg_class rd_rel
Definition: rel.h:84
#define GetTriggerSharedData(evt)
Definition: trigger.c:3667
#define for_each_event(eptr, cptr)
Definition: trigger.c:3699
void FreeExecutorState(EState *estate)
Definition: execUtils.c:188
#define ERROR
Definition: elog.h:43
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:247
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc)
Definition: execTuples.c:232
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:409
EState * CreateExecutorState(void)
Definition: execUtils.c:80
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1392
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:170
TupleDesc rd_att
Definition: rel.h:85
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3697
CommandId ats_firing_id
Definition: trigger.c:3634
void ExecCleanUpTriggerState(EState *estate)
Definition: execMain.c:1473
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3619
#define elog
Definition: elog.h:219
#define RelationGetRelid(relation)
Definition: rel.h:407
static void AfterTriggerExecute(AfterTriggerEvent event, Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, TupleTableSlot *trig_tuple_slot1, TupleTableSlot *trig_tuple_slot2)
Definition: trigger.c:4178
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:412

◆ afterTriggerMarkEvents()

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

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

4375 {
4376  bool found = false;
4377  AfterTriggerEvent event;
4378  AfterTriggerEventChunk *chunk;
4379 
4380  for_each_event_chunk(event, chunk, *events)
4381  {
4382  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4383  bool defer_it = false;
4384 
4385  if (!(event->ate_flags &
4387  {
4388  /*
4389  * This trigger hasn't been called or scheduled yet. Check if we
4390  * should call it now.
4391  */
4392  if (immediate_only && afterTriggerCheckState(evtshared))
4393  {
4394  defer_it = true;
4395  }
4396  else
4397  {
4398  /*
4399  * Mark it as to be fired in this firing cycle.
4400  */
4402  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4403  found = true;
4404  }
4405  }
4406 
4407  /*
4408  * If it's deferred, move it to move_list, if requested.
4409  */
4410  if (defer_it && move_list != NULL)
4411  {
4412  /* add it to move_list */
4413  afterTriggerAddEvent(move_list, event, evtshared);
4414  /* mark original copy "done" so we don't do it again */
4415  event->ate_flags |= AFTER_TRIGGER_DONE;
4416  }
4417  }
4418 
4419  return found;
4420 }
TriggerFlags ate_flags
Definition: trigger.c:3642
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3618
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3704
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3901
#define GetTriggerSharedData(evt)
Definition: trigger.c:3667
CommandId firing_counter
Definition: trigger.c:3797
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3947
CommandId ats_firing_id
Definition: trigger.c:3634
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3619
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

5629 {
5630  AfterTriggerEvent event;
5631  AfterTriggerEventChunk *chunk;
5632  int depth;
5633 
5634  /* Scan queued events */
5636  {
5637  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5638 
5639  /*
5640  * We can ignore completed events. (Even if a DONE flag is rolled
5641  * back by subxact abort, it's OK because the effects of the TRUNCATE
5642  * or whatever must get rolled back too.)
5643  */
5644  if (event->ate_flags & AFTER_TRIGGER_DONE)
5645  continue;
5646 
5647  if (evtshared->ats_relid == relid)
5648  return true;
5649  }
5650 
5651  /*
5652  * Also scan events queued by incomplete queries. This could only matter
5653  * if TRUNCATE/etc is executed by a function or trigger within an updating
5654  * query on the same relation, which is pretty perverse, but let's check.
5655  */
5656  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5657  {
5659  {
5660  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5661 
5662  if (event->ate_flags & AFTER_TRIGGER_DONE)
5663  continue;
5664 
5665  if (evtshared->ats_relid == relid)
5666  return true;
5667  }
5668  }
5669 
5670  return false;
5671 }
TriggerFlags ate_flags
Definition: trigger.c:3642
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3618
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3704
AfterTriggersQueryData * query_stack
Definition: trigger.c:3803
#define GetTriggerSharedData(evt)
Definition: trigger.c:3667
AfterTriggerEventList events
Definition: trigger.c:3799
AfterTriggerEventList events
Definition: trigger.c:3814
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ afterTriggerRestoreEventList()

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

Definition at line 4084 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

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

◆ AfterTriggerSaveEvent()

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

Definition at line 5698 of file trigger.c.

References AFTER_TRIGGER_1CTID, AFTER_TRIGGER_2CTID, AFTER_TRIGGER_DEFERRABLE, AFTER_TRIGGER_FDW_FETCH, AFTER_TRIGGER_FDW_REUSE, AFTER_TRIGGER_INITDEFERRED, afterTriggerAddEvent(), AfterTriggerEnlargeQueryState(), Assert, AfterTriggerEventData::ate_ctid1, AfterTriggerEventData::ate_ctid2, AfterTriggerEventData::ate_flags, AfterTriggerSharedData::ats_event, AfterTriggerSharedData::ats_firing_id, AfterTriggerSharedData::ats_relid, AfterTriggerSharedData::ats_table, AfterTriggerSharedData::ats_tgoid, cancel_prior_stmt_triggers(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, do_convert_tuple(), elog, ERROR, AfterTriggersQueryData::events, GetCurrentFDWTuplestore(), i, ItemPointerCopy, ItemPointerSetInvalid, list_member_oid(), AfterTriggersData::maxquerydepth, AfterTriggersTableData::new_tuplestore, TriggerDesc::numtriggers, AfterTriggersTableData::old_tuplestore, pfree(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, RelationData::rd_rel, RelationGetRelid, relkind, RI_FKey_fk_upd_check_required(), RI_FKey_pk_upd_check_required(), RI_FKey_trigger_type(), ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, RI_TRIGGER_FK, RI_TRIGGER_NONE, RI_TRIGGER_PK, HeapTupleData::t_self, TransitionCaptureState::tcs_delete_old_table, TransitionCaptureState::tcs_insert_new_table, TransitionCaptureState::tcs_map, TransitionCaptureState::tcs_original_insert_tuple, TransitionCaptureState::tcs_private, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, Trigger::tgconstrindid, Trigger::tgdeferrable, Trigger::tgfoid, Trigger::tginitdeferred, Trigger::tgnewtable, Trigger::tgoid, Trigger::tgoldtable, Trigger::tgtype, TriggerDesc::trig_delete_after_row, TriggerDesc::trig_insert_after_row, TriggerDesc::trig_update_after_row, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_OPMASK, TRIGGER_EVENT_ROW, TRIGGER_EVENT_TRUNCATE, TRIGGER_EVENT_UPDATE, TRIGGER_FIRED_BY_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and tuplestore_puttuple().

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

5703 {
5704  Relation rel = relinfo->ri_RelationDesc;
5705  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
5706  AfterTriggerEventData new_event;
5707  AfterTriggerSharedData new_shared;
5708  char relkind = rel->rd_rel->relkind;
5709  int tgtype_event;
5710  int tgtype_level;
5711  int i;
5712  Tuplestorestate *fdw_tuplestore = NULL;
5713 
5714  /*
5715  * Check state. We use a normal test not Assert because it is possible to
5716  * reach here in the wrong state given misconfigured RI triggers, in
5717  * particular deferring a cascade action trigger.
5718  */
5719  if (afterTriggers.query_depth < 0)
5720  elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
5721 
5722  /* Be sure we have enough space to record events at this query depth. */
5725 
5726  /*
5727  * If the directly named relation has any triggers with transition tables,
5728  * then we need to capture transition tuples.
5729  */
5730  if (row_trigger && transition_capture != NULL)
5731  {
5732  HeapTuple original_insert_tuple = transition_capture->tcs_original_insert_tuple;
5733  TupleConversionMap *map = transition_capture->tcs_map;
5734  bool delete_old_table = transition_capture->tcs_delete_old_table;
5735  bool update_old_table = transition_capture->tcs_update_old_table;
5736  bool update_new_table = transition_capture->tcs_update_new_table;
5737  bool insert_new_table = transition_capture->tcs_insert_new_table;;
5738 
5739  /*
5740  * For INSERT events newtup should be non-NULL, for DELETE events
5741  * oldtup should be non-NULL, whereas for UPDATE events normally both
5742  * oldtup and newtup are non-NULL. But for UPDATE events fired for
5743  * capturing transition tuples during UPDATE partition-key row
5744  * movement, oldtup is NULL when the event is for a row being inserted,
5745  * whereas newtup is NULL when the event is for a row being deleted.
5746  */
5747  Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
5748  oldtup == NULL));
5749  Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
5750  newtup == NULL));
5751 
5752  if (oldtup != NULL &&
5753  ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
5754  (event == TRIGGER_EVENT_UPDATE && update_old_table)))
5755  {
5756  Tuplestorestate *old_tuplestore;
5757 
5758  old_tuplestore = transition_capture->tcs_private->old_tuplestore;
5759 
5760  if (map != NULL)
5761  {
5762  HeapTuple converted = do_convert_tuple(oldtup, map);
5763 
5764  tuplestore_puttuple(old_tuplestore, converted);
5765  pfree(converted);
5766  }
5767  else
5768  tuplestore_puttuple(old_tuplestore, oldtup);
5769  }
5770  if (newtup != NULL &&
5771  ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
5772  (event == TRIGGER_EVENT_UPDATE && update_new_table)))
5773  {
5774  Tuplestorestate *new_tuplestore;
5775 
5776  new_tuplestore = transition_capture->tcs_private->new_tuplestore;
5777 
5778  if (original_insert_tuple != NULL)
5779  tuplestore_puttuple(new_tuplestore, original_insert_tuple);
5780  else if (map != NULL)
5781  {
5782  HeapTuple converted = do_convert_tuple(newtup, map);
5783 
5784  tuplestore_puttuple(new_tuplestore, converted);
5785  pfree(converted);
5786  }
5787  else
5788  tuplestore_puttuple(new_tuplestore, newtup);
5789  }
5790 
5791  /*
5792  * If transition tables are the only reason we're here, return. As
5793  * mentioned above, we can also be here during update tuple routing in
5794  * presence of transition tables, in which case this function is called
5795  * separately for oldtup and newtup, so we expect exactly one of them
5796  * to be NULL.
5797  */
5798  if (trigdesc == NULL ||
5799  (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
5800  (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
5801  (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
5802  (event == TRIGGER_EVENT_UPDATE && ((oldtup == NULL) ^ (newtup == NULL))))
5803  return;
5804  }
5805 
5806  /*
5807  * Validate the event code and collect the associated tuple CTIDs.
5808  *
5809  * The event code will be used both as a bitmask and an array offset, so
5810  * validation is important to make sure we don't walk off the edge of our
5811  * arrays.
5812  *
5813  * Also, if we're considering statement-level triggers, check whether we
5814  * already queued a set of them for this event, and cancel the prior set
5815  * if so. This preserves the behavior that statement-level triggers fire
5816  * just once per statement and fire after row-level triggers.
5817  */
5818  switch (event)
5819  {
5820  case TRIGGER_EVENT_INSERT:
5821  tgtype_event = TRIGGER_TYPE_INSERT;
5822  if (row_trigger)
5823  {
5824  Assert(oldtup == NULL);
5825  Assert(newtup != NULL);
5826  ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid1));
5827  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5828  }
5829  else
5830  {
5831  Assert(oldtup == NULL);
5832  Assert(newtup == NULL);
5833  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5834  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5836  CMD_INSERT, event);
5837  }
5838  break;
5839  case TRIGGER_EVENT_DELETE:
5840  tgtype_event = TRIGGER_TYPE_DELETE;
5841  if (row_trigger)
5842  {
5843  Assert(oldtup != NULL);
5844  Assert(newtup == NULL);
5845  ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
5846  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5847  }
5848  else
5849  {
5850  Assert(oldtup == NULL);
5851  Assert(newtup == NULL);
5852  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5853  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5855  CMD_DELETE, event);
5856  }
5857  break;
5858  case TRIGGER_EVENT_UPDATE:
5859  tgtype_event = TRIGGER_TYPE_UPDATE;
5860  if (row_trigger)
5861  {
5862  Assert(oldtup != NULL);
5863  Assert(newtup != NULL);
5864  ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
5865  ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid2));
5866  }
5867  else
5868  {
5869  Assert(oldtup == NULL);
5870  Assert(newtup == NULL);
5871  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5872  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5874  CMD_UPDATE, event);
5875  }
5876  break;
5878  tgtype_event = TRIGGER_TYPE_TRUNCATE;
5879  Assert(oldtup == NULL);
5880  Assert(newtup == NULL);
5881  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5882  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5883  break;
5884  default:
5885  elog(ERROR, "invalid after-trigger event code: %d", event);
5886  tgtype_event = 0; /* keep compiler quiet */
5887  break;
5888  }
5889 
5890  if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
5891  new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ?
5893  /* else, we'll initialize ate_flags for each trigger */
5894 
5895  tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
5896 
5897  for (i = 0; i < trigdesc->numtriggers; i++)
5898  {
5899  Trigger *trigger = &trigdesc->triggers[i];
5900 
5901  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
5902  tgtype_level,
5903  TRIGGER_TYPE_AFTER,
5904  tgtype_event))
5905  continue;
5906  if (!TriggerEnabled(estate, relinfo, trigger, event,
5907  modifiedCols, oldtup, newtup))
5908  continue;
5909 
5910  if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
5911  {
5912  if (fdw_tuplestore == NULL)
5913  {
5914  fdw_tuplestore = GetCurrentFDWTuplestore();
5915  new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH;
5916  }
5917  else
5918  /* subsequent event for the same tuple */
5919  new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE;
5920  }
5921 
5922  /*
5923  * If the trigger is a foreign key enforcement trigger, there are
5924  * certain cases where we can skip queueing the event because we can
5925  * tell by inspection that the FK constraint will still pass.
5926  */
5927  if (TRIGGER_FIRED_BY_UPDATE(event))
5928  {
5929  switch (RI_FKey_trigger_type(trigger->tgfoid))
5930  {
5931  case RI_TRIGGER_PK:
5932  /* Update on trigger's PK table */
5933  if (!RI_FKey_pk_upd_check_required(trigger, rel,
5934  oldtup, newtup))
5935  {
5936  /* skip queuing this event */
5937  continue;
5938  }
5939  break;
5940 
5941  case RI_TRIGGER_FK:
5942  /* Update on trigger's FK table */
5943  if (!RI_FKey_fk_upd_check_required(trigger, rel,
5944  oldtup, newtup))
5945  {
5946  /* skip queuing this event */
5947  continue;
5948  }
5949  break;
5950 
5951  case RI_TRIGGER_NONE:
5952  /* Not an FK trigger */
5953  break;
5954  }
5955  }
5956 
5957  /*
5958  * If the trigger is a deferred unique constraint check trigger, only
5959  * queue it if the unique constraint was potentially violated, which
5960  * we know from index insertion time.
5961  */
5962  if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
5963  {
5964  if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
5965  continue; /* Uniqueness definitely not violated */
5966  }
5967 
5968  /*
5969  * Fill in event structure and add it to the current query's queue.
5970  * Note we set ats_table to NULL whenever this trigger doesn't use
5971  * transition tables, to improve sharability of the shared event data.
5972  */
5973  new_shared.ats_event =
5974  (event & TRIGGER_EVENT_OPMASK) |
5975  (row_trigger ? TRIGGER_EVENT_ROW : 0) |
5976  (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
5977  (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
5978  new_shared.ats_tgoid = trigger->tgoid;
5979  new_shared.ats_relid = RelationGetRelid(rel);
5980  new_shared.ats_firing_id = 0;
5981  if ((trigger->tgoldtable || trigger->tgnewtable) &&
5982  transition_capture != NULL)
5983  new_shared.ats_table = transition_capture->tcs_private;
5984  else
5985  new_shared.ats_table = NULL;
5986 
5988  &new_event, &new_shared);
5989  }
5990 
5991  /*
5992  * Finally, spool any foreign tuple(s). The tuplestore squashes them to
5993  * minimal tuples, so this loses any system columns. The executor lost
5994  * those columns before us, for an unrelated reason, so this is fine.
5995  */
5996  if (fdw_tuplestore)
5997  {
5998  if (oldtup != NULL)
5999  tuplestore_puttuple(fdw_tuplestore, oldtup);
6000  if (newtup != NULL)
6001  tuplestore_puttuple(fdw_tuplestore, newtup);
6002  }
6003 }
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
TriggerEvent ats_event
Definition: trigger.c:3631
Relation ri_RelationDesc
Definition: execnodes.h:397
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3232
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3621
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:114
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3865
ItemPointerData ate_ctid2
Definition: trigger.c:3644
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:99
Oid tgfoid
Definition: reltrigger.h:28
TriggerFlags ate_flags
Definition: trigger.c:3642
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:6056
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:113
Oid tgoid
Definition: reltrigger.h:25
AfterTriggersQueryData * query_stack
Definition: trigger.c:3803
Tuplestorestate * old_tuplestore
Definition: trigger.c:3837
Form_pg_class rd_rel
Definition: rel.h:84
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:102
HeapTuple tcs_original_insert_tuple
Definition: trigger.h:82
char relkind
Definition: pg_class.h:51
void pfree(void *pointer)
Definition: mcxt.c:1031
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
ItemPointerData t_self
Definition: htup.h:65
bool tginitdeferred
Definition: reltrigger.h:36
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition: tuplestore.c:730
bool trig_delete_after_row
Definition: reltrigger.h:66
Trigger * triggers
Definition: reltrigger.h:48
bool trig_insert_after_row
Definition: reltrigger.h:56
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3623
Tuplestorestate * new_tuplestore
Definition: trigger.c:3838
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3624
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:409
bool trig_update_after_row
Definition: reltrigger.h:61
int numtriggers
Definition: reltrigger.h:49
bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, HeapTuple old_row, HeapTuple new_row)
Definition: ri_triggers.c:1736
char * tgnewtable
Definition: reltrigger.h:43
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3635
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, HeapTuple oldtup, HeapTuple newtup)
Definition: trigger.c:3388
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:5183
#define RI_TRIGGER_FK
Definition: trigger.h:268
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:505
#define Assert(condition)
Definition: c.h:699
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3947
#define TRIGGER_EVENT_TRUNCATE
Definition: trigger.h:101
Oid tgconstrindid
Definition: reltrigger.h:33
bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, HeapTuple old_row, HeapTuple new_row)
Definition: ri_triggers.c:1679
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3622
CommandId ats_firing_id
Definition: trigger.c:3634
HeapTuple do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
Definition: tupconvert.c:354
#define TRIGGER_EVENT_UPDATE
Definition: trigger.h:100
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:98
#define RI_TRIGGER_PK
Definition: trigger.h:267
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
int i
#define elog
Definition: elog.h:219
ItemPointerData ate_ctid1
Definition: trigger.c:3643
#define RI_TRIGGER_NONE
Definition: trigger.h:269
char * tgoldtable
Definition: reltrigger.h:42
AfterTriggerEventList events
Definition: trigger.c:3814
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:122
static AfterTriggersData afterTriggers
Definition: trigger.c:3841
#define RelationGetRelid(relation)
Definition: rel.h:407
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:139

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5305 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(), heap_close, heap_open(), HeapTupleGetOid, HeapTupleIsValid, i, IsSubTransaction(), lappend_oid(), lfirst, lfirst_oid, list_free(), list_make1_oid, LookupExplicitNamespace(), MyDatabaseId, NIL, SetConstraintStateData::numstates, ObjectIdGetDatum, PopActiveSnapshot(), PushActiveSnapshot(), RangeVar::relname, ScanKeyInit(), RangeVar::schemaname, SetConstraintTriggerData::sct_tgisdeferred, SetConstraintTriggerData::sct_tgoid, SetConstraintStateAddItem(), SetConstraintStateCopy(), SetConstraintStateCreate(), AfterTriggersData::state, AfterTriggersTransData::state, systable_beginscan(), systable_endscan(), systable_getnext(), AfterTriggersData::trans_stack, TriggerConstraintIndexId, and SetConstraintStateData::trigstates.

Referenced by standard_ProcessUtility().

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

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

6011 {
6012  bool result;
6013  AfterTriggersTableData *table;
6014 
6015  /* Check state, like AfterTriggerSaveEvent. */
6016  if (afterTriggers.query_depth < 0)
6017  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
6018 
6019  /* Be sure we have enough space to record events at this query depth. */
6022 
6023  /*
6024  * We keep this state in the AfterTriggersTableData that also holds
6025  * transition tables for the relation + operation. In this way, if we are
6026  * forced to make a new set of transition tables because more tuples get
6027  * entered after we've already fired triggers, we will allow a new set of
6028  * statement triggers to get queued.
6029  */
6030  table = GetAfterTriggersTableData(relid, cmdType);
6031  result = table->before_trig_done;
6032  table->before_trig_done = true;
6033  return result;
6034 }
#define ERROR
Definition: elog.h:43
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4585
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:5183
#define elog
Definition: elog.h:219
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ cancel_prior_stmt_triggers()

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

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

6057 {
6058  AfterTriggersTableData *table;
6060 
6061  /*
6062  * We keep this state in the AfterTriggersTableData that also holds
6063  * transition tables for the relation + operation. In this way, if we are
6064  * forced to make a new set of transition tables because more tuples get
6065  * entered after we've already fired triggers, we will allow a new set of
6066  * statement triggers to get queued without canceling the old ones.
6067  */
6068  table = GetAfterTriggersTableData(relid, cmdType);
6069 
6070  if (table->after_trig_done)
6071  {
6072  /*
6073  * We want to start scanning from the tail location that existed just
6074  * before we inserted any statement triggers. But the events list
6075  * might've been entirely empty then, in which case scan from the
6076  * current head.
6077  */
6078  AfterTriggerEvent event;
6079  AfterTriggerEventChunk *chunk;
6080 
6081  if (table->after_trig_events.tail)
6082  {
6083  chunk = table->after_trig_events.tail;
6084  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
6085  }
6086  else
6087  {
6088  chunk = qs->events.head;
6089  event = NULL;
6090  }
6091 
6092  for_each_chunk_from(chunk)
6093  {
6094  if (event == NULL)
6095  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
6096  for_each_event_from(event, chunk)
6097  {
6098  AfterTriggerShared evtshared = GetTriggerSharedData(event);
6099 
6100  /*
6101  * Exit loop when we reach events that aren't AS triggers for
6102  * the target relation.
6103  */
6104  if (evtshared->ats_relid != relid)
6105  goto done;
6106  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
6107  goto done;
6108  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
6109  goto done;
6110  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
6111  goto done;
6112  /* OK, mark it DONE */
6113  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
6114  event->ate_flags |= AFTER_TRIGGER_DONE;
6115  }
6116  /* signal we must reinitialize event ptr for next chunk */
6117  event = NULL;
6118  }
6119  }
6120 done:
6121 
6122  /* In any case, save current insertion point for next time */
6123  table->after_trig_done = true;
6124  table->after_trig_events = qs->events;
6125 }
TriggerEvent ats_event
Definition: trigger.c:3631
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3618
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3686
AfterTriggersQueryData * query_stack
Definition: trigger.c:3803
AfterTriggerEventChunk * tail
Definition: trigger.c:3692
#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:3667
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4585
AfterTriggerEventList after_trig_events
Definition: trigger.c:3836
#define for_each_chunk_from(cptr)
Definition: trigger.c:3708
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3710
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3638
AfterTriggerEventChunk * head
Definition: trigger.c:3691
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3619
AfterTriggerEventList events
Definition: trigger.c:3814
static AfterTriggersData afterTriggers
Definition: trigger.c:3841

◆ ConvertTriggerToFK()

static void ConvertTriggerToFK ( CreateTrigStmt stmt,
Oid  funcoid 
)
static

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

1226 {
1227  static List *info_list = NIL;
1228 
1229  static const char *const funcdescr[3] = {
1230  gettext_noop("Found referenced table's UPDATE trigger."),
1231  gettext_noop("Found referenced table's DELETE trigger."),
1232  gettext_noop("Found referencing table's trigger.")
1233  };
1234 
1235  char *constr_name;
1236  char *fk_table_name;
1237  char *pk_table_name;
1238  char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1239  List *fk_attrs = NIL;
1240  List *pk_attrs = NIL;
1242  int funcnum;
1243  OldTriggerInfo *info = NULL;
1244  ListCell *l;
1245  int i;
1246 
1247  /* Parse out the trigger arguments */
1248  constr_name = strVal(linitial(stmt->args));
1249  fk_table_name = strVal(lsecond(stmt->args));
1250  pk_table_name = strVal(lthird(stmt->args));
1251  i = 0;
1252  foreach(l, stmt->args)
1253  {
1254  Value *arg = (Value *) lfirst(l);
1255 
1256  i++;
1257  if (i < 4) /* skip constraint and table names */
1258  continue;
1259  if (i == 4) /* handle match type */
1260  {
1261  if (strcmp(strVal(arg), "FULL") == 0)
1262  fk_matchtype = FKCONSTR_MATCH_FULL;
1263  else
1264  fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1265  continue;
1266  }
1267  if (i % 2)
1268  fk_attrs = lappend(fk_attrs, arg);
1269  else
1270  pk_attrs = lappend(pk_attrs, arg);
1271  }
1272 
1273  /* Prepare description of constraint for use in messages */
1274  initStringInfo(&buf);
1275  appendStringInfo(&buf, "FOREIGN KEY %s(",
1276  quote_identifier(fk_table_name));
1277  i = 0;
1278  foreach(l, fk_attrs)
1279  {
1280  Value *arg = (Value *) lfirst(l);
1281 
1282  if (i++ > 0)
1283  appendStringInfoChar(&buf, ',');
1285  }
1286  appendStringInfo(&buf, ") REFERENCES %s(",
1287  quote_identifier(pk_table_name));
1288  i = 0;
1289  foreach(l, pk_attrs)
1290  {
1291  Value *arg = (Value *) lfirst(l);
1292 
1293  if (i++ > 0)
1294  appendStringInfoChar(&buf, ',');
1296  }
1297  appendStringInfoChar(&buf, ')');
1298 
1299  /* Identify class of trigger --- update, delete, or referencing-table */
1300  switch (funcoid)
1301  {
1302  case F_RI_FKEY_CASCADE_UPD:
1303  case F_RI_FKEY_RESTRICT_UPD:
1304  case F_RI_FKEY_SETNULL_UPD:
1305  case F_RI_FKEY_SETDEFAULT_UPD:
1306  case F_RI_FKEY_NOACTION_UPD:
1307  funcnum = 0;
1308  break;
1309 
1310  case F_RI_FKEY_CASCADE_DEL:
1311  case F_RI_FKEY_RESTRICT_DEL:
1312  case F_RI_FKEY_SETNULL_DEL:
1313  case F_RI_FKEY_SETDEFAULT_DEL:
1314  case F_RI_FKEY_NOACTION_DEL:
1315  funcnum = 1;
1316  break;
1317 
1318  default:
1319  funcnum = 2;
1320  break;
1321  }
1322 
1323  /* See if we have a match to this trigger */
1324  foreach(l, info_list)
1325  {
1326  info = (OldTriggerInfo *) lfirst(l);
1327  if (info->funcoids[funcnum] == InvalidOid &&
1328  equal(info->args, stmt->args))
1329  {
1330  info->funcoids[funcnum] = funcoid;
1331  break;
1332  }
1333  }
1334 
1335  if (l == NULL)
1336  {
1337  /* First trigger of set, so create a new list entry */
1338  MemoryContext oldContext;
1339 
1340  ereport(NOTICE,
1341  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1342  constr_name, buf.data),
1343  errdetail_internal("%s", _(funcdescr[funcnum]))));
1345  info = (OldTriggerInfo *) palloc0(sizeof(OldTriggerInfo));
1346  info->args = copyObject(stmt->args);
1347  info->funcoids[funcnum] = funcoid;
1348  info_list = lappend(info_list, info);
1349  MemoryContextSwitchTo(oldContext);
1350  }
1351  else if (info->funcoids[0] == InvalidOid ||
1352  info->funcoids[1] == InvalidOid ||
1353  info->funcoids[2] == InvalidOid)
1354  {
1355  /* Second trigger of set */
1356  ereport(NOTICE,
1357  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1358  constr_name, buf.data),
1359  errdetail_internal("%s", _(funcdescr[funcnum]))));
1360  }
1361  else
1362  {
1363  /* OK, we have a set, so make the FK constraint ALTER TABLE cmd */
1366  Constraint *fkcon = makeNode(Constraint);
1367  PlannedStmt *wrapper = makeNode(PlannedStmt);
1368 
1369  ereport(NOTICE,
1370  (errmsg("converting trigger group into constraint \"%s\" %s",
1371  constr_name, buf.data),
1372  errdetail_internal("%s", _(funcdescr[funcnum]))));
1373  fkcon->contype = CONSTR_FOREIGN;
1374  fkcon->location = -1;
1375  if (funcnum == 2)
1376  {
1377  /* This trigger is on the FK table */
1378  atstmt->relation = stmt->relation;
1379  if (stmt->constrrel)
1380  fkcon->pktable = stmt->constrrel;
1381  else
1382  {
1383  /* Work around ancient pg_dump bug that omitted constrrel */
1384  fkcon->pktable = makeRangeVar(NULL, pk_table_name, -1);
1385  }
1386  }
1387  else
1388  {
1389  /* This trigger is on the PK table */
1390  fkcon->pktable = stmt->relation;
1391  if (stmt->constrrel)
1392  atstmt->relation = stmt->constrrel;
1393  else
1394  {
1395  /* Work around ancient pg_dump bug that omitted constrrel */
1396  atstmt->relation = makeRangeVar(NULL, fk_table_name, -1);
1397  }
1398  }
1399  atstmt->cmds = list_make1(atcmd);
1400  atstmt->relkind = OBJECT_TABLE;
1401  atcmd->subtype = AT_AddConstraint;
1402  atcmd->def = (Node *) fkcon;
1403  if (strcmp(constr_name, "<unnamed>") == 0)
1404  fkcon->conname = NULL;
1405  else
1406  fkcon->conname = constr_name;
1407  fkcon->fk_attrs = fk_attrs;
1408  fkcon->pk_attrs = pk_attrs;
1409  fkcon->fk_matchtype = fk_matchtype;
1410  switch (info->funcoids[0])
1411  {
1412  case F_RI_FKEY_NOACTION_UPD:
1414  break;
1415  case F_RI_FKEY_CASCADE_UPD:
1417  break;
1418  case F_RI_FKEY_RESTRICT_UPD:
1420  break;
1421  case F_RI_FKEY_SETNULL_UPD:
1423  break;
1424  case F_RI_FKEY_SETDEFAULT_UPD:
1426  break;
1427  default:
1428  /* can't get here because of earlier checks */
1429  elog(ERROR, "confused about RI update function");
1430  }
1431  switch (info->funcoids[1])
1432  {
1433  case F_RI_FKEY_NOACTION_DEL:
1435  break;
1436  case F_RI_FKEY_CASCADE_DEL:
1438  break;
1439  case F_RI_FKEY_RESTRICT_DEL:
1441  break;
1442  case F_RI_FKEY_SETNULL_DEL:
1444  break;
1445  case F_RI_FKEY_SETDEFAULT_DEL:
1447  break;
1448  default:
1449  /* can't get here because of earlier checks */
1450  elog(ERROR, "confused about RI delete function");
1451  }
1452  fkcon->deferrable = stmt->deferrable;
1453  fkcon->initdeferred = stmt->initdeferred;
1454  fkcon->skip_validation = false;
1455  fkcon->initially_valid = true;
1456 
1457  /* finally, wrap it in a dummy PlannedStmt */
1458  wrapper->commandType = CMD_UTILITY;
1459  wrapper->canSetTag = false;
1460  wrapper->utilityStmt = (Node *) atstmt;
1461  wrapper->stmt_location = -1;
1462  wrapper->stmt_len = -1;
1463 
1464  /* ... and execute it */
1465  ProcessUtility(wrapper,
1466  "(generated ALTER TABLE ADD FOREIGN KEY command)",
1467  PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1468  None_Receiver, NULL);
1469 
1470  /* Remove the matched item from the list */
1471  info_list = list_delete_ptr(info_list, info);
1472  pfree(info);
1473  /* We leak the copied args ... not worth worrying about */
1474  }
1475 }
#define NIL
Definition: pg_list.h:69
#define FKCONSTR_MATCH_SIMPLE
Definition: parsenodes.h:2087
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:10488
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2986
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2078
char fk_matchtype
Definition: parsenodes.h:2127
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2082
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define gettext_noop(x)
Definition: c.h:1036
Definition: nodes.h:517
#define strVal(v)
Definition: value.h:54
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, char *completionTag)
Definition: utility.c:337
bool initdeferred
Definition: parsenodes.h:2097
AlterTableType subtype
Definition: parsenodes.h:1806
List * pk_attrs
Definition: parsenodes.h:2126
char * conname
Definition: parsenodes.h:2095
List * list_delete_ptr(List *list, void *datum)
Definition: list.c:590
DestReceiver * None_Receiver
Definition: dest.c:91
int stmt_len
Definition: plannodes.h:100
#define lsecond(l)
Definition: pg_list.h:116
int errdetail_internal(const char *fmt,...)
Definition: elog.c:900
#define list_make1(x1)
Definition: pg_list.h:139
RangeVar * constrrel
Definition: parsenodes.h:2385
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:111
#define ERROR
Definition: elog.h:43
bool deferrable
Definition: parsenodes.h:2096
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:157
int stmt_location
Definition: plannodes.h:99
static char * buf
Definition: pg_test_fsync.c:67
Node * utilityStmt
Definition: plannodes.h:96
Oid funcoids[3]
Definition: trigger.c:1220
#define ereport(elevel, rest)
Definition: elog.h:122
ObjectType relkind
Definition: parsenodes.h:1720
MemoryContext TopMemoryContext
Definition: mcxt.c:44
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2080
List * lappend(List *list, void *datum)
Definition: list.c:128
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:169
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
#define FKCONSTR_MATCH_FULL
Definition: parsenodes.h:2085
bool canSetTag
Definition: plannodes.h:53
void * palloc0(Size size)
Definition: mcxt.c:955
CmdType commandType
Definition: plannodes.h:45
#define InvalidOid
Definition: postgres_ext.h:36
bool initially_valid
Definition: parsenodes.h:2136
#define NOTICE
Definition: elog.h:37
#define makeNode(_type_)
Definition: nodes.h:565
char fk_del_action
Definition: parsenodes.h:2129
#define lfirst(lc)
Definition: pg_list.h:106
Definition: value.h:42
List * args
Definition: trigger.c:1219
int errmsg(const char *fmt,...)
Definition: elog.c:797
RangeVar * relation
Definition: parsenodes.h:1718
int i
RangeVar * relation
Definition: parsenodes.h:2369
ConstrType contype
Definition: parsenodes.h:2092
void * arg
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2079
#define lthird(l)
Definition: pg_list.h:121
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:630
RangeVar * pktable
Definition: parsenodes.h:2124
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2081
Definition: pg_list.h:45
bool skip_validation
Definition: parsenodes.h:2135
#define _(x)
Definition: elog.c:84
List * fk_attrs
Definition: parsenodes.h:2125
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:421
char fk_upd_action
Definition: parsenodes.h:2128

◆ CopyTriggerDesc()

TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

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

2139 {
2140  TriggerDesc *newdesc;
2141  Trigger *trigger;
2142  int i;
2143 
2144  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2145  return NULL;
2146 
2147  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
2148  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2149 
2150  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2151  memcpy(trigger, trigdesc->triggers,
2152  trigdesc->numtriggers * sizeof(Trigger));
2153  newdesc->triggers = trigger;
2154 
2155  for (i = 0; i < trigdesc->numtriggers; i++)
2156  {
2157  trigger->tgname = pstrdup(trigger->tgname);
2158  if (trigger->tgnattr > 0)
2159  {
2160  int16 *newattr;
2161 
2162  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2163  memcpy(newattr, trigger->tgattr,
2164  trigger->tgnattr * sizeof(int16));
2165  trigger->tgattr = newattr;
2166  }
2167  if (trigger->tgnargs > 0)
2168  {
2169  char **newargs;
2170  int16 j;
2171 
2172  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2173  for (j = 0; j < trigger->tgnargs; j++)
2174  newargs[j] = pstrdup(trigger->tgargs[j]);
2175  trigger->tgargs = newargs;
2176  }
2177  if (trigger->tgqual)
2178  trigger->tgqual = pstrdup(trigger->tgqual);
2179  if (trigger->tgoldtable)
2180  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2181  if (trigger->tgnewtable)
2182  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2183  trigger++;
2184  }
2185 
2186  return newdesc;
2187 }
signed short int16
Definition: c.h:312
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 159 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_INTERNAL_AUTO, DEPENDENCY_NORMAL, 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(), GetNewOid(), GETSTRUCT, GetUserId(), has_superclass(), heap_close, heap_form_tuple(), heap_freetuple(), heap_open(), heap_openrv(), HeapTupleIsValid, HeapTupleSetOid, i, IndexGetRelation(), CreateTrigStmt::initdeferred, Int16GetDatum, InvalidAttrNumber, InvalidObjectAddress, InvalidOid, InvokeObjectPostCreateHookArg, CreateTrigStmt::isconstraint, TriggerTransition::isNew, IsSystemRelation(), TriggerTransition::isTable, 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(), 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, 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, CreateTrigStmt::timing, transformWhereClause(), CreateTrigStmt::transitionRels, TRIGGER_FIRES_ON_ORIGIN, TriggerRelidNameIndexId, CreateTrigStmt::trigname, values, Var::varattno, Var::varno, WARNING, and CreateTrigStmt::whenClause.

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

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