PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_inherits_fn.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
SetConstraintTriggerData
SetConstraintTrigger
 
typedef struct
SetConstraintStateData 
SetConstraintStateData
 
typedef SetConstraintStateDataSetConstraintState
 
typedef uint32 TriggerFlags
 
typedef struct
AfterTriggerSharedData
AfterTriggerShared
 
typedef struct
AfterTriggerSharedData 
AfterTriggerSharedData
 
typedef struct
AfterTriggerEventData
AfterTriggerEvent
 
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, bool isInternal)
 
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)
 
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

#define AFTER_TRIGGER_1CTID   0x40000000

Definition at line 3384 of file trigger.c.

Referenced by AfterTriggerSaveEvent().

#define AFTER_TRIGGER_2CTID   0xC0000000

Definition at line 3385 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

#define AFTER_TRIGGER_DONE   0x10000000
#define AFTER_TRIGGER_FDW_FETCH   0x80000000

Definition at line 3383 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

#define AFTER_TRIGGER_FDW_REUSE   0x00000000

Definition at line 3382 of file trigger.c.

Referenced by AfterTriggerExecute(), and AfterTriggerSaveEvent().

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

Definition at line 3378 of file trigger.c.

Referenced by afterTriggerAddEvent().

#define AFTER_TRIGGER_TUP_BITS   0xC0000000

Definition at line 3386 of file trigger.c.

Referenced by AfterTriggerExecute().

#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)

Definition at line 3458 of file trigger.c.

Referenced by afterTriggerInvokeEvents().

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

Definition at line 3469 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

#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:3447
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3421
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3399

Definition at line 3460 of file trigger.c.

Referenced by afterTriggerInvokeEvents().

#define for_each_event_chunk (   eptr,
  cptr,
  evtlist 
)    for_each_chunk(cptr, evtlist) for_each_event(eptr, cptr)
#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:3421
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3399

Definition at line 3471 of file trigger.c.

Referenced by cancel_prior_stmt_triggers().

#define GetTriggerSharedData (   evt)    ((AfterTriggerShared) ((char *) (evt) + ((evt)->ate_flags & AFTER_TRIGGER_OFFSET)))
#define GetUpdatedColumns (   relinfo,
  estate 
)    (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
#define MAX_CHUNK_SIZE   (1024*1024)

Referenced by afterTriggerAddEvent().

#define MIN_CHUNK_SIZE   1024

Referenced by afterTriggerAddEvent().

#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:3386
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3384
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3385

Definition at line 3421 of file trigger.c.

Referenced by afterTriggerAddEvent().

Typedef Documentation

Definition at line 3399 of file trigger.c.

Definition at line 3388 of file trigger.c.

Definition at line 3376 of file trigger.c.

Function Documentation

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

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

3717 {
3718  Size eventsize = SizeofTriggerEvent(event);
3719  Size needed = eventsize + sizeof(AfterTriggerSharedData);
3720  AfterTriggerEventChunk *chunk;
3721  AfterTriggerShared newshared;
3722  AfterTriggerEvent newevent;
3723 
3724  /*
3725  * If empty list or not enough room in the tail chunk, make a new chunk.
3726  * We assume here that a new shared record will always be needed.
3727  */
3728  chunk = events->tail;
3729  if (chunk == NULL ||
3730  chunk->endfree - chunk->freeptr < needed)
3731  {
3732  Size chunksize;
3733 
3734  /* Create event context if we didn't already */
3735  if (afterTriggers.event_cxt == NULL)
3738  "AfterTriggerEvents",
3740 
3741  /*
3742  * Chunk size starts at 1KB and is allowed to increase up to 1MB.
3743  * These numbers are fairly arbitrary, though there is a hard limit at
3744  * AFTER_TRIGGER_OFFSET; else we couldn't link event records to their
3745  * shared records using the available space in ate_flags. Another
3746  * constraint is that if the chunk size gets too huge, the search loop
3747  * below would get slow given a (not too common) usage pattern with
3748  * many distinct event types in a chunk. Therefore, we double the
3749  * preceding chunk size only if there weren't too many shared records
3750  * in the preceding chunk; otherwise we halve it. This gives us some
3751  * ability to adapt to the actual usage pattern of the current query
3752  * while still having large chunk sizes in typical usage. All chunk
3753  * sizes used should be MAXALIGN multiples, to ensure that the shared
3754  * records will be aligned safely.
3755  */
3756 #define MIN_CHUNK_SIZE 1024
3757 #define MAX_CHUNK_SIZE (1024*1024)
3758 
3759 #if MAX_CHUNK_SIZE > (AFTER_TRIGGER_OFFSET+1)
3760 #error MAX_CHUNK_SIZE must not exceed AFTER_TRIGGER_OFFSET
3761 #endif
3762 
3763  if (chunk == NULL)
3764  chunksize = MIN_CHUNK_SIZE;
3765  else
3766  {
3767  /* preceding chunk size... */
3768  chunksize = chunk->endptr - (char *) chunk;
3769  /* check number of shared records in preceding chunk */
3770  if ((chunk->endptr - chunk->endfree) <=
3771  (100 * sizeof(AfterTriggerSharedData)))
3772  chunksize *= 2; /* okay, double it */
3773  else
3774  chunksize /= 2; /* too many shared records */
3775  chunksize = Min(chunksize, MAX_CHUNK_SIZE);
3776  }
3777  chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize);
3778  chunk->next = NULL;
3779  chunk->freeptr = CHUNK_DATA_START(chunk);
3780  chunk->endptr = chunk->endfree = (char *) chunk + chunksize;
3781  Assert(chunk->endfree - chunk->freeptr >= needed);
3782 
3783  if (events->head == NULL)
3784  events->head = chunk;
3785  else
3786  events->tail->next = chunk;
3787  events->tail = chunk;
3788  /* events->tailfree is now out of sync, but we'll fix it below */
3789  }
3790 
3791  /*
3792  * Try to locate a matching shared-data record already in the chunk. If
3793  * none, make a new one.
3794  */
3795  for (newshared = ((AfterTriggerShared) chunk->endptr) - 1;
3796  (char *) newshared >= chunk->endfree;
3797  newshared--)
3798  {
3799  if (newshared->ats_tgoid == evtshared->ats_tgoid &&
3800  newshared->ats_relid == evtshared->ats_relid &&
3801  newshared->ats_event == evtshared->ats_event &&
3802  newshared->ats_table == evtshared->ats_table &&
3803  newshared->ats_firing_id == 0)
3804  break;
3805  }
3806  if ((char *) newshared < chunk->endfree)
3807  {
3808  *newshared = *evtshared;
3809  newshared->ats_firing_id = 0; /* just to be sure */
3810  chunk->endfree = (char *) newshared;
3811  }
3812 
3813  /* Insert the data */
3814  newevent = (AfterTriggerEvent) chunk->freeptr;
3815  memcpy(newevent, event, eventsize);
3816  /* ... and link the new event to its shared record */
3817  newevent->ate_flags &= ~AFTER_TRIGGER_OFFSET;
3818  newevent->ate_flags |= (char *) newshared - (char *) newevent;
3819 
3820  chunk->freeptr += eventsize;
3821  events->tailfree = chunk->freeptr;
3822 }
TriggerEvent ats_event
Definition: trigger.c:3392
MemoryContext TopTransactionContext
Definition: mcxt.c:48
#define MIN_CHUNK_SIZE
TriggerFlags ate_flags
Definition: trigger.c:3403
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3447
struct AfterTriggerSharedData AfterTriggerSharedData
#define Min(x, y)
Definition: c.h:795
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
struct AfterTriggerEventChunk * next
Definition: trigger.c:3440
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:165
#define SizeofTriggerEvent(evt)
Definition: trigger.c:3421
#define AFTER_TRIGGER_OFFSET
Definition: trigger.c:3378
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:322
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3396
#define Assert(condition)
Definition: c.h:664
size_t Size
Definition: c.h:350
CommandId ats_firing_id
Definition: trigger.c:3395
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3399
#define MAX_CHUNK_SIZE
AfterTriggerEventChunk * head
Definition: trigger.c:3452
MemoryContext event_cxt
Definition: trigger.c:3561
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:706
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
void AfterTriggerBeginQuery ( void  )

Definition at line 4540 of file trigger.c.

References AfterTriggersData::query_depth.

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

4541 {
4542  /* Increase the query stack depth */
4544 }
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
void AfterTriggerBeginSubXact ( void  )

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

4809 {
4810  int my_level = GetCurrentTransactionNestLevel();
4811 
4812  /*
4813  * Allocate more space in the trans_stack if needed. (Note: because the
4814  * minimum nest level of a subtransaction is 2, we waste the first couple
4815  * entries of the array; not worth the notational effort to avoid it.)
4816  */
4817  while (my_level >= afterTriggers.maxtransdepth)
4818  {
4819  if (afterTriggers.maxtransdepth == 0)
4820  {
4821  /* Arbitrarily initialize for max of 8 subtransaction levels */
4824  8 * sizeof(AfterTriggersTransData));
4826  }
4827  else
4828  {
4829  /* repalloc will keep the stack in the same context */
4830  int new_alloc = afterTriggers.maxtransdepth * 2;
4831 
4834  new_alloc * sizeof(AfterTriggersTransData));
4835  afterTriggers.maxtransdepth = new_alloc;
4836  }
4837  }
4838 
4839  /*
4840  * Push the current information into the stack. The SET CONSTRAINTS state
4841  * is not saved until/unless changed. Likewise, we don't make a
4842  * per-subtransaction event context until needed.
4843  */
4844  afterTriggers.trans_stack[my_level].state = NULL;
4848 }
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
MemoryContext TopTransactionContext
Definition: mcxt.c:48
AfterTriggerEventList events
Definition: trigger.c:3584
CommandId firing_counter
Definition: trigger.c:3586
CommandId firing_counter
Definition: trigger.c:3558
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:762
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:962
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:706
AfterTriggerEventList events
Definition: trigger.c:3560
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
SetConstraintState state
Definition: trigger.c:3583
void AfterTriggerBeginXact ( void  )

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

4509 {
4510  /*
4511  * Initialize after-trigger state structure to empty
4512  */
4513  afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
4515 
4516  /*
4517  * Verify that there is no leftover state remaining. If these assertions
4518  * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
4519  * up properly.
4520  */
4521  Assert(afterTriggers.state == NULL);
4522  Assert(afterTriggers.query_stack == NULL);
4524  Assert(afterTriggers.event_cxt == NULL);
4525  Assert(afterTriggers.events.head == NULL);
4526  Assert(afterTriggers.trans_stack == NULL);
4528 }
uint32 CommandId
Definition: c.h:405
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
SetConstraintState state
Definition: trigger.c:3559
CommandId firing_counter
Definition: trigger.c:3558
#define Assert(condition)
Definition: c.h:664
AfterTriggerEventChunk * head
Definition: trigger.c:3452
MemoryContext event_cxt
Definition: trigger.c:3561
AfterTriggerEventList events
Definition: trigger.c:3560
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
static bool afterTriggerCheckState ( AfterTriggerShared  evtshared)
static

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

3670 {
3671  Oid tgoid = evtshared->ats_tgoid;
3673  int i;
3674 
3675  /*
3676  * For not-deferrable triggers (i.e. normal AFTER ROW triggers and
3677  * constraints declared NOT DEFERRABLE), the state is always false.
3678  */
3679  if ((evtshared->ats_event & AFTER_TRIGGER_DEFERRABLE) == 0)
3680  return false;
3681 
3682  /*
3683  * If constraint state exists, SET CONSTRAINTS might have been executed
3684  * either for this trigger or for all triggers.
3685  */
3686  if (state != NULL)
3687  {
3688  /* Check for SET CONSTRAINTS for this specific trigger. */
3689  for (i = 0; i < state->numstates; i++)
3690  {
3691  if (state->trigstates[i].sct_tgoid == tgoid)
3692  return state->trigstates[i].sct_tgisdeferred;
3693  }
3694 
3695  /* Check for SET CONSTRAINTS ALL. */
3696  if (state->all_isset)
3697  return state->all_isdeferred;
3698  }
3699 
3700  /*
3701  * Otherwise return the default state for the trigger.
3702  */
3703  return ((evtshared->ats_event & AFTER_TRIGGER_INITDEFERRED) != 0);
3704 }
TriggerEvent ats_event
Definition: trigger.c:3392
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:114
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:113
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3333
unsigned int Oid
Definition: postgres_ext.h:31
SetConstraintState state
Definition: trigger.c:3559
Definition: regguts.h:298
int i
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
static void afterTriggerDeleteHeadEventChunk ( AfterTriggersQueryData qs)
static

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

