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

Referenced by AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_2CTID

#define AFTER_TRIGGER_2CTID   0xC0000000

Definition at line 3625 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_DONE

#define AFTER_TRIGGER_DONE   0x10000000

◆ AFTER_TRIGGER_FDW_FETCH

#define AFTER_TRIGGER_FDW_FETCH   0x80000000

Definition at line 3623 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_FDW_REUSE

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3622 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

◆ AFTER_TRIGGER_IN_PROGRESS

#define AFTER_TRIGGER_IN_PROGRESS   0x20000000

◆ AFTER_TRIGGER_OFFSET

#define AFTER_TRIGGER_OFFSET   0x0FFFFFFF /* must be low-order bits */

Definition at line 3618 of file trigger.c.

Referenced by afterTriggerAddEvent().

◆ AFTER_TRIGGER_TUP_BITS

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3626 of file trigger.c.

Referenced by AfterTriggerExecute().

◆ CHUNK_DATA_START

#define CHUNK_DATA_START (   cptr)    ((char *) (cptr) + MAXALIGN(sizeof(AfterTriggerEventChunk)))

◆ for_each_chunk

#define for_each_chunk (   cptr,
  evtlist 
)    for (cptr = (evtlist).head; cptr != NULL; cptr = cptr->next)

Definition at line 3698 of file trigger.c.

Referenced by afterTriggerInvokeEvents().

◆ for_each_chunk_from

#define for_each_chunk_from (   cptr)    for (; cptr != NULL; cptr = cptr->next)

Definition at line 3709 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

◆ for_each_event

#define for_each_event (   eptr,
  cptr 
)
Value:
for (eptr = (AfterTriggerEvent) CHUNK_DATA_START(cptr); \
(char *) eptr < (cptr)->freeptr; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3687
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3661
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3639

Definition at line 3700 of file trigger.c.

Referenced by afterTriggerInvokeEvents().

◆ for_each_event_chunk

#define for_each_event_chunk (   eptr,
  cptr,
  evtlist 
)    for_each_chunk(cptr, evtlist) for_each_event(eptr, cptr)

◆ for_each_event_from

#define for_each_event_from (   eptr,
  cptr 
)
Value:
for (; \
(char *) eptr < (cptr)->freeptr; \
eptr = (AfterTriggerEvent) (((char *) eptr) + SizeofTriggerEvent(eptr)))
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3661
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3639

Definition at line 3711 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

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

Definition at line 3661 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

◆ AfterTriggerEvent

Definition at line 3639 of file trigger.c.

◆ AfterTriggerEventChunk

◆ AfterTriggerEventData

◆ AfterTriggerEventDataOneCtid

◆ AfterTriggerEventDataZeroCtids

◆ AfterTriggerEventList

◆ AfterTriggersData

◆ AfterTriggerShared

Definition at line 3628 of file trigger.c.

◆ AfterTriggerSharedData

◆ AfterTriggersQueryData

◆ AfterTriggersTableData

◆ AfterTriggersTransData

◆ SetConstraintState

◆ SetConstraintStateData

◆ SetConstraintTrigger

◆ SetConstraintTriggerData

◆ TriggerFlags

Definition at line 3616 of file trigger.c.

Function Documentation

◆ afterTriggerAddEvent()

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

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

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

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )

Definition at line 4766 of file trigger.c.

References AfterTriggersData::query_depth.

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

4767 {
4768  /* Increase the query stack depth */
4770 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3842

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )

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

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

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )

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

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

◆ afterTriggerCheckState()

static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

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

◆ afterTriggerDeleteHeadEventChunk()

static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

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

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)

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

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

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)

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

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

◆ AfterTriggerEnlargeQueryState()

static void AfterTriggerEnlargeQueryState ( void  )
static

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

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

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

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

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )

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

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

◆ afterTriggerFreeEventList()

static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 4064 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

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

◆ AfterTriggerFreeQuery()

static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

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

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

◆ afterTriggerInvokeEvents()

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

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

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

◆ afterTriggerMarkEvents()

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

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

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

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)

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

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

◆ afterTriggerRestoreEventList()

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

Definition at line 4085 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

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

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

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

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)

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

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

◆ before_stmt_triggers_fired()

static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

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

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

◆ cancel_prior_stmt_triggers()

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

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

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

◆ 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:338
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:3592
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:668
bool IsSystemRelation(Relation relation)
Definition: catalog.c:63
Oid GetUserId(void)
Definition: miscinit.c:379
#define PointerGetDatum(X)
Definition: postgres.h:541
#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:436
#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:250
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:710
#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:3349
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:492
#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:563
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:367
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:387
Relation heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: heapam.c:1323
bool allowSystemTableMods
Definition: globals.c:119
#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:401
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4627
#define DatumGetPointer(X)
Definition: postgres.h:534
Oid LookupFuncName(List *funcname, int nargs, const Oid