3893 {
3894  AfterTriggerEventChunk *target = qs->events.head;
3895  ListCell *lc;
3896 
3897  Assert(target && target->next);
3898 
3899  /*
3900  * First, update any pointers in the per-table data, so that they won't be
3901  * dangling. Resetting obsoleted pointers to NULL will make
3902  * cancel_prior_stmt_triggers start from the list head, which is fine.
3903  */
3904  foreach(lc, qs->tables)
3905  {
3907 
3908  if (table->after_trig_done &&
3909  table->after_trig_events.tail == target)
3910  {
3911  table->after_trig_events.head = NULL;
3912  table->after_trig_events.tail = NULL;
3913  table->after_trig_events.tailfree = NULL;
3914  }
3915  }
3916 
3917  /* Now we can flush the head chunk */
3918  qs->events.head = target->next;
3919  pfree(target);
3920 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
struct AfterTriggerEventChunk * next
Definition: trigger.c:3440
void pfree(void *pointer)
Definition: mcxt.c:949
AfterTriggerEventList after_trig_events
Definition: trigger.c:3597
#define Assert(condition)
Definition: c.h:664
#define lfirst(lc)
Definition: pg_list.h:106
AfterTriggerEventChunk * head
Definition: trigger.c:3452
AfterTriggerEventList events
Definition: trigger.c:3575
void AfterTriggerEndQuery ( EState estate)

Definition at line 4560 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(), ExecuteTruncate(), and standard_ExecutorFinish().

4561 {
4563 
4564  /* Must be inside a query, too */
4566 
4567  /*
4568  * If we never even got as far as initializing the event stack, there
4569  * certainly won't be any events, so exit quickly.
4570  */
4572  {
4574  return;
4575  }
4576 
4577  /*
4578  * Process all immediate-mode triggers queued by the query, and move the
4579  * deferred ones to the main list of deferred events.
4580  *
4581  * Notice that we decide which ones will be fired, and put the deferred
4582  * ones on the main list, before anything is actually fired. This ensures
4583  * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
4584  * IMMEDIATE: all events we have decided to defer will be available for it
4585  * to fire.
4586  *
4587  * We loop in case a trigger queues more events at the same query level.
4588  * Ordinary trigger functions, including all PL/pgSQL trigger functions,
4589  * will instead fire any triggers in a dedicated query level. Foreign key
4590  * enforcement triggers do add to the current query level, thanks to their
4591  * passing fire_triggers = false to SPI_execute_snapshot(). Other
4592  * C-language triggers might do likewise.
4593  *
4594  * If we find no firable events, we don't have to increment
4595  * firing_counter.
4596  */
4598 
4599  for (;;)
4600  {
4602  {
4603  CommandId firing_id = afterTriggers.firing_counter++;
4604  AfterTriggerEventChunk *oldtail = qs->events.tail;
4605 
4606  if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
4607  break; /* all fired */
4608 
4609  /*
4610  * Firing a trigger could result in query_stack being repalloc'd,
4611  * so we must recalculate qs after each afterTriggerInvokeEvents
4612  * call. Furthermore, it's unsafe to pass delete_ok = true here,
4613  * because that could cause afterTriggerInvokeEvents to try to
4614  * access qs->events after the stack has been repalloc'd.
4615  */
4617 
4618  /*
4619  * We'll need to scan the events list again. To reduce the cost
4620  * of doing so, get rid of completely-fired chunks. We know that
4621  * all events were marked IN_PROGRESS or DONE at the conclusion of
4622  * afterTriggerMarkEvents, so any still-interesting events must
4623  * have been added after that, and so must be in the chunk that
4624  * was then the tail chunk, or in later chunks. So, zap all
4625  * chunks before oldtail. This is approximately the same set of
4626  * events we would have gotten rid of by passing delete_ok = true.
4627  */
4628  Assert(oldtail != NULL);
4629  while (qs->events.head != oldtail)
4631  }
4632  else
4633  break;
4634  }
4635 
4636  /* Release query-level-local storage, including tuplestores if any */
4638 
4640 }
uint32 CommandId
Definition: c.h:405
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition: trigger.c:3892
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4212
CommandId firing_counter
Definition: trigger.c:3558
#define Assert(condition)
Definition: c.h:664
AfterTriggerEventChunk * head
Definition: trigger.c:3452
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4140
AfterTriggerEventList events
Definition: trigger.c:3560
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4651
AfterTriggerEventList events
Definition: trigger.c:3575
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
void AfterTriggerEndSubXact ( bool  isCommit)

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

4857 {
4858  int my_level = GetCurrentTransactionNestLevel();
4860  AfterTriggerEvent event;
4861  AfterTriggerEventChunk *chunk;
4862  CommandId subxact_firing_id;
4863 
4864  /*
4865  * Pop the prior state if needed.
4866  */
4867  if (isCommit)
4868  {
4869  Assert(my_level < afterTriggers.maxtransdepth);
4870  /* If we saved a prior state, we don't need it anymore */
4871  state = afterTriggers.trans_stack[my_level].state;
4872  if (state != NULL)
4873  pfree(state);
4874  /* this avoids double pfree if error later: */
4875  afterTriggers.trans_stack[my_level].state = NULL;
4878  }
4879  else
4880  {
4881  /*
4882  * Aborting. It is possible subxact start failed before calling
4883  * AfterTriggerBeginSubXact, in which case we mustn't risk touching
4884  * trans_stack levels that aren't there.
4885  */
4886  if (my_level >= afterTriggers.maxtransdepth)
4887  return;
4888 
4889  /*
4890  * Release query-level storage for queries being aborted, and restore
4891  * query_depth to its pre-subxact value. This assumes that a
4892  * subtransaction will not add events to query levels started in a
4893  * earlier transaction state.
4894  */
4896  {
4900  }
4903 
4904  /*
4905  * Restore the global deferred-event list to its former length,
4906  * discarding any events queued by the subxact.
4907  */
4909  &afterTriggers.trans_stack[my_level].events);
4910 
4911  /*
4912  * Restore the trigger state. If the saved state is NULL, then this
4913  * subxact didn't save it, so it doesn't need restoring.
4914  */
4915  state = afterTriggers.trans_stack[my_level].state;
4916  if (state != NULL)
4917  {
4919  afterTriggers.state = state;
4920  }
4921  /* this avoids double pfree if error later: */
4922  afterTriggers.trans_stack[my_level].state = NULL;
4923 
4924  /*
4925  * Scan for any remaining deferred events that were marked DONE or IN
4926  * PROGRESS by this subxact or a child, and un-mark them. We can
4927  * recognize such events because they have a firing ID greater than or
4928  * equal to the firing_counter value we saved at subtransaction start.
4929  * (This essentially assumes that the current subxact includes all
4930  * subxacts started after it.)
4931  */
4932  subxact_firing_id = afterTriggers.trans_stack[my_level].firing_counter;
4934  {
4935  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4936 
4937  if (event->ate_flags &
4939  {
4940  if (evtshared->ats_firing_id >= subxact_firing_id)
4941  event->ate_flags &=
4943  }
4944  }
4945  }
4946 }
uint32 CommandId
Definition: c.h:405
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
TriggerFlags ate_flags
Definition: trigger.c:3403
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3465
AfterTriggerEventList events
Definition: trigger.c:3584
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
CommandId firing_counter
Definition: trigger.c:3586
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
void pfree(void *pointer)
Definition: mcxt.c:949
SetConstraintState state
Definition: trigger.c:3559
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:762
#define Assert(condition)
Definition: c.h:664
Definition: regguts.h:298
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition: trigger.c:3852
CommandId ats_firing_id
Definition: trigger.c:3395
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3380
AfterTriggerEventList events
Definition: trigger.c:3560
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition: trigger.c:4651
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
SetConstraintState state
Definition: trigger.c:3583
void AfterTriggerEndXact ( bool  isCommit)

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

4761 {
4762  /*
4763  * Forget the pending-events list.
4764  *
4765  * Since all the info is in TopTransactionContext or children thereof, we
4766  * don't really need to do anything to reclaim memory. However, the
4767  * pending-events list could be large, and so it's useful to discard it as
4768  * soon as possible --- especially if we are aborting because we ran out
4769  * of memory for the list!
4770  */
4772  {
4774  afterTriggers.event_cxt = NULL;
4775  afterTriggers.events.head = NULL;
4776  afterTriggers.events.tail = NULL;
4777  afterTriggers.events.tailfree = NULL;
4778  }
4779 
4780  /*
4781  * Forget any subtransaction state as well. Since this can't be very
4782  * large, we let the eventual reset of TopTransactionContext free the
4783  * memory instead of doing it here.
4784  */
4785  afterTriggers.trans_stack = NULL;
4787 
4788 
4789  /*
4790  * Forget the query stack and constraint-related state information. As
4791  * with the subtransaction state information, we don't bother freeing the
4792  * memory here.
4793  */
4794  afterTriggers.query_stack = NULL;
4796  afterTriggers.state = NULL;
4797 
4798  /* No more afterTriggers manipulation until next transaction starts. */
4800 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
SetConstraintState state
Definition: trigger.c:3559
AfterTriggerEventChunk * head
Definition: trigger.c:3452
MemoryContext event_cxt
Definition: trigger.c:3561
AfterTriggerEventList events
Definition: trigger.c:3560
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
static void AfterTriggerEnlargeQueryState ( void  )
static

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

4959 {
4960  int init_depth = afterTriggers.maxquerydepth;
4961 
4963 
4964  if (afterTriggers.maxquerydepth == 0)
4965  {
4966  int new_alloc = Max(afterTriggers.query_depth + 1, 8);
4967 
4970  new_alloc * sizeof(AfterTriggersQueryData));
4971  afterTriggers.maxquerydepth = new_alloc;
4972  }
4973  else
4974  {
4975  /* repalloc will keep the stack in the same context */
4976  int old_alloc = afterTriggers.maxquerydepth;
4977  int new_alloc = Max(afterTriggers.query_depth + 1,
4978  old_alloc * 2);
4979 
4982  new_alloc * sizeof(AfterTriggersQueryData));
4983  afterTriggers.maxquerydepth = new_alloc;
4984  }
4985 
4986  /* Initialize new array entries to empty */
4987  while (init_depth < afterTriggers.maxquerydepth)
4988  {
4990 
4991  qs->events.head = NULL;
4992  qs->events.tail = NULL;
4993  qs->events.tailfree = NULL;
4994  qs->fdw_tuplestore = NULL;
4995  qs->tables = NIL;
4996 
4997  ++init_depth;
4998  }
4999 }
#define NIL
Definition: pg_list.h:69
MemoryContext TopTransactionContext
Definition: mcxt.c:48
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3576
#define Max(x, y)
Definition: c.h:789
#define Assert(condition)
Definition: c.h:664
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:962
AfterTriggerEventChunk * head
Definition: trigger.c:3452
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:706
AfterTriggerEventList events
Definition: trigger.c:3575
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
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 3946 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().

3952 {
3953  AfterTriggerShared evtshared = GetTriggerSharedData(event);
3954  Oid tgoid = evtshared->ats_tgoid;
3955  TriggerData LocTriggerData;
3956  HeapTupleData tuple1;
3957  HeapTupleData tuple2;
3958  HeapTuple rettuple;
3959  Buffer buffer1 = InvalidBuffer;
3960  Buffer buffer2 = InvalidBuffer;
3961  int tgindx;
3962 
3963  /*
3964  * Locate trigger in trigdesc.
3965  */
3966  LocTriggerData.tg_trigger = NULL;
3967  for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
3968  {
3969  if (trigdesc->triggers[tgindx].tgoid == tgoid)
3970  {
3971  LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
3972  break;
3973  }
3974  }
3975  if (LocTriggerData.tg_trigger == NULL)
3976  elog(ERROR, "could not find trigger %u", tgoid);
3977 
3978  /*
3979  * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
3980  * to include time spent re-fetching tuples in the trigger cost.
3981  */
3982  if (instr)
3983  InstrStartNode(instr + tgindx);
3984 
3985  /*
3986  * Fetch the required tuple(s).
3987  */
3988  switch (event->ate_flags & AFTER_TRIGGER_TUP_BITS)
3989  {
3991  {
3992  Tuplestorestate *fdw_tuplestore = GetCurrentFDWTuplestore();
3993 
3994  if (!tuplestore_gettupleslot(fdw_tuplestore, true, false,
3995  trig_tuple_slot1))
3996  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
3997 
3998  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4000  !tuplestore_gettupleslot(fdw_tuplestore, true, false,
4001  trig_tuple_slot2))
4002  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4003  }
4004  /* fall through */
4006 
4007  /*
4008  * Using ExecMaterializeSlot() rather than ExecFetchSlotTuple()
4009  * ensures that tg_trigtuple does not reference tuplestore memory.
4010  * (It is formally possible for the trigger function to queue
4011  * trigger events that add to the same tuplestore, which can push
4012  * other tuples out of memory.) The distinction is academic,
4013  * because we start with a minimal tuple that ExecFetchSlotTuple()
4014  * must materialize anyway.
4015  */
4016  LocTriggerData.tg_trigtuple =
4017  ExecMaterializeSlot(trig_tuple_slot1);
4018  LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
4019 
4020  LocTriggerData.tg_newtuple =
4021  ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
4023  ExecMaterializeSlot(trig_tuple_slot2) : NULL;
4024  LocTriggerData.tg_newtuplebuf = InvalidBuffer;
4025 
4026  break;
4027 
4028  default:
4029  if (ItemPointerIsValid(&(event->ate_ctid1)))
4030  {
4031  ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
4032  if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
4033  elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
4034  LocTriggerData.tg_trigtuple = &tuple1;
4035  LocTriggerData.tg_trigtuplebuf = buffer1;
4036  }
4037  else
4038  {
4039  LocTriggerData.tg_trigtuple = NULL;
4040  LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
4041  }
4042 
4043  /* don't touch ctid2 if not there */
4044  if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) ==
4046  ItemPointerIsValid(&(event->ate_ctid2)))
4047  {
4048  ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
4049  if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
4050  elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
4051  LocTriggerData.tg_newtuple = &tuple2;
4052  LocTriggerData.tg_newtuplebuf = buffer2;
4053  }
4054  else
4055  {
4056  LocTriggerData.tg_newtuple = NULL;
4057  LocTriggerData.tg_newtuplebuf = InvalidBuffer;
4058  }
4059  }
4060 
4061  /*
4062  * Set up the tuplestore information to let the trigger have access to
4063  * transition tables. When we first make a transition table available to
4064  * a trigger, mark it "closed" so that it cannot change anymore. If any
4065  * additional events of the same type get queued in the current trigger
4066  * query level, they'll go into new transition tables.
4067  */
4068  LocTriggerData.tg_oldtable = LocTriggerData.tg_newtable = NULL;
4069  if (evtshared->ats_table)
4070  {
4071  if (LocTriggerData.tg_trigger->tgoldtable)
4072  {
4073  LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
4074  evtshared->ats_table->closed = true;
4075  }
4076 
4077  if (LocTriggerData.tg_trigger->tgnewtable)
4078  {
4079  LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
4080  evtshared->ats_table->closed = true;
4081  }
4082  }
4083 
4084  /*
4085  * Setup the remaining trigger information
4086  */
4087  LocTriggerData.type = T_TriggerData;
4088  LocTriggerData.tg_event =
4090  LocTriggerData.tg_relation = rel;
4091 
4092  MemoryContextReset(per_tuple_context);
4093 
4094  /*
4095  * Call the trigger and throw away any possibly returned updated tuple.
4096  * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
4097  */
4098  rettuple = ExecCallTriggerFunc(&LocTriggerData,
4099  tgindx,
4100  finfo,
4101  NULL,
4102  per_tuple_context);
4103  if (rettuple != NULL &&
4104  rettuple != LocTriggerData.tg_trigtuple &&
4105  rettuple != LocTriggerData.tg_newtuple)
4106  heap_freetuple(rettuple);
4107 
4108  /*
4109  * Release buffers
4110  */
4111  if (buffer1 != InvalidBuffer)
4112  ReleaseBuffer(buffer1);
4113  if (buffer2 != InvalidBuffer)
4114  ReleaseBuffer(buffer2);
4115 
4116  /*
4117  * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
4118  * one "tuple returned" (really the number of firings).
4119  */
4120  if (instr)
4121  InstrStopNode(instr + tgindx, 1);
4122 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:60
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
TriggerEvent ats_event
Definition: trigger.c:3392
void InstrStopNode(Instrumentation *instr, double nTuples)
Definition: instrument.c:80
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3382
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3626
ItemPointerData ate_ctid2
Definition: trigger.c:3405
TriggerFlags ate_flags
Definition: trigger.c:3403
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:1876
Oid tgoid
Definition: reltrigger.h:25
#define AFTER_TRIGGER_TUP_BITS
Definition: trigger.c:3386
#define InvalidBuffer
Definition: buf.h:25
Tuplestorestate * old_tuplestore
Definition: trigger.c:3598
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:135
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
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:3428
#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:3599
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3385
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:3396
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:3383
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:725
#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:2126
ItemPointerData ate_ctid1
Definition: trigger.c:3404
char * tgoldtable
Definition: reltrigger.h:42
int Buffer
Definition: buf.h:23
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:139
Relation tg_relation
Definition: trigger.h:34
void AfterTriggerFireDeferred ( void  )

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

4705 {
4706  AfterTriggerEventList *events;
4707  bool snap_pushed = false;
4708 
4709  /* Must not be inside a query */
4711 
4712  /*
4713  * If there are any triggers to fire, make sure we have set a snapshot for
4714  * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
4715  * can't assume ActiveSnapshot is valid on entry.)
4716  */
4717  events = &afterTriggers.events;
4718  if (events->head != NULL)
4719  {
4721  snap_pushed = true;
4722  }
4723 
4724  /*
4725  * Run all the remaining triggers. Loop until they are all gone, in case
4726  * some trigger queues more for us to do.
4727  */
4728  while (afterTriggerMarkEvents(events, NULL, false))
4729  {
4730  CommandId firing_id = afterTriggers.firing_counter++;
4731 
4732  if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
4733  break; /* all fired */
4734  }
4735 
4736  /*
4737  * We don't bother freeing the event list, since it will go away anyway
4738  * (and more efficiently than via pfree) in AfterTriggerEndXact.
4739  */
4740 
4741  if (snap_pushed)
4743 }
uint32 CommandId
Definition: c.h:405
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4212
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:3558
#define Assert(condition)
Definition: c.h:664
AfterTriggerEventChunk * head
Definition: trigger.c:3452
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4140
AfterTriggerEventList events
Definition: trigger.c:3560
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
static void afterTriggerFreeEventList ( AfterTriggerEventList events)
static

Definition at line 3831 of file trigger.c.

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

Referenced by AfterTriggerFreeQuery(), and afterTriggerRestoreEventList().

3832 {
3833  AfterTriggerEventChunk *chunk;
3834 
3835  while ((chunk = events->head) != NULL)
3836  {
3837  events->head = chunk->next;
3838  pfree(chunk);
3839  }
3840  events->tail = NULL;
3841  events->tailfree = NULL;
3842 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
struct AfterTriggerEventChunk * next
Definition: trigger.c:3440
void pfree(void *pointer)
Definition: mcxt.c:949
AfterTriggerEventChunk * head
Definition: trigger.c:3452
static void AfterTriggerFreeQuery ( AfterTriggersQueryData qs)
static

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

4652 {
4653  Tuplestorestate *ts;
4654  List *tables;
4655  ListCell *lc;
4656 
4657  /* Drop the trigger events */
4659 
4660  /* Drop FDW tuplestore if any */
4661  ts = qs->fdw_tuplestore;
4662  qs->fdw_tuplestore = NULL;
4663  if (ts)
4664  tuplestore_end(ts);
4665 
4666  /* Release per-table subsidiary storage */
4667  tables = qs->tables;
4668  foreach(lc, tables)
4669  {
4671 
4672  ts = table->old_tuplestore;
4673  table->old_tuplestore = NULL;
4674  if (ts)
4675  tuplestore_end(ts);
4676  ts = table->new_tuplestore;
4677  table->new_tuplestore = NULL;
4678  if (ts)
4679  tuplestore_end(ts);
4680  }
4681 
4682  /*
4683  * Now free the AfterTriggersTableData structs and list cells. Reset list
4684  * pointer first; if list_free_deep somehow gets an error, better to leak
4685  * that storage than have an infinite loop.
4686  */
4687  qs->tables = NIL;
4688  list_free_deep(tables);
4689 }
#define NIL
Definition: pg_list.h:69
Tuplestorestate * old_tuplestore
Definition: trigger.c:3598
void list_free_deep(List *list)
Definition: list.c:1147
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3831
Tuplestorestate * fdw_tuplestore
Definition: trigger.c:3576
Tuplestorestate * new_tuplestore
Definition: trigger.c:3599
#define lfirst(lc)
Definition: pg_list.h:106
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
AfterTriggerEventList events
Definition: trigger.c:3575
Definition: pg_list.h:45
static bool afterTriggerInvokeEvents ( AfterTriggerEventList events,
CommandId  firing_id,
EState estate,
bool  delete_ok 
)
static

Definition at line 4212 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, RELKIND_FOREIGN_TABLE, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigFunctions, ResultRelInfo::ri_TrigInstrument, AfterTriggerEventList::tail, and AfterTriggerEventList::tailfree.

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

4216 {
4217  bool all_fired = true;
4218  AfterTriggerEventChunk *chunk;
4219  MemoryContext per_tuple_context;
4220  bool local_estate = false;
4221  Relation rel = NULL;
4222  TriggerDesc *trigdesc = NULL;
4223  FmgrInfo *finfo = NULL;
4224  Instrumentation *instr = NULL;
4225  TupleTableSlot *slot1 = NULL,
4226  *slot2 = NULL;
4227 
4228  /* Make a local EState if need be */
4229  if (estate == NULL)
4230  {
4231  estate = CreateExecutorState();
4232  local_estate = true;
4233  }
4234 
4235  /* Make a per-tuple memory context for trigger function calls */
4236  per_tuple_context =
4238  "AfterTriggerTupleContext",
4240 
4241  for_each_chunk(chunk, *events)
4242  {
4243  AfterTriggerEvent event;
4244  bool all_fired_in_chunk = true;
4245 
4246  for_each_event(event, chunk)
4247  {
4248  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4249 
4250  /*
4251  * Is it one for me to fire?
4252  */
4253  if ((event->ate_flags & AFTER_TRIGGER_IN_PROGRESS) &&
4254  evtshared->ats_firing_id == firing_id)
4255  {
4256  /*
4257  * So let's fire it... but first, find the correct relation if
4258  * this is not the same relation as before.
4259  */
4260  if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
4261  {
4262  ResultRelInfo *rInfo;
4263 
4264  rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
4265  rel = rInfo->ri_RelationDesc;
4266  trigdesc = rInfo->ri_TrigDesc;
4267  finfo = rInfo->ri_TrigFunctions;
4268  instr = rInfo->ri_TrigInstrument;
4269  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
4270  {
4271  if (slot1 != NULL)
4272  {
4275  }
4276  slot1 = MakeSingleTupleTableSlot(rel->rd_att);
4277  slot2 = MakeSingleTupleTableSlot(rel->rd_att);
4278  }
4279  if (trigdesc == NULL) /* should not happen */
4280  elog(ERROR, "relation %u has no triggers",
4281  evtshared->ats_relid);
4282  }
4283 
4284  /*
4285  * Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is
4286  * still set, so recursive examinations of the event list
4287  * won't try to re-fire it.
4288  */
4289  AfterTriggerExecute(event, rel, trigdesc, finfo, instr,
4290  per_tuple_context, slot1, slot2);
4291 
4292  /*
4293  * Mark the event as done.
4294  */
4295  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
4296  event->ate_flags |= AFTER_TRIGGER_DONE;
4297  }
4298  else if (!(event->ate_flags & AFTER_TRIGGER_DONE))
4299  {
4300  /* something remains to be done */
4301  all_fired = all_fired_in_chunk = false;
4302  }
4303  }
4304 
4305  /* Clear the chunk if delete_ok and nothing left of interest */
4306  if (delete_ok && all_fired_in_chunk)
4307  {
4308  chunk->freeptr = CHUNK_DATA_START(chunk);
4309  chunk->endfree = chunk->endptr;
4310 
4311  /*
4312  * If it's last chunk, must sync event list's tailfree too. Note
4313  * that delete_ok must NOT be passed as true if there could be
4314  * additional AfterTriggerEventList values pointing at this event
4315  * list, since we'd fail to fix their copies of tailfree.
4316  */
4317  if (chunk == events->tail)
4318  events->tailfree = chunk->freeptr;
4319  }
4320  }
4321  if (slot1 != NULL)
4322  {
4325  }
4326 
4327  /* Release working resources */
4328  MemoryContextDelete(per_tuple_context);
4329 
4330  if (local_estate)
4331  {
4332  ExecCleanUpTriggerState(estate);
4333  FreeExecutorState(estate);
4334  }
4335 
4336  return all_fired;
4337 }
Definition: fmgr.h:56
Relation ri_RelationDesc
Definition: execnodes.h:354
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
TriggerFlags ate_flags
Definition: trigger.c:3403
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3447
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:375
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
Form_pg_class rd_rel
Definition: rel.h:114
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
#define for_each_event(eptr, cptr)
Definition: trigger.c:3460
void FreeExecutorState(EState *estate)
Definition: execUtils.c:183
#define ERROR
Definition: elog.h:43
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:165
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:216
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc)
Definition: execTuples.c:199
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:366
EState * CreateExecutorState(void)
Definition: execUtils.c:80
ResultRelInfo * ExecGetTriggerResultRel(EState *estate, Oid relid)
Definition: execMain.c:1390
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:322
TupleDesc rd_att
Definition: rel.h:115
#define for_each_chunk(cptr, evtlist)
Definition: trigger.c:3458
CommandId ats_firing_id
Definition: trigger.c:3395
void ExecCleanUpTriggerState(EState *estate)
Definition: execMain.c:1468
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3380
#define elog
Definition: elog.h:219
#define RelationGetRelid(relation)
Definition: rel.h:416
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:3946
FmgrInfo * ri_TrigFunctions
Definition: execnodes.h:369
static bool afterTriggerMarkEvents ( AfterTriggerEventList events,
AfterTriggerEventList move_list,
bool  immediate_only 
)
static

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

4143 {
4144  bool found = false;
4145  AfterTriggerEvent event;
4146  AfterTriggerEventChunk *chunk;
4147 
4148  for_each_event_chunk(event, chunk, *events)
4149  {
4150  AfterTriggerShared evtshared = GetTriggerSharedData(event);
4151  bool defer_it = false;
4152 
4153  if (!(event->ate_flags &
4155  {
4156  /*
4157  * This trigger hasn't been called or scheduled yet. Check if we
4158  * should call it now.
4159  */
4160  if (immediate_only && afterTriggerCheckState(evtshared))
4161  {
4162  defer_it = true;
4163  }
4164  else
4165  {
4166  /*
4167  * Mark it as to be fired in this firing cycle.
4168  */
4170  event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS;
4171  found = true;
4172  }
4173  }
4174 
4175  /*
4176  * If it's deferred, move it to move_list, if requested.
4177  */
4178  if (defer_it && move_list != NULL)
4179  {
4180  /* add it to move_list */
4181  afterTriggerAddEvent(move_list, event, evtshared);
4182  /* mark original copy "done" so we don't do it again */
4183  event->ate_flags |= AFTER_TRIGGER_DONE;
4184  }
4185  }
4186 
4187  return found;
4188 }
TriggerFlags ate_flags
Definition: trigger.c:3403
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3465
static bool afterTriggerCheckState(AfterTriggerShared evtshared)
Definition: trigger.c:3669
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
CommandId firing_counter
Definition: trigger.c:3558
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3715
CommandId ats_firing_id
Definition: trigger.c:3395
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3380
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
bool AfterTriggerPendingOnRel ( Oid  relid)

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

5375 {
5376  AfterTriggerEvent event;
5377  AfterTriggerEventChunk *chunk;
5378  int depth;
5379 
5380  /* Scan queued events */
5382  {
5383  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5384 
5385  /*
5386  * We can ignore completed events. (Even if a DONE flag is rolled
5387  * back by subxact abort, it's OK because the effects of the TRUNCATE
5388  * or whatever must get rolled back too.)
5389  */
5390  if (event->ate_flags & AFTER_TRIGGER_DONE)
5391  continue;
5392 
5393  if (evtshared->ats_relid == relid)
5394  return true;
5395  }
5396 
5397  /*
5398  * Also scan events queued by incomplete queries. This could only matter
5399  * if TRUNCATE/etc is executed by a function or trigger within an updating
5400  * query on the same relation, which is pretty perverse, but let's check.
5401  */
5402  for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
5403  {
5405  {
5406  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5407 
5408  if (event->ate_flags & AFTER_TRIGGER_DONE)
5409  continue;
5410 
5411  if (evtshared->ats_relid == relid)
5412  return true;
5413  }
5414  }
5415 
5416  return false;
5417 }
TriggerFlags ate_flags
Definition: trigger.c:3403
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition: trigger.c:3465
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
#define GetTriggerSharedData(evt)
Definition: trigger.c:3428
AfterTriggerEventList events
Definition: trigger.c:3560
AfterTriggerEventList events
Definition: trigger.c:3575
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
static void afterTriggerRestoreEventList ( AfterTriggerEventList events,
const AfterTriggerEventList old_events 
)
static

Definition at line 3852 of file trigger.c.

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

Referenced by AfterTriggerEndSubXact().

3854 {
3855  AfterTriggerEventChunk *chunk;
3856  AfterTriggerEventChunk *next_chunk;
3857 
3858  if (old_events->tail == NULL)
3859  {
3860  /* restoring to a completely empty state, so free everything */
3861  afterTriggerFreeEventList(events);
3862  }
3863  else
3864  {
3865  *events = *old_events;
3866  /* free any chunks after the last one we want to keep */
3867  for (chunk = events->tail->next; chunk != NULL; chunk = next_chunk)
3868  {
3869  next_chunk = chunk->next;
3870  pfree(chunk);
3871  }
3872  /* and clean up the tail chunk to be the right length */
3873  events->tail->next = NULL;
3874  events->tail->freeptr = events->tailfree;
3875 
3876  /*
3877  * We don't make any effort to remove now-unused shared data records.
3878  * They might still be useful, anyway.
3879  */
3880  }
3881 }
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
struct AfterTriggerEventChunk * next
Definition: trigger.c:3440
void pfree(void *pointer)
Definition: mcxt.c:949
static void afterTriggerFreeEventList(AfterTriggerEventList *events)
Definition: trigger.c:3831
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 5439 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_FOREIGN_TABLE, 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, TRIGGER_TYPE_AFTER, TRIGGER_TYPE_DELETE, TRIGGER_TYPE_INSERT, TRIGGER_TYPE_MATCHES, TRIGGER_TYPE_ROW, TRIGGER_TYPE_STATEMENT, TRIGGER_TYPE_TRUNCATE, TRIGGER_TYPE_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and tuplestore_puttuple().

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

5444 {
5445  Relation rel = relinfo->ri_RelationDesc;
5446  TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
5447  AfterTriggerEventData new_event;
5448  AfterTriggerSharedData new_shared;
5449  char relkind = rel->rd_rel->relkind;
5450  int tgtype_event;
5451  int tgtype_level;
5452  int i;
5453  Tuplestorestate *fdw_tuplestore = NULL;
5454 
5455  /*
5456  * Check state. We use a normal test not Assert because it is possible to
5457  * reach here in the wrong state given misconfigured RI triggers, in
5458  * particular deferring a cascade action trigger.
5459  */
5460  if (afterTriggers.query_depth < 0)
5461  elog(ERROR, "AfterTriggerSaveEvent() called outside of query");
5462 
5463  /* Be sure we have enough space to record events at this query depth. */
5466 
5467  /*
5468  * If the directly named relation has any triggers with transition tables,
5469  * then we need to capture transition tuples.
5470  */
5471  if (row_trigger && transition_capture != NULL)
5472  {
5473  HeapTuple original_insert_tuple = transition_capture->tcs_original_insert_tuple;
5474  TupleConversionMap *map = transition_capture->tcs_map;
5475  bool delete_old_table = transition_capture->tcs_delete_old_table;
5476  bool update_old_table = transition_capture->tcs_update_old_table;
5477  bool update_new_table = transition_capture->tcs_update_new_table;
5478  bool insert_new_table = transition_capture->tcs_insert_new_table;;
5479 
5480  if ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
5481  (event == TRIGGER_EVENT_UPDATE && update_old_table))
5482  {
5483  Tuplestorestate *old_tuplestore;
5484 
5485  Assert(oldtup != NULL);
5486  old_tuplestore = transition_capture->tcs_private->old_tuplestore;
5487 
5488  if (map != NULL)
5489  {
5490  HeapTuple converted = do_convert_tuple(oldtup, map);
5491 
5492  tuplestore_puttuple(old_tuplestore, converted);
5493  pfree(converted);
5494  }
5495  else
5496  tuplestore_puttuple(old_tuplestore, oldtup);
5497  }
5498  if ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
5499  (event == TRIGGER_EVENT_UPDATE && update_new_table))
5500  {
5501  Tuplestorestate *new_tuplestore;
5502 
5503  Assert(newtup != NULL);
5504  new_tuplestore = transition_capture->tcs_private->new_tuplestore;
5505 
5506  if (original_insert_tuple != NULL)
5507  tuplestore_puttuple(new_tuplestore, original_insert_tuple);
5508  else if (map != NULL)
5509  {
5510  HeapTuple converted = do_convert_tuple(newtup, map);
5511 
5512  tuplestore_puttuple(new_tuplestore, converted);
5513  pfree(converted);
5514  }
5515  else
5516  tuplestore_puttuple(new_tuplestore, newtup);
5517  }
5518 
5519  /* If transition tables are the only reason we're here, return. */
5520  if (trigdesc == NULL ||
5521  (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
5522  (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
5523  (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row))
5524  return;
5525  }
5526 
5527  /*
5528  * Validate the event code and collect the associated tuple CTIDs.
5529  *
5530  * The event code will be used both as a bitmask and an array offset, so
5531  * validation is important to make sure we don't walk off the edge of our
5532  * arrays.
5533  *
5534  * Also, if we're considering statement-level triggers, check whether we
5535  * already queued a set of them for this event, and cancel the prior set
5536  * if so. This preserves the behavior that statement-level triggers fire
5537  * just once per statement and fire after row-level triggers.
5538  */
5539  switch (event)
5540  {
5541  case TRIGGER_EVENT_INSERT:
5542  tgtype_event = TRIGGER_TYPE_INSERT;
5543  if (row_trigger)
5544  {
5545  Assert(oldtup == NULL);
5546  Assert(newtup != NULL);
5547  ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid1));
5548  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5549  }
5550  else
5551  {
5552  Assert(oldtup == NULL);
5553  Assert(newtup == NULL);
5554  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5555  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5557  CMD_INSERT, event);
5558  }
5559  break;
5560  case TRIGGER_EVENT_DELETE:
5561  tgtype_event = TRIGGER_TYPE_DELETE;
5562  if (row_trigger)
5563  {
5564  Assert(oldtup != NULL);
5565  Assert(newtup == NULL);
5566  ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
5567  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5568  }
5569  else
5570  {
5571  Assert(oldtup == NULL);
5572  Assert(newtup == NULL);
5573  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5574  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5576  CMD_DELETE, event);
5577  }
5578  break;
5579  case TRIGGER_EVENT_UPDATE:
5580  tgtype_event = TRIGGER_TYPE_UPDATE;
5581  if (row_trigger)
5582  {
5583  Assert(oldtup != NULL);
5584  Assert(newtup != NULL);
5585  ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
5586  ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid2));
5587  }
5588  else
5589  {
5590  Assert(oldtup == NULL);
5591  Assert(newtup == NULL);
5592  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5593  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5595  CMD_UPDATE, event);
5596  }
5597  break;
5599  tgtype_event = TRIGGER_TYPE_TRUNCATE;
5600  Assert(oldtup == NULL);
5601  Assert(newtup == NULL);
5602  ItemPointerSetInvalid(&(new_event.ate_ctid1));
5603  ItemPointerSetInvalid(&(new_event.ate_ctid2));
5604  break;
5605  default:
5606  elog(ERROR, "invalid after-trigger event code: %d", event);
5607  tgtype_event = 0; /* keep compiler quiet */
5608  break;
5609  }
5610 
5611  if (!(relkind == RELKIND_FOREIGN_TABLE && row_trigger))
5612  new_event.ate_flags = (row_trigger && event == TRIGGER_EVENT_UPDATE) ?
5614  /* else, we'll initialize ate_flags for each trigger */
5615 
5616  tgtype_level = (row_trigger ? TRIGGER_TYPE_ROW : TRIGGER_TYPE_STATEMENT);
5617 
5618  for (i = 0; i < trigdesc->numtriggers; i++)
5619  {
5620  Trigger *trigger = &trigdesc->triggers[i];
5621 
5622  if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
5623  tgtype_level,
5625  tgtype_event))
5626  continue;
5627  if (!TriggerEnabled(estate, relinfo, trigger, event,
5628  modifiedCols, oldtup, newtup))
5629  continue;
5630 
5631  if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
5632  {
5633  if (fdw_tuplestore == NULL)
5634  {
5635  fdw_tuplestore = GetCurrentFDWTuplestore();
5636  new_event.ate_flags = AFTER_TRIGGER_FDW_FETCH;
5637  }
5638  else
5639  /* subsequent event for the same tuple */
5640  new_event.ate_flags = AFTER_TRIGGER_FDW_REUSE;
5641  }
5642 
5643  /*
5644  * If the trigger is a foreign key enforcement trigger, there are
5645  * certain cases where we can skip queueing the event because we can
5646  * tell by inspection that the FK constraint will still pass.
5647  */
5648  if (TRIGGER_FIRED_BY_UPDATE(event))
5649  {
5650  switch (RI_FKey_trigger_type(trigger->tgfoid))
5651  {
5652  case RI_TRIGGER_PK:
5653  /* Update on trigger's PK table */
5654  if (!RI_FKey_pk_upd_check_required(trigger, rel,
5655  oldtup, newtup))
5656  {
5657  /* skip queuing this event */
5658  continue;
5659  }
5660  break;
5661 
5662  case RI_TRIGGER_FK:
5663  /* Update on trigger's FK table */
5664  if (!RI_FKey_fk_upd_check_required(trigger, rel,
5665  oldtup, newtup))
5666  {
5667  /* skip queuing this event */
5668  continue;
5669  }
5670  break;
5671 
5672  case RI_TRIGGER_NONE:
5673  /* Not an FK trigger */
5674  break;
5675  }
5676  }
5677 
5678  /*
5679  * If the trigger is a deferred unique constraint check trigger, only
5680  * queue it if the unique constraint was potentially violated, which
5681  * we know from index insertion time.
5682  */
5683  if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
5684  {
5685  if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
5686  continue; /* Uniqueness definitely not violated */
5687  }
5688 
5689  /*
5690  * Fill in event structure and add it to the current query's queue.
5691  * Note we set ats_table to NULL whenever this trigger doesn't use
5692  * transition tables, to improve sharability of the shared event data.
5693  */
5694  new_shared.ats_event =
5695  (event & TRIGGER_EVENT_OPMASK) |
5696  (row_trigger ? TRIGGER_EVENT_ROW : 0) |
5697  (trigger->tgdeferrable ? AFTER_TRIGGER_DEFERRABLE : 0) |
5698  (trigger->tginitdeferred ? AFTER_TRIGGER_INITDEFERRED : 0);
5699  new_shared.ats_tgoid = trigger->tgoid;
5700  new_shared.ats_relid = RelationGetRelid(rel);
5701  new_shared.ats_firing_id = 0;
5702  if ((trigger->tgoldtable || trigger->tgnewtable) &&
5703  transition_capture != NULL)
5704  new_shared.ats_table = transition_capture->tcs_private;
5705  else
5706  new_shared.ats_table = NULL;
5707 
5709  &new_event, &new_shared);
5710  }
5711 
5712  /*
5713  * Finally, spool any foreign tuple(s). The tuplestore squashes them to
5714  * minimal tuples, so this loses any system columns. The executor lost
5715  * those columns before us, for an unrelated reason, so this is fine.
5716  */
5717  if (fdw_tuplestore)
5718  {
5719  if (oldtup != NULL)
5720  tuplestore_puttuple(fdw_tuplestore, oldtup);
5721  if (newtup != NULL)
5722  tuplestore_puttuple(fdw_tuplestore, newtup);
5723  }
5724 }
#define TRIGGER_EVENT_ROW
Definition: trigger.h:104
TriggerEvent ats_event
Definition: trigger.c:3392
Relation ri_RelationDesc
Definition: execnodes.h:354
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3701
#define AFTER_TRIGGER_FDW_REUSE
Definition: trigger.c:3382
#define AFTER_TRIGGER_INITDEFERRED
Definition: trigger.h:114
static Tuplestorestate * GetCurrentFDWTuplestore(void)
Definition: trigger.c:3626
ItemPointerData ate_ctid2
Definition: trigger.c:3405
#define TRIGGER_TYPE_DELETE
Definition: pg_trigger.h:101
#define TRIGGER_EVENT_DELETE
Definition: trigger.h:99
Oid tgfoid
Definition: reltrigger.h:28
TriggerFlags ate_flags
Definition: trigger.c:3403
static void cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
Definition: trigger.c:5777
#define AFTER_TRIGGER_DEFERRABLE
Definition: trigger.h:113
#define TRIGGER_TYPE_MATCHES(type, level, timing, event)
Definition: pg_trigger.h:146
Oid tgoid
Definition: reltrigger.h:25
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
Tuplestorestate * old_tuplestore
Definition: trigger.c:3598
Form_pg_class rd_rel
Definition: rel.h:114
#define TRIGGER_EVENT_OPMASK
Definition: trigger.h:102
HeapTuple tcs_original_insert_tuple
Definition: trigger.h:82
void pfree(void *pointer)
Definition: mcxt.c:949
struct AfterTriggersTableData * tcs_private
Definition: trigger.h:87
#define ERROR
Definition: elog.h:43
#define TRIGGER_TYPE_AFTER
Definition: pg_trigger.h:112
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
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
bool trig_insert_after_row
Definition: reltrigger.h:56
#define TRIGGER_TYPE_ROW
Definition: pg_trigger.h:98
#define AFTER_TRIGGER_1CTID
Definition: trigger.c:3384
Tuplestorestate * new_tuplestore
Definition: trigger.c:3599
#define AFTER_TRIGGER_2CTID
Definition: trigger.c:3385
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:366
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:2141
#define TRIGGER_TYPE_TRUNCATE
Definition: pg_trigger.h:103
char * tgnewtable
Definition: reltrigger.h:43
#define TRIGGER_TYPE_UPDATE
Definition: pg_trigger.h:102
struct AfterTriggersTableData * ats_table
Definition: trigger.c:3396
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, HeapTuple oldtup, HeapTuple newtup)
Definition: trigger.c:3151
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4958
#define RI_TRIGGER_FK
Definition: trigger.h:267
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:505
#define Assert(condition)
Definition: c.h:664
static void afterTriggerAddEvent(AfterTriggerEventList *events, AfterTriggerEvent event, AfterTriggerShared evtshared)
Definition: trigger.c:3715
#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:2084
#define TRIGGER_TYPE_INSERT
Definition: pg_trigger.h:100
#define AFTER_TRIGGER_FDW_FETCH
Definition: trigger.c:3383
CommandId ats_firing_id
Definition: trigger.c:3395
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:266
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
int i
#define elog
Definition: elog.h:219
#define TRIGGER_TYPE_STATEMENT
Definition: pg_trigger.h:107
ItemPointerData ate_ctid1
Definition: trigger.c:3404
#define RI_TRIGGER_NONE
Definition: trigger.h:268
char * tgoldtable
Definition: reltrigger.h:42
AfterTriggerEventList events
Definition: trigger.c:3575
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:122
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
#define RelationGetRelid(relation)
Definition: rel.h:416
#define ItemPointerCopy(fromPointer, toPointer)
Definition: itemptr.h:139
void AfterTriggerSetState ( ConstraintsSetStmt stmt)

Definition at line 5080 of file trigger.c.

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, Anum_pg_constraint_conname, Anum_pg_constraint_connamespace, Anum_pg_trigger_tgconstraint, BTEqualStrategyNumber, RangeVar::catalogname, ConstraintNameNspIndexId, ConstraintRelationId, 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, TriggerRelationId, and SetConstraintStateData::trigstates.

Referenced by standard_ProcessUtility().

5081 {
5082  int my_level = GetCurrentTransactionNestLevel();
5083 
5084  /* If we haven't already done so, initialize our state. */
5085  if (afterTriggers.state == NULL)
5087 
5088  /*
5089  * If in a subtransaction, and we didn't save the current state already,
5090  * save it so it can be restored if the subtransaction aborts.
5091  */
5092  if (my_level > 1 &&
5093  afterTriggers.trans_stack[my_level].state == NULL)
5094  {
5095  afterTriggers.trans_stack[my_level].state =
5097  }
5098 
5099  /*
5100  * Handle SET CONSTRAINTS ALL ...
5101  */
5102  if (stmt->constraints == NIL)
5103  {
5104  /*
5105  * Forget any previous SET CONSTRAINTS commands in this transaction.
5106  */
5108 
5109  /*
5110  * Set the per-transaction ALL state to known.
5111  */
5112  afterTriggers.state->all_isset = true;
5114  }
5115  else
5116  {
5117  Relation conrel;
5118  Relation tgrel;
5119  List *conoidlist = NIL;
5120  List *tgoidlist = NIL;
5121  ListCell *lc;
5122 
5123  /*
5124  * Handle SET CONSTRAINTS constraint-name [, ...]
5125  *
5126  * First, identify all the named constraints and make a list of their
5127  * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5128  * the same name within a schema, the specifications are not
5129  * necessarily unique. Our strategy is to target all matching
5130  * constraints within the first search-path schema that has any
5131  * matches, but disregard matches in schemas beyond the first match.
5132  * (This is a bit odd but it's the historical behavior.)
5133  */
5135 
5136  foreach(lc, stmt->constraints)
5137  {
5138  RangeVar *constraint = lfirst(lc);
5139  bool found;
5140  List *namespacelist;
5141  ListCell *nslc;
5142 
5143  if (constraint->catalogname)
5144  {
5145  if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5146  ereport(ERROR,
5147  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5148  errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5149  constraint->catalogname, constraint->schemaname,
5150  constraint->relname)));
5151  }
5152 
5153  /*
5154  * If we're given the schema name with the constraint, look only
5155  * in that schema. If given a bare constraint name, use the
5156  * search path to find the first matching constraint.
5157  */
5158  if (constraint->schemaname)
5159  {
5160  Oid namespaceId = LookupExplicitNamespace(constraint->schemaname,
5161  false);
5162 
5163  namespacelist = list_make1_oid(namespaceId);
5164  }
5165  else
5166  {
5167  namespacelist = fetch_search_path(true);
5168  }
5169 
5170  found = false;
5171  foreach(nslc, namespacelist)
5172  {
5173  Oid namespaceId = lfirst_oid(nslc);
5174  SysScanDesc conscan;
5175  ScanKeyData skey[2];
5176  HeapTuple tup;
5177 
5178  ScanKeyInit(&skey[0],
5180  BTEqualStrategyNumber, F_NAMEEQ,
5181  CStringGetDatum(constraint->relname));
5182  ScanKeyInit(&skey[1],
5184  BTEqualStrategyNumber, F_OIDEQ,
5185  ObjectIdGetDatum(namespaceId));
5186 
5187  conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
5188  true, NULL, 2, skey);
5189 
5190  while (HeapTupleIsValid(tup = systable_getnext(conscan)))
5191  {
5193 
5194  if (con->condeferrable)
5195  conoidlist = lappend_oid(conoidlist,
5196  HeapTupleGetOid(tup));
5197  else if (stmt->deferred)
5198  ereport(ERROR,
5199  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5200  errmsg("constraint \"%s\" is not deferrable",
5201  constraint->relname)));
5202  found = true;
5203  }
5204 
5205  systable_endscan(conscan);
5206 
5207  /*
5208  * Once we've found a matching constraint we do not search
5209  * later parts of the search path.
5210  */
5211  if (found)
5212  break;
5213  }
5214 
5215  list_free(namespacelist);
5216 
5217  /*
5218  * Not found ?
5219  */
5220  if (!found)
5221  ereport(ERROR,
5222  (errcode(ERRCODE_UNDEFINED_OBJECT),
5223  errmsg("constraint \"%s\" does not exist",
5224  constraint->relname)));
5225  }
5226 
5227  heap_close(conrel, AccessShareLock);
5228 
5229  /*
5230  * Now, locate the trigger(s) implementing each of these constraints,
5231  * and make a list of their OIDs.
5232  */
5234 
5235  foreach(lc, conoidlist)
5236  {
5237  Oid conoid = lfirst_oid(lc);
5238  bool found;
5239  ScanKeyData skey;
5240  SysScanDesc tgscan;
5241  HeapTuple htup;
5242 
5243  found = false;
5244 
5245  ScanKeyInit(&skey,
5247  BTEqualStrategyNumber, F_OIDEQ,
5248  ObjectIdGetDatum(conoid));
5249 
5250  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
5251  NULL, 1, &skey);
5252 
5253  while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
5254  {
5255  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
5256 
5257  /*
5258  * Silently skip triggers that are marked as non-deferrable in
5259  * pg_trigger. This is not an error condition, since a
5260  * deferrable RI constraint may have some non-deferrable
5261  * actions.
5262  */
5263  if (pg_trigger->tgdeferrable)
5264  tgoidlist = lappend_oid(tgoidlist,
5265  HeapTupleGetOid(htup));
5266 
5267  found = true;
5268  }
5269 
5270  systable_endscan(tgscan);
5271 
5272  /* Safety check: a deferrable constraint should have triggers */
5273  if (!found)
5274  elog(ERROR, "no triggers found for constraint with OID %u",
5275  conoid);
5276  }
5277 
5278  heap_close(tgrel, AccessShareLock);
5279 
5280  /*
5281  * Now we can set the trigger states of individual triggers for this
5282  * xact.
5283  */
5284  foreach(lc, tgoidlist)
5285  {
5286  Oid tgoid = lfirst_oid(lc);
5288  bool found = false;
5289  int i;
5290 
5291  for (i = 0; i < state->numstates; i++)
5292  {
5293  if (state->trigstates[i].sct_tgoid == tgoid)
5294  {
5295  state->trigstates[i].sct_tgisdeferred = stmt->deferred;
5296  found = true;
5297  break;
5298  }
5299  }
5300  if (!found)
5301  {
5303  SetConstraintStateAddItem(state, tgoid, stmt->deferred);
5304  }
5305  }
5306  }
5307 
5308  /*
5309  * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
5310  * checks against that constraint must be made when the SET CONSTRAINTS
5311  * command is executed -- i.e. the effects of the SET CONSTRAINTS command
5312  * apply retroactively. We've updated the constraints state, so scan the
5313  * list of previously deferred events to fire any that have now become
5314  * immediate.
5315  *
5316  * Obviously, if this was SET ... DEFERRED then it can't have converted
5317  * any unfired events to immediate, so we need do nothing in that case.
5318  */
5319  if (!stmt->deferred)
5320  {
5322  bool snapshot_set = false;
5323 
5324  while (afterTriggerMarkEvents(events, NULL, true))
5325  {
5326  CommandId firing_id = afterTriggers.firing_counter++;
5327 
5328  /*
5329  * Make sure a snapshot has been established in case trigger
5330  * functions need one. Note that we avoid setting a snapshot if
5331  * we don't find at least one trigger that has to be fired now.
5332  * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
5333  * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
5334  * at the start of a transaction it's not possible for any trigger
5335  * events to be queued yet.)
5336  */
5337  if (!snapshot_set)
5338  {
5340  snapshot_set = true;
5341  }
5342 
5343  /*
5344  * We can delete fired events if we are at top transaction level,
5345  * but we'd better not if inside a subtransaction, since the
5346  * subtransaction could later get rolled back.
5347  */
5348  if (afterTriggerInvokeEvents(events, firing_id, NULL,
5349  !IsSubTransaction()))
5350  break; /* all fired */
5351  }
5352 
5353  if (snapshot_set)
5355  }
5356 }
#define NIL
Definition: pg_list.h:69
uint32 CommandId
Definition: c.h:405
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2853
AfterTriggersTransData * trans_stack
Definition: trigger.c:3569
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
#define Anum_pg_trigger_tgconstraint
Definition: pg_trigger.h:87
SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER]
Definition: trigger.c:3333
#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:5050
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition: trigger.c:4212
void PopActiveSnapshot(void)
Definition: snapmgr.c:812
#define heap_close(r, l)
Definition: heapam.h:97
#define Anum_pg_constraint_conname
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:328
char * schemaname
Definition: primnodes.h:67
char * relname
Definition: primnodes.h:68
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
SetConstraintState state
Definition: trigger.c:3559
char * get_database_name(Oid dbid)
Definition: dbcommands.c:2056
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:733
#define CStringGetDatum(X)
Definition: postgres.h:584
#define TriggerConstraintIndexId
Definition: indexing.h:247
#define ereport(elevel, rest)
Definition: elog.h:122
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition: trigger.c:5005
#define list_make1_oid(x1)
Definition: pg_list.h:151
Oid MyDatabaseId
Definition: globals.c:77
#define Anum_pg_constraint_connamespace
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
CommandId firing_counter
Definition: trigger.c:3558
#define ConstraintNameNspIndexId
Definition: indexing.h:124
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:762
FormData_pg_constraint * Form_pg_constraint
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define lfirst(lc)
Definition: pg_list.h:106
Definition: regguts.h:298
#define TriggerRelationId
Definition: pg_trigger.h:34
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:72
bool IsSubTransaction(void)
Definition: xact.c:4528
int errmsg(const char *fmt,...)
Definition: elog.c:797
void list_free(List *list)
Definition: list.c:1133
int i
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition: trigger.c:4140
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
AfterTriggerEventList events
Definition: trigger.c:3560
#define ConstraintRelationId
Definition: pg_constraint.h:29
#define elog
Definition: elog.h:219
#define HeapTupleGetOid(tuple)
Definition: htup_details.h:695
Definition: pg_list.h:45
List * fetch_search_path(bool includeImplicit)
Definition: namespace.c:4168
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
char * catalogname
Definition: primnodes.h:66
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define lfirst_oid(lc)
Definition: pg_list.h:108
SetConstraintState state
Definition: trigger.c:3583
static SetConstraintState SetConstraintStateCopy(SetConstraintState state)
Definition: trigger.c:5030
static bool before_stmt_triggers_fired ( Oid  relid,
CmdType  cmdType 
)
static

Definition at line 5731 of file trigger.c.

References AfterTriggerEnlargeQueryState(), AfterTriggersTableData::before_trig_done, elog, ERROR, GetAfterTriggersTableData(), AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, and result.

Referenced by ExecBSDeleteTriggers(), ExecBSInsertTriggers(), and ExecBSUpdateTriggers().

5732 {
5733  bool result;
5734  AfterTriggersTableData *table;
5735 
5736  /* Check state, like AfterTriggerSaveEvent. */
5737  if (afterTriggers.query_depth < 0)
5738  elog(ERROR, "before_stmt_triggers_fired() called outside of query");
5739 
5740  /* Be sure we have enough space to record events at this query depth. */
5743 
5744  /*
5745  * We keep this state in the AfterTriggersTableData that also holds
5746  * transition tables for the relation + operation. In this way, if we are
5747  * forced to make a new set of transition tables because more tuples get
5748  * entered after we've already fired triggers, we will allow a new set of
5749  * statement triggers to get queued.
5750  */
5751  table = GetAfterTriggersTableData(relid, cmdType);
5752  result = table->before_trig_done;
5753  table->before_trig_done = true;
5754  return result;
5755 }
return result
Definition: formatting.c:1633
#define ERROR
Definition: elog.h:43
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4353
static void AfterTriggerEnlargeQueryState(void)
Definition: trigger.c:4958
#define elog
Definition: elog.h:219
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
static void cancel_prior_stmt_triggers ( Oid  relid,
CmdType  cmdType,
int  tgevent 
)
static

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

5778 {
5779  AfterTriggersTableData *table;
5781 
5782  /*
5783  * We keep this state in the AfterTriggersTableData that also holds
5784  * transition tables for the relation + operation. In this way, if we are
5785  * forced to make a new set of transition tables because more tuples get
5786  * entered after we've already fired triggers, we will allow a new set of
5787  * statement triggers to get queued without canceling the old ones.
5788  */
5789  table = GetAfterTriggersTableData(relid, cmdType);
5790 
5791  if (table->after_trig_done)
5792  {
5793  /*
5794  * We want to start scanning from the tail location that existed just
5795  * before we inserted any statement triggers. But the events list
5796  * might've been entirely empty then, in which case scan from the
5797  * current head.
5798  */
5799  AfterTriggerEvent event;
5800  AfterTriggerEventChunk *chunk;
5801 
5802  if (table->after_trig_events.tail)
5803  {
5804  chunk = table->after_trig_events.tail;
5805  event = (AfterTriggerEvent) table->after_trig_events.tailfree;
5806  }
5807  else
5808  {
5809  chunk = qs->events.head;
5810  event = NULL;
5811  }
5812 
5813  for_each_chunk_from(chunk)
5814  {
5815  if (event == NULL)
5816  event = (AfterTriggerEvent) CHUNK_DATA_START(chunk);
5817  for_each_event_from(event, chunk)
5818  {
5819  AfterTriggerShared evtshared = GetTriggerSharedData(event);
5820 
5821  /*
5822  * Exit loop when we reach events that aren't AS triggers for
5823  * the target relation.
5824  */
5825  if (evtshared->ats_relid != relid)
5826  goto done;
5827  if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) != tgevent)
5828  goto done;
5829  if (!TRIGGER_FIRED_FOR_STATEMENT(evtshared->ats_event))
5830  goto done;
5831  if (!TRIGGER_FIRED_AFTER(evtshared->ats_event))
5832  goto done;
5833  /* OK, mark it DONE */
5834  event->ate_flags &= ~AFTER_TRIGGER_IN_PROGRESS;
5835  event->ate_flags |= AFTER_TRIGGER_DONE;
5836  }
5837  /* signal we must reinitialize event ptr for next chunk */
5838  event = NULL;
5839  }
5840  }
5841 done:
5842 
5843  /* In any case, save current insertion point for next time */
5844  table->after_trig_done = true;
5845  table->after_trig_events = qs->events;
5846 }
TriggerEvent ats_event
Definition: trigger.c:3392
#define AFTER_TRIGGER_DONE
Definition: trigger.c:3379
#define CHUNK_DATA_START(cptr)
Definition: trigger.c:3447
AfterTriggersQueryData * query_stack
Definition: trigger.c:3564
AfterTriggerEventChunk * tail
Definition: trigger.c:3453
#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:3428
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition: trigger.c:4353
AfterTriggerEventList after_trig_events
Definition: trigger.c:3597
#define for_each_chunk_from(cptr)
Definition: trigger.c:3469
#define for_each_event_from(eptr, cptr)
Definition: trigger.c:3471
struct AfterTriggerEventData * AfterTriggerEvent
Definition: trigger.c:3399
AfterTriggerEventChunk * head
Definition: trigger.c:3452
#define AFTER_TRIGGER_IN_PROGRESS
Definition: trigger.c:3380
AfterTriggerEventList events
Definition: trigger.c:3575
static AfterTriggersData afterTriggers
Definition: trigger.c:3602
static void ConvertTriggerToFK ( CreateTrigStmt stmt,
Oid  funcoid 
)
static

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

1020 {
1021  static List *info_list = NIL;
1022 
1023  static const char *const funcdescr[3] = {
1024  gettext_noop("Found referenced table's UPDATE trigger."),
1025  gettext_noop("Found referenced table's DELETE trigger."),
1026  gettext_noop("Found referencing table's trigger.")
1027  };
1028 
1029  char *constr_name;
1030  char *fk_table_name;
1031  char *pk_table_name;
1032  char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1033  List *fk_attrs = NIL;
1034  List *pk_attrs = NIL;
1036  int funcnum;
1037  OldTriggerInfo *info = NULL;
1038  ListCell *l;
1039  int i;
1040 
1041  /* Parse out the trigger arguments */
1042  constr_name = strVal(linitial(stmt->args));
1043  fk_table_name = strVal(lsecond(stmt->args));
1044  pk_table_name = strVal(lthird(stmt->args));
1045  i = 0;
1046  foreach(l, stmt->args)
1047  {
1048  Value *arg = (Value *) lfirst(l);
1049 
1050  i++;
1051  if (i < 4) /* skip constraint and table names */
1052  continue;
1053  if (i == 4) /* handle match type */
1054  {
1055  if (strcmp(strVal(arg), "FULL") == 0)
1056  fk_matchtype = FKCONSTR_MATCH_FULL;
1057  else
1058  fk_matchtype = FKCONSTR_MATCH_SIMPLE;
1059  continue;
1060  }
1061  if (i % 2)
1062  fk_attrs = lappend(fk_attrs, arg);
1063  else
1064  pk_attrs = lappend(pk_attrs, arg);
1065  }
1066 
1067  /* Prepare description of constraint for use in messages */
1068  initStringInfo(&buf);
1069  appendStringInfo(&buf, "FOREIGN KEY %s(",
1070  quote_identifier(fk_table_name));
1071  i = 0;
1072  foreach(l, fk_attrs)
1073  {
1074  Value *arg = (Value *) lfirst(l);
1075 
1076  if (i++ > 0)
1077  appendStringInfoChar(&buf, ',');
1079  }
1080  appendStringInfo(&buf, ") REFERENCES %s(",
1081  quote_identifier(pk_table_name));
1082  i = 0;
1083  foreach(l, pk_attrs)
1084  {
1085  Value *arg = (Value *) lfirst(l);
1086 
1087  if (i++ > 0)
1088  appendStringInfoChar(&buf, ',');
1090  }
1091  appendStringInfoChar(&buf, ')');
1092 
1093  /* Identify class of trigger --- update, delete, or referencing-table */
1094  switch (funcoid)
1095  {
1096  case F_RI_FKEY_CASCADE_UPD:
1097  case F_RI_FKEY_RESTRICT_UPD:
1098  case F_RI_FKEY_SETNULL_UPD:
1099  case F_RI_FKEY_SETDEFAULT_UPD:
1100  case F_RI_FKEY_NOACTION_UPD:
1101  funcnum = 0;
1102  break;
1103 
1104  case F_RI_FKEY_CASCADE_DEL:
1105  case F_RI_FKEY_RESTRICT_DEL:
1106  case F_RI_FKEY_SETNULL_DEL:
1107  case F_RI_FKEY_SETDEFAULT_DEL:
1108  case F_RI_FKEY_NOACTION_DEL:
1109  funcnum = 1;
1110  break;
1111 
1112  default:
1113  funcnum = 2;
1114  break;
1115  }
1116 
1117  /* See if we have a match to this trigger */
1118  foreach(l, info_list)
1119  {
1120  info = (OldTriggerInfo *) lfirst(l);
1121  if (info->funcoids[funcnum] == InvalidOid &&
1122  equal(info->args, stmt->args))
1123  {
1124  info->funcoids[funcnum] = funcoid;
1125  break;
1126  }
1127  }
1128 
1129  if (l == NULL)
1130  {
1131  /* First trigger of set, so create a new list entry */
1132  MemoryContext oldContext;
1133 
1134  ereport(NOTICE,
1135  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1136  constr_name, buf.data),
1137  errdetail_internal("%s", _(funcdescr[funcnum]))));
1139  info = (OldTriggerInfo *) palloc0(sizeof(OldTriggerInfo));
1140  info->args = copyObject(stmt->args);
1141  info->funcoids[funcnum] = funcoid;
1142  info_list = lappend(info_list, info);
1143  MemoryContextSwitchTo(oldContext);
1144  }
1145  else if (info->funcoids[0] == InvalidOid ||
1146  info->funcoids[1] == InvalidOid ||
1147  info->funcoids[2] == InvalidOid)
1148  {
1149  /* Second trigger of set */
1150  ereport(NOTICE,
1151  (errmsg("ignoring incomplete trigger group for constraint \"%s\" %s",
1152  constr_name, buf.data),
1153  errdetail_internal("%s", _(funcdescr[funcnum]))));
1154  }
1155  else
1156  {
1157  /* OK, we have a set, so make the FK constraint ALTER TABLE cmd */
1160  Constraint *fkcon = makeNode(Constraint);
1161  PlannedStmt *wrapper = makeNode(PlannedStmt);
1162 
1163  ereport(NOTICE,
1164  (errmsg("converting trigger group into constraint \"%s\" %s",
1165  constr_name, buf.data),
1166  errdetail_internal("%s", _(funcdescr[funcnum]))));
1167  fkcon->contype = CONSTR_FOREIGN;
1168  fkcon->location = -1;
1169  if (funcnum == 2)
1170  {
1171  /* This trigger is on the FK table */
1172  atstmt->relation = stmt->relation;
1173  if (stmt->constrrel)
1174  fkcon->pktable = stmt->constrrel;
1175  else
1176  {
1177  /* Work around ancient pg_dump bug that omitted constrrel */
1178  fkcon->pktable = makeRangeVar(NULL, pk_table_name, -1);
1179  }
1180  }
1181  else
1182  {
1183  /* This trigger is on the PK table */
1184  fkcon->pktable = stmt->relation;
1185  if (stmt->constrrel)
1186  atstmt->relation = stmt->constrrel;
1187  else
1188  {
1189  /* Work around ancient pg_dump bug that omitted constrrel */
1190  atstmt->relation = makeRangeVar(NULL, fk_table_name, -1);
1191  }
1192  }
1193  atstmt->cmds = list_make1(atcmd);
1194  atstmt->relkind = OBJECT_TABLE;
1195  atcmd->subtype = AT_AddConstraint;
1196  atcmd->def = (Node *) fkcon;
1197  if (strcmp(constr_name, "<unnamed>") == 0)
1198  fkcon->conname = NULL;
1199  else
1200  fkcon->conname = constr_name;
1201  fkcon->fk_attrs = fk_attrs;
1202  fkcon->pk_attrs = pk_attrs;
1203  fkcon->fk_matchtype = fk_matchtype;
1204  switch (info->funcoids[0])
1205  {
1206  case F_RI_FKEY_NOACTION_UPD:
1208  break;
1209  case F_RI_FKEY_CASCADE_UPD:
1211  break;
1212  case F_RI_FKEY_RESTRICT_UPD:
1214  break;
1215  case F_RI_FKEY_SETNULL_UPD:
1217  break;
1218  case F_RI_FKEY_SETDEFAULT_UPD:
1220  break;
1221  default:
1222  /* can't get here because of earlier checks */
1223  elog(ERROR, "confused about RI update function");
1224  }
1225  switch (info->funcoids[1])
1226  {
1227  case F_RI_FKEY_NOACTION_DEL:
1229  break;
1230  case F_RI_FKEY_CASCADE_DEL:
1232  break;
1233  case F_RI_FKEY_RESTRICT_DEL:
1235  break;
1236  case F_RI_FKEY_SETNULL_DEL:
1238  break;
1239  case F_RI_FKEY_SETDEFAULT_DEL:
1241  break;
1242  default:
1243  /* can't get here because of earlier checks */
1244  elog(ERROR, "confused about RI delete function");
1245  }
1246  fkcon->deferrable = stmt->deferrable;
1247  fkcon->initdeferred = stmt->initdeferred;
1248  fkcon->skip_validation = false;
1249  fkcon->initially_valid = true;
1250 
1251  /* finally, wrap it in a dummy PlannedStmt */
1252  wrapper->commandType = CMD_UTILITY;
1253  wrapper->canSetTag = false;
1254  wrapper->utilityStmt = (Node *) atstmt;
1255  wrapper->stmt_location = -1;
1256  wrapper->stmt_len = -1;
1257 
1258  /* ... and execute it */
1259  ProcessUtility(wrapper,
1260  "(generated ALTER TABLE ADD FOREIGN KEY command)",
1261  PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1262  None_Receiver, NULL);
1263 
1264  /* Remove the matched item from the list */
1265  info_list = list_delete_ptr(info_list, info);
1266  pfree(info);
1267  /* We leak the copied args ... not worth worrying about */
1268  }
1269 }
#define NIL
Definition: pg_list.h:69
#define FKCONSTR_MATCH_SIMPLE
Definition: parsenodes.h:2076
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:10390
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2964
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2067
char fk_matchtype
Definition: parsenodes.h:2113
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2071
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define gettext_noop(x)
Definition: c.h:139
Definition: nodes.h:509
#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:335
bool initdeferred
Definition: parsenodes.h:2086
AlterTableType subtype
Definition: parsenodes.h:1778
List * pk_attrs
Definition: parsenodes.h:2112
char * conname
Definition: parsenodes.h:2084
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:98
#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:2371
void pfree(void *pointer)
Definition: mcxt.c:949
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:2085
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:157
int stmt_location
Definition: plannodes.h:97
static char * buf
Definition: pg_test_fsync.c:67
Node * utilityStmt
Definition: plannodes.h:94
Oid funcoids[3]
Definition: trigger.c:1014
#define ereport(elevel, rest)
Definition: elog.h:122
ObjectType relkind
Definition: parsenodes.h:1693
MemoryContext TopMemoryContext
Definition: mcxt.c:43
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2069
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:2074
bool canSetTag
Definition: plannodes.h:53
void * palloc0(Size size)
Definition: mcxt.c:877
CmdType commandType
Definition: plannodes.h:45
#define InvalidOid
Definition: postgres_ext.h:36
bool initially_valid
Definition: parsenodes.h:2122
#define NOTICE
Definition: elog.h:37
#define makeNode(_type_)
Definition: nodes.h:557
char fk_del_action
Definition: parsenodes.h:2115
#define lfirst(lc)
Definition: pg_list.h:106
Definition: value.h:42
List * args
Definition: trigger.c:1013
int errmsg(const char *fmt,...)
Definition: elog.c:797
RangeVar * relation
Definition: parsenodes.h:1691
int i
RangeVar * relation
Definition: parsenodes.h:2355
ConstrType contype
Definition: parsenodes.h:2081
void * arg
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2068
#define lthird(l)
Definition: pg_list.h:121
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:622
RangeVar * pktable
Definition: parsenodes.h:2110
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2070
Definition: pg_list.h:45
bool skip_validation
Definition: parsenodes.h:2121
#define _(x)
Definition: elog.c:84
List * fk_attrs
Definition: parsenodes.h:2111
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:419
char fk_upd_action
Definition: parsenodes.h:2114
TriggerDesc* CopyTriggerDesc ( TriggerDesc trigdesc)

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

1912 {
1913  TriggerDesc *newdesc;
1914  Trigger *trigger;
1915  int i;
1916 
1917  if (trigdesc == NULL || trigdesc->numtriggers <= 0)
1918  return NULL;
1919 
1920  newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
1921  memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
1922 
1923  trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
1924  memcpy(trigger, trigdesc->triggers,
1925  trigdesc->numtriggers * sizeof(Trigger));
1926  newdesc->triggers = trigger;
1927 
1928  for (i = 0; i < trigdesc->numtriggers; i++)
1929  {
1930  trigger->tgname = pstrdup(trigger->tgname);
1931  if (trigger->tgnattr > 0)
1932  {
1933  int16 *newattr;
1934 
1935  newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
1936  memcpy(newattr, trigger->tgattr,
1937  trigger->tgnattr * sizeof(int16));
1938  trigger->tgattr = newattr;
1939  }
1940  if (trigger->tgnargs > 0)
1941  {
1942  char **newargs;
1943  int16 j;
1944 
1945  newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
1946  for (j = 0; j < trigger->tgnargs; j++)
1947  newargs[j] = pstrdup(trigger->tgargs[j]);
1948  trigger->tgargs = newargs;
1949  }
1950  if (trigger->tgqual)
1951  trigger->tgqual = pstrdup(trigger->tgqual);
1952  if (trigger->tgoldtable)
1953  trigger->tgoldtable = pstrdup(trigger->tgoldtable);
1954  if (trigger->tgnewtable)
1955  trigger->tgnewtable = pstrdup(trigger->tgnewtable);
1956  trigger++;
1957  }
1958 
1959  return newdesc;
1960 }
signed short int16
Definition: c.h:245
char * pstrdup(const char *in)
Definition: mcxt.c:1076
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:848
int i
int16 tgnargs
Definition: reltrigger.h:37
char * tgoldtable
Definition: reltrigger.h:42
ObjectAddress CreateTrigger ( CreateTrigStmt stmt,
const char *  queryString,
Oid  relOid,
Oid  refRelOid,
Oid  constraintOid,
Oid  indexOid,
bool  isInternal 
)

Definition at line 141 of file trigger.c.

References AccessShareLock, ACL_EXECUTE, ACL_KIND_CLASS, ACL_KIND_PROC, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addRangeTableEntryForRelation(), addRTEtoQuery(), allowSystemTableMods, Anum_pg_trigger_tgargs, Anum_pg_trigger_tgattr, Anum_pg_trigger_tgconstraint, Anum_pg_trigger_tgconstrindid, Anum_pg_trigger_tgconstrrelid, Anum_pg_trigger_tgdeferrable, Anum_pg_trigger_tgenabled, Anum_pg_trigger_tgfoid, Anum_pg_trigger_tginitdeferred, Anum_pg_trigger_tgisinternal, Anum_pg_trigger_tgname, Anum_pg_trigger_tgnargs, Anum_pg_trigger_tgnewtable, Anum_pg_trigger_tgoldtable, Anum_pg_trigger_tgqual, Anum_pg_trigger_tgrelid, Anum_pg_trigger_tgtype, generate_unaccent_rules::args, CreateTrigStmt::args, Assert, assign_expr_collations(), attnameAttNum(), BoolGetDatum, BTEqualStrategyNumber, buildint2vector(), byteain(), CatalogTupleInsert(), CatalogTupleUpdate(), CharGetDatum, ObjectAddress::classId, CreateTrigStmt::columns, CONSTRAINT_TRIGGER, ConstraintRelationId, CreateTrigStmt::constrrel, ConvertTriggerToFK(), copyObject, CreateConstraintEntry(), CStringGetDatum, CStringGetTextDatum, DatumGetPointer, CreateTrigStmt::deferrable, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, DirectFunctionCall1, elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errhint(), errmsg(), ERROR, CreateTrigStmt::events, EXPR_KIND_TRIGGER_WHEN, free_parsestate(), CreateTrigStmt::funcname, get_func_rettype(), get_rel_name(), GetNewOid(), GETSTRUCT, GetUserId(), has_superclass(), heap_close, heap_form_tuple(), heap_freetuple(), heap_open(), heap_openrv(), HeapTupleIsValid, HeapTupleSetOid, i, CreateTrigStmt::initdeferred, Int16GetDatum, InvalidAttrNumber, InvalidObjectAddress, InvalidOid, InvokeObjectPostCreateHookArg, CreateTrigStmt::isconstraint, TriggerTransition::isNew, IsSystemRelation(), TriggerTransition::isTable, lfirst, lfirst_node, list_length(), Var::location, LockRelationOid(), LookupFuncName(), make_parsestate(), makeAlias(), name, TriggerTransition::name, NAMEDATALEN, namein(), NameListToString(), namestrcmp(), Natts_pg_trigger, NIL, nodeToString(), NoLock, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, OidIsValid, OPAQUEOID, ParseState::p_rtable, ParseState::p_sourcetext, palloc(), parser_errposition(), pfree(), pg_class_aclcheck(), pg_proc_aclcheck(), PointerGetDatum, ProcedureRelationId, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pull_var_clause(), RangeVarGetRelid, RelationData::rd_att, RelationData::rd_id, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnExpr(), CreateTrigStmt::relation, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RelationRelationId, RELKIND_FOREIGN_TABLE, RELKIND_PARTITIONED_TABLE, RELKIND_RELATION, RELKIND_VIEW, 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_CLEAR_TYPE, TRIGGER_FIRES_ON_ORIGIN, TRIGGER_FOR_BEFORE, TRIGGER_FOR_DELETE, TRIGGER_FOR_INSERT, TRIGGER_FOR_INSTEAD, TRIGGER_FOR_ROW, TRIGGER_FOR_TRUNCATE, TRIGGER_FOR_UPDATE, TRIGGER_SETT_ROW, TRIGGER_TYPE_AFTER, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSTEAD, TRIGGEROID, TriggerRelationId, TriggerRelidNameIndexId, CreateTrigStmt::trigname, values, Var::varattno, Var::varno, WARNING, and CreateTrigStmt::whenClause.

Referenced by CreateFKCheckTrigger(), createForeignKeyTriggers(), index_constraint_create(), and ProcessUtilitySlow().

144 {
145  int16 tgtype;
146  int ncolumns;
147  int16 *columns;
148  int2vector *tgattr;
149  Node *whenClause;
150  List *whenRtable;
151  char *qual;
153  bool nulls[Natts_pg_trigger];
154  Relation rel;
155  AclResult aclresult;
156  Relation tgrel;
157  SysScanDesc tgscan;
158  ScanKeyData key;
159  Relation pgrel;
160  HeapTuple tuple;
161  Oid fargtypes[1]; /* dummy */
162  Oid funcoid;
163  Oid funcrettype;
164  Oid trigoid;
165  char internaltrigname[NAMEDATALEN];
166  char *trigname;
167  Oid constrrelid = InvalidOid;
168  ObjectAddress myself,
169  referenced;
170  char *oldtablename = NULL;
171  char *newtablename = NULL;
172 
173  if (OidIsValid(relOid))
174  rel = heap_open(relOid, ShareRowExclusiveLock);
175  else
177 
178  /*
179  * Triggers must be on tables or views, and there are additional
180  * relation-type-specific restrictions.
181  */
182  if (rel->rd_rel->relkind == RELKIND_RELATION ||
183  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
184  {
185  /* Tables can't have INSTEAD OF triggers */
186  if (stmt->timing != TRIGGER_TYPE_BEFORE &&
187  stmt->timing != TRIGGER_TYPE_AFTER)
188  ereport(ERROR,
189  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
190  errmsg("\"%s\" is a table",
192  errdetail("Tables cannot have INSTEAD OF triggers.")));
193  /* Disallow ROW triggers on partitioned tables */
194  if (stmt->row && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
195  ereport(ERROR,
196  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
197  errmsg("\"%s\" is a partitioned table",
199  errdetail("Partitioned tables cannot have ROW triggers.")));
200  }
201  else if (rel->rd_rel->relkind == RELKIND_VIEW)
202  {
203  /*
204  * Views can have INSTEAD OF triggers (which we check below are
205  * row-level), or statement-level BEFORE/AFTER triggers.
206  */
207  if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row)
208  ereport(ERROR,
209  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
210  errmsg("\"%s\" is a view",
212  errdetail("Views cannot have row-level BEFORE or AFTER triggers.")));
213  /* Disallow TRUNCATE triggers on VIEWs */
214  if (TRIGGER_FOR_TRUNCATE(stmt->events))
215  ereport(ERROR,
216  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
217  errmsg("\"%s\" is a view",
219  errdetail("Views cannot have TRUNCATE triggers.")));
220  }
221  else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
222  {
223  if (stmt->timing != TRIGGER_TYPE_BEFORE &&
224  stmt->timing != TRIGGER_TYPE_AFTER)
225  ereport(ERROR,
226  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
227  errmsg("\"%s\" is a foreign table",
229  errdetail("Foreign tables cannot have INSTEAD OF triggers.")));
230 
231  if (TRIGGER_FOR_TRUNCATE(stmt->events))
232  ereport(ERROR,
233  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
234  errmsg("\"%s\" is a foreign table",
236  errdetail("Foreign tables cannot have TRUNCATE triggers.")));
237 
238  /*
239  * We disallow constraint triggers to protect the assumption that
240  * triggers on FKs can't be deferred. See notes with AfterTriggers
241  * data structures, below.
242  */
243  if (stmt->isconstraint)
244  ereport(ERROR,
245  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
246  errmsg("\"%s\" is a foreign table",
248  errdetail("Foreign tables cannot have constraint triggers.")));
249  }
250  else
251  ereport(ERROR,
252  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
253  errmsg("\"%s\" is not a table or view",
254  RelationGetRelationName(rel))));
255 
257  ereport(ERROR,
258  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
259  errmsg("permission denied: \"%s\" is a system catalog",
260  RelationGetRelationName(rel))));
261 
262  if (stmt->isconstraint)
263  {
264  /*
265  * We must take a lock on the target relation to protect against
266  * concurrent drop. It's not clear that AccessShareLock is strong
267  * enough, but we certainly need at least that much... otherwise, we
268  * might end up creating a pg_constraint entry referencing a
269  * nonexistent table.
270  */
271  if (OidIsValid(refRelOid))
272  {
273  LockRelationOid(refRelOid, AccessShareLock);
274  constrrelid = refRelOid;
275  }
276  else if (stmt->constrrel != NULL)
277  constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock,
278  false);
279  }
280 
281  /* permission checks */
282  if (!isInternal)
283  {
284  aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
285  ACL_TRIGGER);
286  if (aclresult != ACLCHECK_OK)
287  aclcheck_error(aclresult, ACL_KIND_CLASS,
289 
290  if (OidIsValid(constrrelid))
291  {
292  aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
293  ACL_TRIGGER);
294  if (aclresult != ACLCHECK_OK)
295  aclcheck_error(aclresult, ACL_KIND_CLASS,
296  get_rel_name(constrrelid));
297  }
298  }
299 
300  /* Compute tgtype */
301  TRIGGER_CLEAR_TYPE(tgtype);
302  if (stmt->row)
303  TRIGGER_SETT_ROW(tgtype);
304  tgtype |= stmt->timing;
305  tgtype |= stmt->events;
306 
307  /* Disallow ROW-level TRUNCATE triggers */
308  if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
309  ereport(ERROR,
310  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
311  errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
312 
313  /* INSTEAD triggers must be row-level, and can't have WHEN or columns */
314  if (TRIGGER_FOR_INSTEAD(tgtype))
315  {
316  if (!TRIGGER_FOR_ROW(tgtype))
317  ereport(ERROR,
318  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
319  errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
320  if (stmt->whenClause)
321  ereport(ERROR,
322  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
323  errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
324  if (stmt->columns != NIL)
325  ereport(ERROR,
326  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
327  errmsg("INSTEAD OF triggers cannot have column lists")));
328  }
329 
330  /*
331  * We don't yet support naming ROW transition variables, but the parser
332  * recognizes the syntax so we can give a nicer message here.
333  *
334  * Per standard, REFERENCING TABLE names are only allowed on AFTER
335  * triggers. Per standard, REFERENCING ROW names are not allowed with FOR
336  * EACH STATEMENT. Per standard, each OLD/NEW, ROW/TABLE permutation is
337  * only allowed once. Per standard, OLD may not be specified when
338  * creating a trigger only for INSERT, and NEW may not be specified when
339  * creating a trigger only for DELETE.
340  *
341  * Notice that the standard allows an AFTER ... FOR EACH ROW trigger to
342  * reference both ROW and TABLE transition data.
343  */
344  if (stmt->transitionRels != NIL)
345  {
346  List *varList = stmt->transitionRels;
347  ListCell *lc;
348 
349  foreach(lc, varList)
350  {
352 
353  if (!(tt->isTable))
354  ereport(ERROR,
355  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
356  errmsg("ROW variable naming in the REFERENCING clause is not supported"),
357  errhint("Use OLD TABLE or NEW TABLE for naming transition tables.")));
358 
359  /*
360  * Because of the above test, we omit further ROW-related testing
361  * below. If we later allow naming OLD and NEW ROW variables,
362  * adjustments will be needed below.
363  */
364 
365  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
366  ereport(ERROR,
367  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
368  errmsg("\"%s\" is a foreign table",
370  errdetail("Triggers on foreign tables cannot have transition tables.")));
371 
372  if (rel->rd_rel->relkind == RELKIND_VIEW)
373  ereport(ERROR,
374  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
375  errmsg("\"%s\" is a view",
377  errdetail("Triggers on views cannot have transition tables.")));
378 
379  /*
380  * We currently don't allow row-level triggers with transition
381  * tables on partition or inheritance children. Such triggers
382  * would somehow need to see tuples converted to the format of the
383  * table they're attached to, and it's not clear which subset of
384  * tuples each child should see. See also the prohibitions in
385  * ATExecAttachPartition() and ATExecAddInherit().
386  */
387  if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id))
388  {
389  /* Use appropriate error message. */
390  if (rel->rd_rel->relispartition)
391  ereport(ERROR,
392  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
393  errmsg("ROW triggers with transition tables are not supported on partitions")));
394  else
395  ereport(ERROR,
396  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
397  errmsg("ROW triggers with transition tables are not supported on inheritance children")));
398  }
399 
400  if (stmt->timing != TRIGGER_TYPE_AFTER)
401  ereport(ERROR,
402  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
403  errmsg("transition table name can only be specified for an AFTER trigger")));
404 
405  if (TRIGGER_FOR_TRUNCATE(tgtype))
406  ereport(ERROR,
407  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
408  errmsg("TRUNCATE triggers with transition tables are not supported")));
409 
410  /*
411  * We currently don't allow multi-event triggers ("INSERT OR
412  * UPDATE") with transition tables, because it's not clear how to
413  * handle INSERT ... ON CONFLICT statements which can fire both
414  * INSERT and UPDATE triggers. We show the inserted tuples to
415  * INSERT triggers and the updated tuples to UPDATE triggers, but
416  * it's not yet clear what INSERT OR UPDATE trigger should see.
417  * This restriction could be lifted if we can decide on the right
418  * semantics in a later release.
419  */
420  if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) +
421  (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) +
422  (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1)
423  ereport(ERROR,
424  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
425  errmsg("transition tables cannot be specified for triggers with more than one event")));
426 
427  /*
428  * We currently don't allow column-specific triggers with
429  * transition tables. Per spec, that seems to require
430  * accumulating separate transition tables for each combination of
431  * columns, which is a lot of work for a rather marginal feature.
432  */
433  if (stmt->columns != NIL)
434  ereport(ERROR,
435  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
436  errmsg("transition tables cannot be specified for triggers with column lists")));
437 
438  /*
439  * We disallow constraint triggers with transition tables, to
440  * protect the assumption that such triggers can't be deferred.
441  * See notes with AfterTriggers data structures, below.
442  *
443  * Currently this is enforced by the grammar, so just Assert here.
444  */
445  Assert(!stmt->isconstraint);
446 
447  if (tt->isNew)
448  {
449  if (!(TRIGGER_FOR_INSERT(tgtype) ||
450  TRIGGER_FOR_UPDATE(tgtype)))
451  ereport(ERROR,
452  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
453  errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger")));
454 
455  if (newtablename != NULL)
456  ereport(ERROR,
457  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
458  errmsg("NEW TABLE cannot be specified multiple times")));
459 
460  newtablename = tt->name;
461  }
462  else
463  {
464  if (!(TRIGGER_FOR_DELETE(tgtype) ||
465  TRIGGER_FOR_UPDATE(tgtype)))
466  ereport(ERROR,
467  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
468  errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger")));
469 
470  if (oldtablename != NULL)
471  ereport(ERROR,
472  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
473  errmsg("OLD TABLE cannot be specified multiple times")));
474 
475  oldtablename = tt->name;
476  }
477  }
478 
479  if (newtablename != NULL && oldtablename != NULL &&
480  strcmp(newtablename, oldtablename) == 0)
481  ereport(ERROR,
482  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
483  errmsg("OLD TABLE name and NEW TABLE name cannot be the same")));
484  }
485 
486  /*
487  * Parse the WHEN clause, if any
488  */
489  if (stmt->whenClause)
490  {
491  ParseState *pstate;
492  RangeTblEntry *rte;
493  List *varList;
494  ListCell *lc;
495 
496  /* Set up a pstate to parse with */
497  pstate = make_parsestate(NULL);
498  pstate->p_sourcetext = queryString;
499 
500  /*
501  * Set up RTEs for OLD and NEW references.
502  *
503  * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
504  */
505  rte = addRangeTableEntryForRelation(pstate, rel,
506  makeAlias("old", NIL),
507  false, false);
508  addRTEtoQuery(pstate, rte, false, true, true);
509  rte = addRangeTableEntryForRelation(pstate, rel,
510  makeAlias("new", NIL),
511  false, false);
512  addRTEtoQuery(pstate, rte, false, true, true);
513 
514  /* Transform expression. Copy to be sure we don't modify original */
515  whenClause = transformWhereClause(pstate,
516  copyObject(stmt->whenClause),
518  "WHEN");
519  /* we have to fix its collations too */
520  assign_expr_collations(pstate, whenClause);
521 
522  /*
523  * Check for disallowed references to OLD/NEW.
524  *
525  * NB: pull_var_clause is okay here only because we don't allow
526  * subselects in WHEN clauses; it would fail to examine the contents
527  * of subselects.
528  */
529  varList = pull_var_clause(whenClause, 0);
530  foreach(lc, varList)
531  {
532  Var *var = (Var *) lfirst(lc);
533 
534  switch (var->varno)
535  {
536  case PRS2_OLD_VARNO:
537  if (!TRIGGER_FOR_ROW(tgtype))
538  ereport(ERROR,
539  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
540  errmsg("statement trigger's WHEN condition cannot reference column values"),
541  parser_errposition(pstate, var->location)));
542  if (TRIGGER_FOR_INSERT(tgtype))
543  ereport(ERROR,
544  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
545  errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
546  parser_errposition(pstate, var->location)));
547  /* system columns are okay here */
548  break;
549  case PRS2_NEW_VARNO:
550  if (!TRIGGER_FOR_ROW(tgtype))
551  ereport(ERROR,
552  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
553  errmsg("statement trigger's WHEN condition cannot reference column values"),
554  parser_errposition(pstate, var->location)));
555  if (TRIGGER_FOR_DELETE(tgtype))
556  ereport(ERROR,
557  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
558  errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
559  parser_errposition(pstate, var->location)));
560  if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
561  ereport(ERROR,
562  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
563  errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
564  parser_errposition(pstate, var->location)));
565  break;
566  default:
567  /* can't happen without add_missing_from, so just elog */
568  elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
569  break;
570  }
571  }
572 
573  /* we'll need the rtable for recordDependencyOnExpr */
574  whenRtable = pstate->p_rtable;
575 
576  qual = nodeToString(whenClause);
577 
578  free_parsestate(pstate);
579  }
580  else
581  {
582  whenClause = NULL;
583  whenRtable = NIL;
584  qual = NULL;
585  }
586 
587  /*
588  * Find and validate the trigger function.
589  */
590  funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false);
591  if (!isInternal)
592  {
593  aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE);
594  if (aclresult != ACLCHECK_OK)
595  aclcheck_error(aclresult, ACL_KIND_PROC,
596  NameListToString(stmt->funcname));
597  }
598  funcrettype = get_func_rettype(funcoid);
599  if (funcrettype != TRIGGEROID)
600  {
601  /*
602  * We allow OPAQUE just so we can load old dump files. When we see a
603  * trigger function declared OPAQUE, change it to TRIGGER.
604  */
605  if (funcrettype == OPAQUEOID)
606  {
608  (errmsg("changing return type of function %s from %s to %s",
609  NameListToString(stmt->funcname),
610  "opaque", "trigger")));
612  }
613  else
614  ereport(ERROR,
615  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
616  errmsg("function %s must return type %s",
617  NameListToString(stmt->funcname), "trigger")));
618  }
619 
620  /*
621  * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that
622  * references one of the built-in RI_FKey trigger functions, assume it is
623  * from a dump of a pre-7.3 foreign key constraint, and take steps to
624  * convert this legacy representation into a regular foreign key
625  * constraint. Ugly, but necessary for loading old dump files.
626  */
627  if (stmt->isconstraint && !isInternal &&
628  list_length(stmt->args) >= 6 &&
629  (list_length(stmt->args) % 2) == 0 &&
631  {
632  /* Keep lock on target rel until end of xact */
633  heap_close(rel, NoLock);
634 
635  ConvertTriggerToFK(stmt, funcoid);
636 
637  return InvalidObjectAddress;
638  }
639 
640  /*
641  * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
642  * corresponding pg_constraint entry.
643  */
644  if (stmt->isconstraint && !OidIsValid(constraintOid))
645  {
646  /* Internal callers should have made their own constraints */
647  Assert(!isInternal);
648  constraintOid = CreateConstraintEntry(stmt->trigname,
651  stmt->deferrable,
652  stmt->initdeferred,
653  true,
654  RelationGetRelid(rel),
655  NULL, /* no conkey */
656  0,
657  InvalidOid, /* no domain */
658  InvalidOid, /* no index */
659  InvalidOid, /* no foreign key */
660  NULL,
661  NULL,
662  NULL,
663  NULL,
664  0,
665  ' ',
666  ' ',
667  ' ',
668  NULL, /* no exclusion */
669  NULL, /* no check constraint */
670  NULL,
671  NULL,
672  true, /* islocal */
673  0, /* inhcount */
674  true, /* isnoinherit */
675  isInternal); /* is_internal */
676  }
677 
678  /*
679  * Generate the trigger's OID now, so that we can use it in the name if
680  * needed.
681  */
683 
684  trigoid = GetNewOid(tgrel);
685 
686  /*
687  * If trigger is internally generated, modify the provided trigger name to
688  * ensure uniqueness by appending the trigger OID. (Callers will usually
689  * supply a simple constant trigger name in these cases.)
690  */
691  if (isInternal)
692  {
693  snprintf(internaltrigname, sizeof(internaltrigname),
694  "%s_%u", stmt->trigname, trigoid);
695  trigname = internaltrigname;
696  }
697  else
698  {
699  /* user-defined trigger; use the specified trigger name as-is */
700  trigname = stmt->trigname;
701  }
702 
703  /*
704  * Scan pg_trigger for existing triggers on relation. We do this only to
705  * give a nice error message if there's already a trigger of the same
706  * name. (The unique index on tgrelid/tgname would complain anyway.) We
707  * can skip this for internally generated triggers, since the name
708  * modification above should be sufficient.
709  *
710  * NOTE that this is cool only because we have ShareRowExclusiveLock on
711  * the relation, so the trigger set won't be changing underneath us.
712  */
713  if (!isInternal)
714  {
715  ScanKeyInit(&key,
717  BTEqualStrategyNumber, F_OIDEQ,
719  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
720  NULL, 1, &key);
721  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
722  {
723  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
724 
725  if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
726  ereport(ERROR,
728  errmsg("trigger \"%s\" for relation \"%s\" already exists",
729  trigname, RelationGetRelationName(rel))));
730  }
731  systable_endscan(tgscan);
732  }
733 
734  /*
735  * Build the new pg_trigger tuple.
736  */
737  memset(nulls, false, sizeof(nulls));
738 
741  CStringGetDatum(trigname));
742  values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
743  values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
745  values[Anum_pg_trigger_tgisinternal - 1] = BoolGetDatum(isInternal);
746  values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
747  values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid);
748  values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
751 
752  if (stmt->args)
753  {
754  ListCell *le;
755  char *args;
756  int16 nargs = list_length(stmt->args);
757  int len = 0;
758 
759  foreach(le, stmt->args)
760  {
761  char *ar = strVal(lfirst(le));
762 
763  len += strlen(ar) + 4;
764  for (; *ar; ar++)
765  {
766  if (*ar == '\\')
767  len++;
768  }
769  }
770  args = (char *) palloc(len + 1);
771  args[0] = '\0';
772  foreach(le, stmt->args)
773  {
774  char *s = strVal(lfirst(le));
775  char *d = args + strlen(args);
776 
777  while (*s)
778  {
779  if (*s == '\\')
780  *d++ = '\\';
781  *d++ = *s++;
782  }
783  strcpy(d, "\\000");
784  }
785  values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
787  CStringGetDatum(args));
788  }
789  else
790  {
791  values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
793  CStringGetDatum(""));
794  }
795 
796  /* build column number array if it's a column-specific trigger */
797  ncolumns = list_length(stmt->columns);
798  if (ncolumns == 0)
799  columns = NULL;
800  else
801  {
802  ListCell *cell;
803  int i = 0;
804 
805  columns = (int16 *) palloc(ncolumns * sizeof(int16));
806  foreach(cell, stmt->columns)
807  {
808  char *name = strVal(lfirst(cell));
809  int16 attnum;
810  int j;
811 
812  /* Lookup column name. System columns are not allowed */
813  attnum = attnameAttNum(rel, name, false);
814  if (attnum == InvalidAttrNumber)
815  ereport(ERROR,
816  (errcode(ERRCODE_UNDEFINED_COLUMN),
817  errmsg("column \"%s\" of relation \"%s\" does not exist",
818  name, RelationGetRelationName(rel))));
819 
820  /* Check for duplicates */
821  for (j = i - 1; j >= 0; j--)
822  {
823  if (columns[j] == attnum)
824  ereport(ERROR,
825  (errcode(ERRCODE_DUPLICATE_COLUMN),
826  errmsg("column \"%s\" specified more than once",
827  name)));
828  }
829 
830  columns[i++] = attnum;
831  }
832  }
833  tgattr = buildint2vector(columns, ncolumns);
834  values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
835 
836  /* set tgqual if trigger has WHEN clause */
837  if (qual)
838  values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(qual);
839  else
840  nulls[Anum_pg_trigger_tgqual - 1] = true;
841 
842  if (oldtablename)
844  CStringGetDatum(oldtablename));
845  else
846  nulls[Anum_pg_trigger_tgoldtable - 1] = true;
847  if (newtablename)
849  CStringGetDatum(newtablename));
850  else
851  nulls[Anum_pg_trigger_tgnewtable - 1] = true;
852 
853  tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
854 
855  /* force tuple to have the desired OID */
856  HeapTupleSetOid(tuple, trigoid);
857 
858  /*
859  * Insert tuple into pg_trigger.
860  */
861  CatalogTupleInsert(tgrel, tuple);
862 
863  heap_freetuple(tuple);
865 
869  if (oldtablename)
871  if (newtablename)
873 
874  /*
875  * Update relation's pg_class entry. Crucial side-effect: other backends
876  * (and this one too!) are sent SI message to make them rebuild relcache
877  * entries.
878  */
880  tuple = SearchSysCacheCopy1(RELOID,
882  if (!HeapTupleIsValid(tuple))
883  elog(ERROR, "cache lookup failed for relation %u",
884  RelationGetRelid(rel));
885 
886  ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
887 
888  CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
889 
890  heap_freetuple(tuple);
892 
893  /*
894  * We used to try to update the rel's relcache entry here, but that's
895  * fairly pointless since it will happen as a byproduct of the upcoming
896  * CommandCounterIncrement...
897  */
898 
899  /*
900  * Record dependencies for trigger. Always place a normal dependency on
901  * the function.
902  */
903  myself.classId = TriggerRelationId;
904  myself.objectId = trigoid;
905  myself.objectSubId = 0;
906 
907  referenced.classId = ProcedureRelationId;
908  referenced.objectId = funcoid;
909  referenced.objectSubId = 0;
910  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
911 
912  if (isInternal && OidIsValid(constraintOid))
913  {
914  /*
915  * Internally-generated trigger for a constraint, so make it an
916  * internal dependency of the constraint. We can skip depending on
917  * the relation(s), as there'll be an indirect dependency via the
918  * constraint.
919  */
920  referenced.classId = ConstraintRelationId;
921  referenced.objectId = constraintOid;
922  referenced.objectSubId = 0;
923  recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
924  }
925  else
926  {
927  /*
928  * User CREATE TRIGGER, so place dependencies. We make trigger be
929  * auto-dropped if its relation is dropped or if the FK relation is
930  * dropped. (Auto drop is compatible with our pre-7.3 behavior.)
931  */
932  referenced.classId = RelationRelationId;
933  referenced.objectId = RelationGetRelid(rel);
934  referenced.objectSubId = 0;
935  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
936  if (OidIsValid(constrrelid))
937  {
938  referenced.classId = RelationRelationId;
939  referenced.objectId = constrrelid;
940  referenced.objectSubId = 0;
941  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
942  }
943  /* Not possible to have an index dependency in this case */
944  Assert(!OidIsValid(indexOid));
945 
946  /*
947  * If it's a user-specified constraint trigger, make the constraint
948  * internally dependent on the trigger instead of vice versa.
949  */
950  if (OidIsValid(constraintOid))
951  {
952  referenced.classId = ConstraintRelationId;
953  referenced.objectId = constraintOid;
954  referenced.objectSubId = 0;
955  recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
956  }
957  }
958 
959  /* If column-specific trigger, add normal dependencies on columns */
960  if (columns != NULL)
961  {
962  int i;
963 
964  referenced.classId = RelationRelationId;
965  referenced.objectId = RelationGetRelid(rel);
966  for (i = 0; i < ncolumns; i++)
967  {
968  referenced.objectSubId = columns[i];
969  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
970  }
971  }
972 
973  /*
974  * If it has a WHEN clause, add dependencies on objects mentioned in the
975  * expression (eg, functions, as well as any columns used).
976  */
977  if (whenClause != NULL)
978  recordDependencyOnExpr(&myself, whenClause, whenRtable,
980 
981  /* Post creation hook for new trigger */
983  isInternal);
984 
985  /* Keep lock on target rel until end of xact */
986  heap_close(rel, NoLock);
987 
988  return myself;
989 }
signed short int16
Definition: c.h:245
#define NIL
Definition: pg_list.h:69
#define Anum_pg_trigger_tgdeferrable
Definition: pg_trigger.h:88
#define TRIGGER_FOR_DELETE(type)
Definition: pg_trigger.h:135
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3701
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:46
int errhint(const char *fmt,...)
Definition: elog.c:987
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
bool IsSystemRelation(Relation relation)
Definition: catalog.c:63
Oid GetUserId(void)
Definition: miscinit.c:284
#define PointerGetDatum(X)
Definition: postgres.h:562
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:53
#define Anum_pg_trigger_tgconstraint
Definition: pg_trigger.h:87
#define ProcedureRelationId
Definition: pg_proc.h:33
Node * whenClause
Definition: parsenodes.h:2364
#define RelationRelationId
Definition: pg_class.h:29
int2vector * buildint2vector(const int16 *int2s, int n)
Definition: int.c:112
#define Anum_pg_trigger_tgqual
Definition: pg_trigger.h:93
#define TRIGGER_CLEAR_TYPE(type)
Definition: pg_trigger.h:118
#define Int16GetDatum(X)
Definition: postgres.h:457
#define Anum_pg_trigger_tgtype
Definition: pg_trigger.h:82
#define AccessShareLock
Definition: lockdefs.h:36
#define Anum_pg_trigger_tgnargs
Definition: pg_trigger.h:90
Definition: nodes.h:509
#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
#define TRIGGER_FOR_UPDATE(type)
Definition: pg_trigger.h:136
AttrNumber varattno
Definition: primnodes.h:168
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
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:695
#define heap_close(r, l)
Definition: heapam.h:97
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:585
Form_pg_class rd_rel
Definition: rel.h:114
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
unsigned int Oid
Definition: postgres_ext.h:31
Definition: primnodes.h:163
#define Anum_pg_trigger_tgisinternal
Definition: pg_trigger.h:84
#define OidIsValid(objectId)
Definition: c.h:532
#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:328
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1459
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:44
#define TriggerRelidNameIndexId
Definition: indexing.h:249
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:698
#define NAMEDATALEN
void assign_expr_collations(ParseState *pstate, Node *expr)
Alias * makeAlias(const char *aliasname, List *colnames)
Definition: makefuncs.c:384
RangeVar * constrrel
Definition: parsenodes.h:2371
#define TRIGGER_TYPE_INSTEAD
Definition: pg_trigger.h:104
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
#define TRIGGER_FOR_ROW(type)
Definition: pg_trigger.h:130
void pfree(void *pointer)
Definition: mcxt.c:949
#define OPAQUEOID
Definition: pg_type.h:700
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
Oid CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:162
#define TRIGGER_TYPE_AFTER
Definition: pg_trigger.h:112
#define TRIGGEROID
Definition: pg_type.h:692
ItemPointerData t_self
Definition: htup.h:65
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:282
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:78
int location
Definition: primnodes.h:178
#define NoLock
Definition: lockdefs.h:34
#define TRIGGER_SETT_ROW(type)
Definition: pg_trigger.h:120
void aclcheck_error(AclResult aclerr, AclObjectKind objectkind, const char *objectname)
Definition: aclchk.c:3399
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define CStringGetDatum(X)
Definition: postgres.h:584
void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior)
Definition: dependency.c:1351
#define RelationGetRelationName(relation)
Definition: rel.h:436
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
const char * p_sourcetext
Definition: parse_node.h:171
#define TRIGGER_FOR_BEFORE(type)
Definition: pg_trigger.h:131
static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
Definition: trigger.c:1019
List * transitionRels
Definition: parsenodes.h:2367
#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:116
#define PRS2_OLD_VARNO
Definition: primnodes.h:160
#define Anum_pg_trigger_tgrelid
Definition: pg_trigger.h:79
#define WARNING
Definition: elog.h:40
Index varno
Definition: primnodes.h:166
char * NameListToString(List *names)
Definition: namespace.c:3063
#define Anum_pg_trigger_tgnewtable
Definition: pg_trigger.h:95
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
Definition: c.h:461
#define Anum_pg_trigger_tgconstrindid
Definition: pg_trigger.h:86
AclResult
Definition: acl.h:178
uintptr_t Datum
Definition: postgres.h:372
#define Anum_pg_trigger_tgenabled
Definition: pg_trigger.h:83
Oid GetNewOid(Relation relation)
Definition: catalog.c:289
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
void SetFunctionReturnType(Oid funcOid, Oid newRetType)
#define TRIGGER_FOR_TRUNCATE(type)
Definition: pg_trigger.h:137
#define InvokeObjectPostCreateHookArg(classId, objectId, subId, is_internal)
Definition: objectaccess.h:147
TupleDesc rd_att
Definition: rel.h:115
#define BoolGetDatum(X)
Definition: postgres.h:408
Relation heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: heapam.c:1318
bool allowSystemTableMods
Definition: globals.c:112
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
int attnameAttNum(Relation rd, const char *attname, bool sysColOK)
#define Assert(condition)
Definition: c.h:664
#define lfirst(lc)
Definition: pg_list.h:106
#define Anum_pg_trigger_tgargs
Definition: pg_trigger.h:92
#define ShareRowExclusiveLock
Definition: lockdefs.h:42
#define Anum_pg_trigger_tgconstrrelid
Definition: pg_trigger.h:85
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
#define Anum_pg_trigger_tgname
Definition: pg_trigger.h:80
#define TriggerRelationId
Definition: pg_trigger.h:34
#define TRIGGER_FOR_INSERT(type)
Definition: pg_trigger.h:134
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:210
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:72
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 CharGetDatum(X)
Definition: postgres.h:422
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4422
#define DatumGetPointer(X)
Definition: postgres.h:555
#define TRIGGER_TYPE_BEFORE
Definition: pg_trigger.h:99
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
Definition: parse_func.c:1929
#define Anum_pg_trigger_tgoldtable
Definition: pg_trigger.h:94
static Datum values[MAXATTR]
Definition: bootstrap.c:163
FormData_pg_class * Form_pg_class
Definition: pg_class.h:95
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:168
const ObjectAddress InvalidObjectAddress
void * palloc(Size size)
Definition: mcxt.c:848
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define ACL_EXECUTE
Definition: parsenodes.h:79
#define RELKIND_VIEW
Definition: pg_class.h:164
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4446
int i
RangeVar * relation
Definition: parsenodes.h:2355
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define CStringGetTextDatum(s)
Definition: builtins.h:91
char * nodeToString(const void *obj)
Definition: outfuncs.c:4255
#define ConstraintRelationId
Definition: pg_constraint.h:29
#define elog
Definition: elog.h:219
#define CONSTRAINT_TRIGGER
#define copyObject(obj)
Definition: nodes.h:622
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:105
void free_parsestate(ParseState *pstate)
Definition: parse_node.c:77
#define RELKIND_RELATION
Definition: pg_class.h:160
#define RI_TRIGGER_NONE
Definition: trigger.h:268
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
Definition: pg_list.h:45
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1726
#define RelationGetRelid(relation)
Definition: rel.h:416
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isValidated, Oid relId, const int16 *constraintKey, int constraintNKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, const char *conSrc, bool conIsLocal, int conInhCount, bool conNoInherit, bool is_internal)
Definition: pg_constraint.c:49
#define PRS2_NEW_VARNO
Definition: primnodes.h:161
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define Anum_pg_trigger_tgfoid
Definition: pg_trigger.h:81
#define Anum_pg_trigger_tginitdeferred
Definition: pg_trigger.h:89
#define Anum_pg_trigger_tgattr
Definition: pg_trigger.h:91
#define Natts_pg_trigger
Definition: pg_trigger.h:78
#define TRIGGER_FOR_INSTEAD(type)
Definition: pg_trigger.h:133
#define RelationGetNamespace(relation)
Definition: rel.h:443
List * p_rtable
Definition: parse_node.h:172
void EnableDisableTrigger ( Relation  rel,
const char *  tgname,
char  fires_when,
bool  skip_system 
)

Definition at line 1581 of file trigger.c.

References Anum_pg_trigger_tgname, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), CStringGetDatum, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_close, heap_copytuple(), heap_freetuple(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, InvokeObjectPostAlterHook, NameStr, ObjectIdGetDatum, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), superuser(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, TriggerRelationId, and TriggerRelidNameIndexId.

Referenced by ATExecEnableDisableTrigger().

1583 {
1584  Relation tgrel;
1585  int nkeys;
1586  ScanKeyData keys[2];
1587  SysScanDesc tgscan;
1588  HeapTuple tuple;
1589  bool found;
1590  bool changed;
1591 
1592  /* Scan the relevant entries in pg_triggers */
1594 
1595  ScanKeyInit(&keys[0],
1597  BTEqualStrategyNumber, F_OIDEQ,
1599  if (tgname)
1600  {
1601  ScanKeyInit(&keys[1],
1603  BTEqualStrategyNumber, F_NAMEEQ,
1604  CStringGetDatum(tgname));
1605  nkeys = 2;
1606  }
1607  else
1608  nkeys = 1;
1609 
1610  tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
1611  NULL, nkeys, keys);
1612 
1613  found = changed = false;
1614 
1615  while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1616  {
1617  Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
1618 
1619  if (oldtrig->tgisinternal)
1620  {
1621  /* system trigger ... ok to process? */
1622  if (skip_system)
1623  continue;
1624  if (!superuser())
1625  ereport(ERROR,
1626  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1627  errmsg("permission denied: \"%s\" is a system trigger",
1628  NameStr(oldtrig->tgname))));
1629  }
1630 
1631  found = true;
1632 
1633  if (oldtrig->tgenabled != fires_when)
1634  {
1635  /* need to change this one ... make a copy to scribble on */
1636  HeapTuple newtup = heap_copytuple(tuple);
1637  Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
1638 
1639  newtrig->tgenabled = fires_when;
1640 
1641  CatalogTupleUpdate(tgrel, &newtup->t_self, newtup);
1642 
1643  heap_freetuple(newtup);
1644 
1645  changed =