PostgreSQL Source Code git master
Loading...
Searching...
No Matches
trigger.h File Reference
Include dependency graph for trigger.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  TriggerData
 
struct  TransitionCaptureState
 

Macros

#define CALLED_AS_TRIGGER(fcinfo)    ((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
 
#define TRIGGER_EVENT_INSERT   0x00000000
 
#define TRIGGER_EVENT_DELETE   0x00000001
 
#define TRIGGER_EVENT_UPDATE   0x00000002
 
#define TRIGGER_EVENT_TRUNCATE   0x00000003
 
#define TRIGGER_EVENT_OPMASK   0x00000003
 
#define TRIGGER_EVENT_ROW   0x00000004
 
#define TRIGGER_EVENT_BEFORE   0x00000008
 
#define TRIGGER_EVENT_AFTER   0x00000000
 
#define TRIGGER_EVENT_INSTEAD   0x00000010
 
#define TRIGGER_EVENT_TIMINGMASK   0x00000018
 
#define AFTER_TRIGGER_DEFERRABLE   0x00000020
 
#define AFTER_TRIGGER_INITDEFERRED   0x00000040
 
#define TRIGGER_FIRED_BY_INSERT(event)    (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_INSERT)
 
#define TRIGGER_FIRED_BY_DELETE(event)    (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_DELETE)
 
#define TRIGGER_FIRED_BY_UPDATE(event)    (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE)
 
#define TRIGGER_FIRED_BY_TRUNCATE(event)    (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_TRUNCATE)
 
#define TRIGGER_FIRED_FOR_ROW(event)    ((event) & TRIGGER_EVENT_ROW)
 
#define TRIGGER_FIRED_FOR_STATEMENT(event)    (!TRIGGER_FIRED_FOR_ROW(event))
 
#define TRIGGER_FIRED_BEFORE(event)    (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_BEFORE)
 
#define TRIGGER_FIRED_AFTER(event)    (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_AFTER)
 
#define TRIGGER_FIRED_INSTEAD(event)    (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_INSTEAD)
 
#define SESSION_REPLICATION_ROLE_ORIGIN   0
 
#define SESSION_REPLICATION_ROLE_REPLICA   1
 
#define SESSION_REPLICATION_ROLE_LOCAL   2
 
#define TRIGGER_FIRES_ON_ORIGIN   'O'
 
#define TRIGGER_FIRES_ALWAYS   'A'
 
#define TRIGGER_FIRES_ON_REPLICA   'R'
 
#define TRIGGER_DISABLED   'D'
 
#define RI_TRIGGER_PK   1 /* is a trigger on the PK relation */
 
#define RI_TRIGGER_FK   2 /* is a trigger on the FK relation */
 
#define RI_TRIGGER_NONE   0 /* is not an RI trigger function */
 

Typedefs

typedef uint32 TriggerEvent
 
typedef struct TriggerData TriggerData
 
typedef struct TransitionCaptureState TransitionCaptureState
 
typedef void(* AfterTriggerBatchCallback) (void *arg)
 

Functions

ObjectAddress CreateTrigger (const CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
 
ObjectAddress CreateTriggerFiringOn (const CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
 
void TriggerSetParentTrigger (Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
 
void RemoveTriggerById (Oid trigOid)
 
Oid get_trigger_oid (Oid relid, const char *trigname, bool missing_ok)
 
ObjectAddress renametrig (RenameStmt *stmt)
 
void EnableDisableTrigger (Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
 
void RelationBuildTriggers (Relation relation)
 
TriggerDescCopyTriggerDesc (TriggerDesc *trigdesc)
 
const charFindTriggerIncompatibleWithInheritance (TriggerDesc *trigdesc)
 
TransitionCaptureStateMakeTransitionCaptureState (TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
 
void FreeTriggerDesc (TriggerDesc *trigdesc)
 
void ExecBSInsertTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASInsertTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecARInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
 
bool ExecIRInsertTriggers (EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
 
void ExecBSDeleteTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASDeleteTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRDeleteTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot, TM_Result *tmresult, TM_FailureData *tmfd, bool is_merge_delete)
 
void ExecARDeleteTriggers (EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
bool ExecIRDeleteTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
 
void ExecBSUpdateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASUpdateTriggers (EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
 
bool ExecBRUpdateTriggers (EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, TM_Result *tmresult, TM_FailureData *tmfd, bool is_merge_update)
 
void ExecARUpdateTriggers (EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture, bool is_crosspart_update)
 
bool ExecIRUpdateTriggers (EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
 
void ExecBSTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
void ExecASTruncateTriggers (EState *estate, ResultRelInfo *relinfo)
 
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)
 
bool RI_FKey_pk_upd_check_required (Trigger *trigger, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
bool RI_FKey_fk_upd_check_required (Trigger *trigger, Relation fk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
bool RI_Initial_Check (Trigger *trigger, Relation fk_rel, Relation pk_rel)
 
void RI_PartitionRemove_Check (Trigger *trigger, Relation fk_rel, Relation pk_rel)
 
int RI_FKey_trigger_type (Oid tgfoid)
 
void RegisterAfterTriggerBatchCallback (AfterTriggerBatchCallback callback, void *arg)
 
bool AfterTriggerIsActive (void)
 
void AtEOXact_RI (bool isCommit)
 

Variables

PGDLLIMPORT int SessionReplicationRole
 

Macro Definition Documentation

◆ AFTER_TRIGGER_DEFERRABLE

#define AFTER_TRIGGER_DEFERRABLE   0x00000020

Definition at line 109 of file trigger.h.

◆ AFTER_TRIGGER_INITDEFERRED

#define AFTER_TRIGGER_INITDEFERRED   0x00000040

Definition at line 110 of file trigger.h.

◆ CALLED_AS_TRIGGER

#define CALLED_AS_TRIGGER (   fcinfo)     ((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))

Definition at line 26 of file trigger.h.

31{
33 TriggerEvent tg_event;
34 Relation tg_relation;
35 HeapTuple tg_trigtuple;
36 HeapTuple tg_newtuple;
37 Trigger *tg_trigger;
38 TupleTableSlot *tg_trigslot;
39 TupleTableSlot *tg_newslot;
40 Tuplestorestate *tg_oldtable;
41 Tuplestorestate *tg_newtable;
42 const Bitmapset *tg_updatedcols;
44
45/*
46 * The state for capturing old and new tuples into transition tables for a
47 * single ModifyTable node (or other operation source, e.g. copyfrom.c).
48 *
49 * This is per-caller to avoid conflicts in setting
50 * tcs_original_insert_tuple. Note, however, that the pointed-to
51 * private data may be shared across multiple callers.
52 */
53struct AfterTriggersTableData; /* private in trigger.c */
54
55typedef struct TransitionCaptureState
56{
57 /*
58 * Is there at least one trigger specifying each transition relation on
59 * the relation explicitly named in the DML statement or COPY command?
60 * Note: in current usage, these flags could be part of the private state,
61 * but it seems possibly useful to let callers see them.
62 */
67
68 /*
69 * For INSERT and COPY, it would be wasteful to convert tuples from child
70 * format to parent format after they have already been converted in the
71 * opposite direction during routing. In that case we bypass conversion
72 * and allow the inserting code (copyfrom.c and nodeModifyTable.c) to
73 * provide a slot containing the original tuple directly.
74 */
76
77 /*
78 * Private data including the tuplestore(s) into which to insert tuples.
79 */
84
85/*
86 * TriggerEvent bit flags
87 *
88 * Note that we assume different event types (INSERT/DELETE/UPDATE/TRUNCATE)
89 * can't be OR'd together in a single TriggerEvent. This is unlike the
90 * situation for pg_trigger rows, so pg_trigger.tgtype uses a different
91 * representation!
92 */
93#define TRIGGER_EVENT_INSERT 0x00000000
94#define TRIGGER_EVENT_DELETE 0x00000001
95#define TRIGGER_EVENT_UPDATE 0x00000002
96#define TRIGGER_EVENT_TRUNCATE 0x00000003
97#define TRIGGER_EVENT_OPMASK 0x00000003
98
99#define TRIGGER_EVENT_ROW 0x00000004
100
101#define TRIGGER_EVENT_BEFORE 0x00000008
102#define TRIGGER_EVENT_AFTER 0x00000000
103#define TRIGGER_EVENT_INSTEAD 0x00000010
104#define TRIGGER_EVENT_TIMINGMASK 0x00000018
105
106/* More TriggerEvent flags, used only within trigger.c */
107
108#define AFTER_TRIGGER_DEFERRABLE 0x00000020
109#define AFTER_TRIGGER_INITDEFERRED 0x00000040
110
111#define TRIGGER_FIRED_BY_INSERT(event) \
112 (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_INSERT)
113
114#define TRIGGER_FIRED_BY_DELETE(event) \
115 (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_DELETE)
116
117#define TRIGGER_FIRED_BY_UPDATE(event) \
118 (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE)
119
120#define TRIGGER_FIRED_BY_TRUNCATE(event) \
121 (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_TRUNCATE)
122
123#define TRIGGER_FIRED_FOR_ROW(event) \
124 ((event) & TRIGGER_EVENT_ROW)
125
126#define TRIGGER_FIRED_FOR_STATEMENT(event) \
127 (!TRIGGER_FIRED_FOR_ROW(event))
128
129#define TRIGGER_FIRED_BEFORE(event) \
130 (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_BEFORE)
131
132#define TRIGGER_FIRED_AFTER(event) \
133 (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_AFTER)
134
135#define TRIGGER_FIRED_INSTEAD(event) \
136 (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_INSTEAD)
137
138/*
139 * Definitions for replication role based firing.
140 */
141#define SESSION_REPLICATION_ROLE_ORIGIN 0
142#define SESSION_REPLICATION_ROLE_REPLICA 1
143#define SESSION_REPLICATION_ROLE_LOCAL 2
145
146/*
147 * States at which a trigger can be fired. These are the
148 * possible values for pg_trigger.tgenabled.
149 */
150#define TRIGGER_FIRES_ON_ORIGIN 'O'
151#define TRIGGER_FIRES_ALWAYS 'A'
152#define TRIGGER_FIRES_ON_REPLICA 'R'
153#define TRIGGER_DISABLED 'D'
154
155extern ObjectAddress CreateTrigger(const CreateTrigStmt *stmt, const char *queryString,
156 Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid,
157 Oid funcoid, Oid parentTriggerOid, Node *whenClause,
158 bool isInternal, bool in_partition);
159extern ObjectAddress CreateTriggerFiringOn(const CreateTrigStmt *stmt, const char *queryString,
162 Node *whenClause, bool isInternal, bool in_partition,
163 char trigger_fires_when);
164
169extern void RemoveTriggerById(Oid trigOid);
170extern Oid get_trigger_oid(Oid relid, const char *trigname, bool missing_ok);
171
173
174extern void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent,
175 char fires_when, bool skip_system, bool recurse,
176 LOCKMODE lockmode);
177
178extern void RelationBuildTriggers(Relation relation);
179
180extern TriggerDesc *CopyTriggerDesc(TriggerDesc *trigdesc);
181
182extern const char *FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc);
183
186
187extern void FreeTriggerDesc(TriggerDesc *trigdesc);
188
189extern void ExecBSInsertTriggers(EState *estate,
191extern void ExecASInsertTriggers(EState *estate,
193 TransitionCaptureState *transition_capture);
194extern bool ExecBRInsertTriggers(EState *estate,
196 TupleTableSlot *slot);
197extern void ExecARInsertTriggers(EState *estate,
199 TupleTableSlot *slot,
201 TransitionCaptureState *transition_capture);
202extern bool ExecIRInsertTriggers(EState *estate,
204 TupleTableSlot *slot);
205extern void ExecBSDeleteTriggers(EState *estate,
207extern void ExecASDeleteTriggers(EState *estate,
209 TransitionCaptureState *transition_capture);
210extern bool ExecBRDeleteTriggers(EState *estate,
211 EPQState *epqstate,
217 TM_FailureData *tmfd,
218 bool is_merge_delete);
219extern void ExecARDeleteTriggers(EState *estate,
223 TransitionCaptureState *transition_capture,
225extern bool ExecIRDeleteTriggers(EState *estate,
228extern void ExecBSUpdateTriggers(EState *estate,
230extern void ExecASUpdateTriggers(EState *estate,
232 TransitionCaptureState *transition_capture);
233extern bool ExecBRUpdateTriggers(EState *estate,
234 EPQState *epqstate,
240 TM_FailureData *tmfd,
241 bool is_merge_update);
242extern void ExecARUpdateTriggers(EState *estate,
250 TransitionCaptureState *transition_capture,
252extern bool ExecIRUpdateTriggers(EState *estate,
256extern void ExecBSTruncateTriggers(EState *estate,
258extern void ExecASTruncateTriggers(EState *estate,
260
261extern void AfterTriggerBeginXact(void);
262extern void AfterTriggerBeginQuery(void);
263extern void AfterTriggerEndQuery(EState *estate);
264extern void AfterTriggerFireDeferred(void);
265extern void AfterTriggerEndXact(bool isCommit);
266extern void AfterTriggerBeginSubXact(void);
267extern void AfterTriggerEndSubXact(bool isCommit);
270
271
272/*
273 * in utils/adt/ri_triggers.c
274 */
279extern bool RI_Initial_Check(Trigger *trigger,
280 Relation fk_rel, Relation pk_rel);
282 Relation pk_rel);
283
284/* result values for RI_FKey_trigger_type: */
285#define RI_TRIGGER_PK 1 /* is a trigger on the PK relation */
286#define RI_TRIGGER_FK 2 /* is a trigger on the FK relation */
287#define RI_TRIGGER_NONE 0 /* is not an RI trigger function */
288
289extern int RI_FKey_trigger_type(Oid tgfoid);
290
291/*
292 * Callback type for end-of-trigger-batch callbacks.
293 *
294 * Currently used by ri_triggers.c to flush fast-path FK batches and
295 * clean up associated resources.
296 *
297 * Registered via RegisterAfterTriggerBatchCallback(). Invoked when
298 * the current trigger-firing batch completes:
299 * - AfterTriggerEndQuery() (immediate constraints)
300 * - AfterTriggerFireDeferred() (deferred constraints at COMMIT)
301 * - AfterTriggerSetState() (SET CONSTRAINTS IMMEDIATE)
302 *
303 * The callback list is cleared after each batch. Callers must
304 * re-register if they need to be called again in a subsequent batch.
305 */
306typedef void (*AfterTriggerBatchCallback) (void *arg);
307
309 void *arg);
310extern bool AfterTriggerIsActive(void);
311
312extern void AtEOXact_RI(bool isCommit);
313
314#endif /* TRIGGER_H */
#define PGDLLIMPORT
Definition c.h:1478
Datum arg
Definition elog.c:1323
#define stmt
int LOCKMODE
Definition lockdefs.h:26
CmdType
Definition nodes.h:271
NodeTag
Definition nodes.h:27
unsigned int Oid
static int fb(int x)
Definition pg_list.h:54
Definition nodes.h:133
TupleTableSlot * tcs_original_insert_tuple
Definition trigger.h:76
struct AfterTriggersTableData * tcs_insert_private
Definition trigger.h:81
struct AfterTriggersTableData * tcs_update_private
Definition trigger.h:82
struct AfterTriggersTableData * tcs_delete_private
Definition trigger.h:83
TM_Result
Definition tableam.h:95
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
uint32 TriggerEvent
Definition trigger.h:29
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, TM_Result *tmresult, TM_FailureData *tmfd, bool is_merge_update)
Definition trigger.c:2998
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition trigger.c:5005
void AfterTriggerBeginXact(void)
Definition trigger.c:5131
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition trigger.c:2828
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2428
bool ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition trigger.c:2492
void AfterTriggerEndSubXact(bool isCommit)
Definition trigger.c:5519
bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
void ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:3307
void RegisterAfterTriggerBatchCallback(AfterTriggerBatchCallback callback, void *arg)
Definition trigger.c:6863
PGDLLIMPORT int SessionReplicationRole
Definition trigger.c:65
void FreeTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2172
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition trigger.c:2875
bool AfterTriggerIsActive(void)
Definition trigger.c:6928
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2657
ObjectAddress CreateTriggerFiringOn(const CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition trigger.c:179
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition trigger.c:1753
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition trigger.c:2304
bool ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition trigger.c:2596
void ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:3354
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition trigger.c:3171
void AfterTriggerSetState(ConstraintsSetStmt *stmt)
Definition trigger.c:5851
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot, TM_Result *tmresult, TM_FailureData *tmfd, bool is_merge_delete)
Definition trigger.c:2728
Oid get_trigger_oid(Oid relid, const char *trigname, bool missing_ok)
Definition trigger.c:1384
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2980
void AtEOXact_RI(bool isCommit)
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2708
ObjectAddress renametrig(RenameStmt *stmt)
Definition trigger.c:1494
void AfterTriggerFireDeferred(void)
Definition trigger.c:5354
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition trigger.c:2570
void TriggerSetParentTrigger(Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
Definition trigger.c:1234
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2479
TriggerDesc * CopyTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2117
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition trigger.c:3241
void AfterTriggerEndXact(bool isCommit)
Definition trigger.c:5417
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6175
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
void(* AfterTriggerBatchCallback)(void *arg)
Definition trigger.h:307
void RelationBuildTriggers(Relation relation)
Definition trigger.c:1888
void AfterTriggerBeginSubXact(void)
Definition trigger.c:5471
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
ObjectAddress CreateTrigger(const CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
Definition trigger.c:162
bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, TupleTableSlot *oldslot, TupleTableSlot *newslot)
int RI_FKey_trigger_type(Oid tgfoid)
void AfterTriggerEndQuery(EState *estate)
Definition trigger.c:5186
void RemoveTriggerById(Oid trigOid)
Definition trigger.c:1305
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2922
void AfterTriggerBeginQuery(void)
Definition trigger.c:5166
const char * type

◆ RI_TRIGGER_FK

#define RI_TRIGGER_FK   2 /* is a trigger on the FK relation */

Definition at line 287 of file trigger.h.

◆ RI_TRIGGER_NONE

#define RI_TRIGGER_NONE   0 /* is not an RI trigger function */

Definition at line 288 of file trigger.h.

◆ RI_TRIGGER_PK

#define RI_TRIGGER_PK   1 /* is a trigger on the PK relation */

Definition at line 286 of file trigger.h.

◆ SESSION_REPLICATION_ROLE_LOCAL

#define SESSION_REPLICATION_ROLE_LOCAL   2

Definition at line 144 of file trigger.h.

◆ SESSION_REPLICATION_ROLE_ORIGIN

#define SESSION_REPLICATION_ROLE_ORIGIN   0

Definition at line 142 of file trigger.h.

◆ SESSION_REPLICATION_ROLE_REPLICA

#define SESSION_REPLICATION_ROLE_REPLICA   1

Definition at line 143 of file trigger.h.

◆ TRIGGER_DISABLED

#define TRIGGER_DISABLED   'D'

Definition at line 154 of file trigger.h.

◆ TRIGGER_EVENT_AFTER

#define TRIGGER_EVENT_AFTER   0x00000000

Definition at line 103 of file trigger.h.

◆ TRIGGER_EVENT_BEFORE

#define TRIGGER_EVENT_BEFORE   0x00000008

Definition at line 102 of file trigger.h.

◆ TRIGGER_EVENT_DELETE

#define TRIGGER_EVENT_DELETE   0x00000001

Definition at line 95 of file trigger.h.

◆ TRIGGER_EVENT_INSERT

#define TRIGGER_EVENT_INSERT   0x00000000

Definition at line 94 of file trigger.h.

◆ TRIGGER_EVENT_INSTEAD

#define TRIGGER_EVENT_INSTEAD   0x00000010

Definition at line 104 of file trigger.h.

◆ TRIGGER_EVENT_OPMASK

#define TRIGGER_EVENT_OPMASK   0x00000003

Definition at line 98 of file trigger.h.

◆ TRIGGER_EVENT_ROW

#define TRIGGER_EVENT_ROW   0x00000004

Definition at line 100 of file trigger.h.

◆ TRIGGER_EVENT_TIMINGMASK

#define TRIGGER_EVENT_TIMINGMASK   0x00000018

Definition at line 105 of file trigger.h.

◆ TRIGGER_EVENT_TRUNCATE

#define TRIGGER_EVENT_TRUNCATE   0x00000003

Definition at line 97 of file trigger.h.

◆ TRIGGER_EVENT_UPDATE

#define TRIGGER_EVENT_UPDATE   0x00000002

Definition at line 96 of file trigger.h.

◆ TRIGGER_FIRED_AFTER

#define TRIGGER_FIRED_AFTER (   event)     (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_AFTER)

Definition at line 133 of file trigger.h.

◆ TRIGGER_FIRED_BEFORE

#define TRIGGER_FIRED_BEFORE (   event)     (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_BEFORE)

Definition at line 130 of file trigger.h.

◆ TRIGGER_FIRED_BY_DELETE

#define TRIGGER_FIRED_BY_DELETE (   event)     (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_DELETE)

Definition at line 115 of file trigger.h.

◆ TRIGGER_FIRED_BY_INSERT

#define TRIGGER_FIRED_BY_INSERT (   event)     (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_INSERT)

Definition at line 112 of file trigger.h.

◆ TRIGGER_FIRED_BY_TRUNCATE

#define TRIGGER_FIRED_BY_TRUNCATE (   event)     (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_TRUNCATE)

Definition at line 121 of file trigger.h.

◆ TRIGGER_FIRED_BY_UPDATE

#define TRIGGER_FIRED_BY_UPDATE (   event)     (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE)

Definition at line 118 of file trigger.h.

◆ TRIGGER_FIRED_FOR_ROW

#define TRIGGER_FIRED_FOR_ROW (   event)     ((event) & TRIGGER_EVENT_ROW)

Definition at line 124 of file trigger.h.

◆ TRIGGER_FIRED_FOR_STATEMENT

#define TRIGGER_FIRED_FOR_STATEMENT (   event)     (!TRIGGER_FIRED_FOR_ROW(event))

Definition at line 127 of file trigger.h.

◆ TRIGGER_FIRED_INSTEAD

#define TRIGGER_FIRED_INSTEAD (   event)     (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_INSTEAD)

Definition at line 136 of file trigger.h.

◆ TRIGGER_FIRES_ALWAYS

#define TRIGGER_FIRES_ALWAYS   'A'

Definition at line 152 of file trigger.h.

◆ TRIGGER_FIRES_ON_ORIGIN

#define TRIGGER_FIRES_ON_ORIGIN   'O'

Definition at line 151 of file trigger.h.

◆ TRIGGER_FIRES_ON_REPLICA

#define TRIGGER_FIRES_ON_REPLICA   'R'

Definition at line 153 of file trigger.h.

Typedef Documentation

◆ AfterTriggerBatchCallback

typedef void(* AfterTriggerBatchCallback) (void *arg)

Definition at line 307 of file trigger.h.

◆ TransitionCaptureState

◆ TriggerData

◆ TriggerEvent

Definition at line 29 of file trigger.h.

Function Documentation

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )
extern

Definition at line 5166 of file trigger.c.

5167{
5168 /* Increase the query stack depth */
5170}
static AfterTriggersData afterTriggers
Definition trigger.c:3977

References afterTriggers, and AfterTriggersData::query_depth.

Referenced by CopyFrom(), create_edata_for_relation(), ExecForPortionOfLeftovers(), ExecuteTruncateGuts(), and standard_ExecutorStart().

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )
extern

Definition at line 5471 of file trigger.c.

5472{
5473 int my_level = GetCurrentTransactionNestLevel();
5474
5475 /*
5476 * Allocate more space in the trans_stack if needed. (Note: because the
5477 * minimum nest level of a subtransaction is 2, we waste the first couple
5478 * entries of the array; not worth the notational effort to avoid it.)
5479 */
5480 while (my_level >= afterTriggers.maxtransdepth)
5481 {
5483 {
5484 /* Arbitrarily initialize for max of 8 subtransaction levels */
5487 8 * sizeof(AfterTriggersTransData));
5489 }
5490 else
5491 {
5492 /* repalloc will keep the stack in the same context */
5494
5499 }
5500 }
5501
5502 /*
5503 * Push the current information into the stack. The SET CONSTRAINTS state
5504 * is not saved until/unless changed. Likewise, we don't make a
5505 * per-subtransaction event context until needed.
5506 */
5507 afterTriggers.trans_stack[my_level].state = NULL;
5511}
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1235
MemoryContext TopTransactionContext
Definition mcxt.c:172
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1635
CommandId firing_counter
Definition trigger.c:3908
AfterTriggersTransData * trans_stack
Definition trigger.c:3919
AfterTriggerEventList events
Definition trigger.c:3910
AfterTriggerEventList events
Definition trigger.c:3947
SetConstraintState state
Definition trigger.c:3946
CommandId firing_counter
Definition trigger.c:3949
int GetCurrentTransactionNestLevel(void)
Definition xact.c:931

References afterTriggers, AfterTriggersData::events, AfterTriggersTransData::events, fb(), 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().

◆ AfterTriggerBeginXact()

void AfterTriggerBeginXact ( void  )
extern

Definition at line 5131 of file trigger.c.

5132{
5133 /*
5134 * Initialize after-trigger state structure to empty
5135 */
5136 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5141
5142 /*
5143 * Verify that there is no leftover state remaining. If these assertions
5144 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5145 * up properly.
5146 */
5154}
#define Assert(condition)
Definition c.h:1002
uint32 CommandId
Definition c.h:809
#define NIL
Definition pg_list.h:68
AfterTriggerEventChunk * head
Definition trigger.c:3802
SetConstraintState state
Definition trigger.c:3909
AfterTriggersQueryData * query_stack
Definition trigger.c:3914
MemoryContext event_cxt
Definition trigger.c:3911
List * batch_callbacks
Definition trigger.c:3922
bool firing_batch_callbacks
Definition trigger.c:3924

References afterTriggers, Assert, AfterTriggersData::batch_callbacks, AfterTriggersData::event_cxt, AfterTriggersData::events, fb(), AfterTriggersData::firing_batch_callbacks, AfterTriggersData::firing_counter, AfterTriggersData::firing_depth, AfterTriggerEventList::head, AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, NIL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, and AfterTriggersData::trans_stack.

Referenced by StartTransaction().

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)
extern

Definition at line 5186 of file trigger.c.

5187{
5189
5190 /* Must be inside a query, too */
5192
5193 /*
5194 * If we never even got as far as initializing the event stack, there
5195 * certainly won't be any events, so exit quickly.
5196 */
5198 {
5200 return;
5201 }
5202
5203 /*
5204 * Process all immediate-mode triggers queued by the query, and move the
5205 * deferred ones to the main list of deferred events.
5206 *
5207 * Notice that we decide which ones will be fired, and put the deferred
5208 * ones on the main list, before anything is actually fired. This ensures
5209 * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5210 * IMMEDIATE: all events we have decided to defer will be available for it
5211 * to fire.
5212 *
5213 * We loop in case a trigger queues more events at the same query level.
5214 * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5215 * will instead fire any triggers in a dedicated query level. Foreign key
5216 * enforcement triggers do add to the current query level, thanks to their
5217 * passing fire_triggers = false to SPI_execute_snapshot(). Other
5218 * C-language triggers might do likewise.
5219 *
5220 * If we find no firable events, we don't have to increment
5221 * firing_counter.
5222 */
5224
5226 for (;;)
5227 {
5228 if (afterTriggerMarkEvents(&qs->events, &afterTriggers.events, true))
5229 {
5231 AfterTriggerEventChunk *oldtail = qs->events.tail;
5232
5233 if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5234 break; /* all fired */
5235
5236 /*
5237 * Firing a trigger could result in query_stack being repalloc'd,
5238 * so we must recalculate qs after each afterTriggerInvokeEvents
5239 * call. Furthermore, it's unsafe to pass delete_ok = true here,
5240 * because that could cause afterTriggerInvokeEvents to try to
5241 * access qs->events after the stack has been repalloc'd.
5242 */
5244
5245 /*
5246 * We'll need to scan the events list again. To reduce the cost
5247 * of doing so, get rid of completely-fired chunks. We know that
5248 * all events were marked IN_PROGRESS or DONE at the conclusion of
5249 * afterTriggerMarkEvents, so any still-interesting events must
5250 * have been added after that, and so must be in the chunk that
5251 * was then the tail chunk, or in later chunks. So, zap all
5252 * chunks before oldtail. This is approximately the same set of
5253 * events we would have gotten rid of by passing delete_ok = true.
5254 */
5255 Assert(oldtail != NULL);
5256 while (qs->events.head != oldtail)
5258 }
5259 else
5260 break;
5261 }
5262
5263 /*
5264 * Fire batch callbacks before releasing query-level storage and before
5265 * decrementing query_depth. Callbacks may do real work (index probes,
5266 * error reporting).
5267 *
5268 * Recompute qs first: the loop above refreshes it after each
5269 * afterTriggerInvokeEvents() call (see comment there), but the "all
5270 * fired" break exits without doing so, leaving qs potentially stale here.
5271 */
5273 FireAfterTriggerBatchCallbacks(qs->batch_callbacks);
5274
5275 /* Release query-level-local storage, including tuplestores if any */
5277
5280}
static void FireAfterTriggerBatchCallbacks(List *callbacks)
Definition trigger.c:6904
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition trigger.c:4313
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition trigger.c:5291
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition trigger.c:4661
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition trigger.c:4745

References afterTriggerDeleteHeadEventChunk(), AfterTriggerFreeQuery(), afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, Assert, AfterTriggersData::events, fb(), FireAfterTriggerBatchCallbacks(), AfterTriggersData::firing_counter, AfterTriggersData::firing_depth, AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.

Referenced by CopyFrom(), ExecForPortionOfLeftovers(), ExecuteTruncateGuts(), finish_edata(), and standard_ExecutorFinish().

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)
extern

Definition at line 5519 of file trigger.c.

5520{
5521 int my_level = GetCurrentTransactionNestLevel();
5523 AfterTriggerEvent event;
5526
5527 /*
5528 * Pop the prior state if needed.
5529 */
5530 if (isCommit)
5531 {
5533 /* If we saved a prior state, we don't need it anymore */
5535 if (state != NULL)
5536 pfree(state);
5537 /* this avoids double pfree if error later: */
5538 afterTriggers.trans_stack[my_level].state = NULL;
5541 }
5542 else
5543 {
5544 /*
5545 * Aborting. It is possible subxact start failed before calling
5546 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5547 * trans_stack levels that aren't there.
5548 */
5549 if (my_level >= afterTriggers.maxtransdepth)
5550 return;
5551
5552 /*
5553 * Release query-level storage for queries being aborted, and restore
5554 * query_depth to its pre-subxact value. This assumes that a
5555 * subtransaction will not add events to query levels started in a
5556 * earlier transaction state.
5557 */
5559 {
5563 }
5566
5567 /*
5568 * Restore the global deferred-event list to its former length,
5569 * discarding any events queued by the subxact.
5570 */
5572 &afterTriggers.trans_stack[my_level].events);
5573
5574 /*
5575 * Restore the trigger state. If the saved state is NULL, then this
5576 * subxact didn't save it, so it doesn't need restoring.
5577 */
5579 if (state != NULL)
5580 {
5583 }
5584 /* this avoids double pfree if error later: */
5585 afterTriggers.trans_stack[my_level].state = NULL;
5586
5587 /*
5588 * Scan for any remaining deferred events that were marked DONE or IN
5589 * PROGRESS by this subxact or a child, and un-mark them. We can
5590 * recognize such events because they have a firing ID greater than or
5591 * equal to the firing_counter value we saved at subtransaction start.
5592 * (This essentially assumes that the current subxact includes all
5593 * subxacts started after it.)
5594 */
5597 {
5599
5600 if (event->ate_flags &
5602 {
5603 if (evtshared->ats_firing_id >= subxact_firing_id)
5604 event->ate_flags &=
5606 }
5607 }
5608 }
5609
5610 /* Reset in case a callback threw an error while firing. */
5612}
void pfree(void *pointer)
Definition mcxt.c:1619
TriggerFlags ate_flags
Definition trigger.c:3735
#define AFTER_TRIGGER_IN_PROGRESS
Definition trigger.c:3710
#define GetTriggerSharedData(evt)
Definition trigger.c:3778
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition trigger.c:4273
#define AFTER_TRIGGER_DONE
Definition trigger.c:3709
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition trigger.c:3815

References AFTER_TRIGGER_DONE, AFTER_TRIGGER_IN_PROGRESS, AfterTriggerFreeQuery(), afterTriggerRestoreEventList(), afterTriggers, Assert, AfterTriggerEventData::ate_flags, AfterTriggersData::events, AfterTriggersTransData::events, fb(), AfterTriggersData::firing_batch_callbacks, 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().

◆ AfterTriggerEndXact()

void AfterTriggerEndXact ( bool  isCommit)
extern

Definition at line 5417 of file trigger.c.

5418{
5419 /*
5420 * Forget the pending-events list.
5421 *
5422 * Since all the info is in TopTransactionContext or children thereof, we
5423 * don't really need to do anything to reclaim memory. However, the
5424 * pending-events list could be large, and so it's useful to discard it as
5425 * soon as possible --- especially if we are aborting because we ran out
5426 * of memory for the list!
5427 */
5429 {
5435 }
5436
5437 /*
5438 * Forget any subtransaction state as well. Since this can't be very
5439 * large, we let the eventual reset of TopTransactionContext free the
5440 * memory instead of doing it here.
5441 */
5444
5445
5446 /*
5447 * Forget the query stack and constraint-related state information. As
5448 * with the subtransaction state information, we don't bother freeing the
5449 * memory here.
5450 */
5454
5455 /* No more afterTriggers manipulation until next transaction starts. */
5457
5459
5463}
void list_free_deep(List *list)
Definition list.c:1560
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
AfterTriggerEventChunk * tail
Definition trigger.c:3803

References afterTriggers, AfterTriggersData::batch_callbacks, AfterTriggersData::event_cxt, AfterTriggersData::events, fb(), AfterTriggersData::firing_batch_callbacks, AfterTriggersData::firing_depth, AfterTriggerEventList::head, list_free_deep(), AfterTriggersData::maxquerydepth, AfterTriggersData::maxtransdepth, MemoryContextDelete(), NIL, AfterTriggersData::query_depth, AfterTriggersData::query_stack, AfterTriggersData::state, AfterTriggerEventList::tail, AfterTriggerEventList::tailfree, and AfterTriggersData::trans_stack.

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

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )
extern

Definition at line 5354 of file trigger.c.

5355{
5356 AfterTriggerEventList *events;
5357 bool snap_pushed = false;
5358
5359 /* Must not be inside a query */
5361
5362 /*
5363 * If there are any triggers to fire, make sure we have set a snapshot for
5364 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5365 * can't assume ActiveSnapshot is valid on entry.)
5366 */
5367 events = &afterTriggers.events;
5368 if (events->head != NULL)
5369 {
5371 snap_pushed = true;
5372 }
5373
5374 /*
5375 * Run all the remaining triggers. Loop until they are all gone, in case
5376 * some trigger queues more for us to do.
5377 */
5379 while (afterTriggerMarkEvents(events, NULL, false))
5380 {
5382
5383 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5384 break; /* all fired */
5385 }
5386
5387 /* Flush any fast-path batches accumulated by the triggers just fired. */
5389
5391
5392 /*
5393 * We don't bother freeing the event list or batch_callbacks, since they
5394 * will go away anyway (and more efficiently than via pfree) in
5395 * AfterTriggerEndXact.
5396 */
5397
5398 if (snap_pushed)
5400}
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition snapmgr.c:682
void PopActiveSnapshot(void)
Definition snapmgr.c:775

References afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, Assert, AfterTriggersData::batch_callbacks, AfterTriggersData::events, fb(), FireAfterTriggerBatchCallbacks(), AfterTriggersData::firing_counter, AfterTriggersData::firing_depth, GetTransactionSnapshot(), AfterTriggerEventList::head, PopActiveSnapshot(), PushActiveSnapshot(), and AfterTriggersData::query_depth.

Referenced by CommitTransaction(), and PrepareTransaction().

◆ AfterTriggerIsActive()

bool AfterTriggerIsActive ( void  )
extern

Definition at line 6928 of file trigger.c.

6929{
6930 return afterTriggers.firing_depth > 0;
6931}

References afterTriggers, and AfterTriggersData::firing_depth.

Referenced by RI_FKey_check().

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)
extern

Definition at line 6175 of file trigger.c.

6176{
6177 AfterTriggerEvent event;
6179 int depth;
6180
6181 /* Scan queued events */
6183 {
6185
6186 /*
6187 * We can ignore completed events. (Even if a DONE flag is rolled
6188 * back by subxact abort, it's OK because the effects of the TRUNCATE
6189 * or whatever must get rolled back too.)
6190 */
6191 if (event->ate_flags & AFTER_TRIGGER_DONE)
6192 continue;
6193
6194 if (evtshared->ats_relid == relid)
6195 return true;
6196 }
6197
6198 /*
6199 * Also scan events queued by incomplete queries. This could only matter
6200 * if TRUNCATE/etc is executed by a function or trigger within an updating
6201 * query on the same relation, which is pretty perverse, but let's check.
6202 */
6203 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6204 {
6206 {
6208
6209 if (event->ate_flags & AFTER_TRIGGER_DONE)
6210 continue;
6211
6212 if (evtshared->ats_relid == relid)
6213 return true;
6214 }
6215 }
6216
6217 return false;
6218}
AfterTriggerEventList events
Definition trigger.c:3937

References AFTER_TRIGGER_DONE, afterTriggers, AfterTriggerEventData::ate_flags, AfterTriggersData::events, AfterTriggersQueryData::events, fb(), for_each_event_chunk, GetTriggerSharedData, AfterTriggersData::maxquerydepth, AfterTriggersData::query_depth, and AfterTriggersData::query_stack.

Referenced by CheckTableNotInUse().

◆ AfterTriggerSetState()

void AfterTriggerSetState ( ConstraintsSetStmt stmt)
extern

Definition at line 5851 of file trigger.c.

5852{
5853 int my_level = GetCurrentTransactionNestLevel();
5854
5855 /* If we haven't already done so, initialize our state. */
5856 if (afterTriggers.state == NULL)
5858
5859 /*
5860 * If in a subtransaction, and we didn't save the current state already,
5861 * save it so it can be restored if the subtransaction aborts.
5862 */
5863 if (my_level > 1 &&
5864 afterTriggers.trans_stack[my_level].state == NULL)
5865 {
5866 afterTriggers.trans_stack[my_level].state =
5868 }
5869
5870 /*
5871 * Handle SET CONSTRAINTS ALL ...
5872 */
5873 if (stmt->constraints == NIL)
5874 {
5875 /*
5876 * Forget any previous SET CONSTRAINTS commands in this transaction.
5877 */
5879
5880 /*
5881 * Set the per-transaction ALL state to known.
5882 */
5885 }
5886 else
5887 {
5890 List *conoidlist = NIL;
5891 List *tgoidlist = NIL;
5892 ListCell *lc;
5893
5894 /*
5895 * Handle SET CONSTRAINTS constraint-name [, ...]
5896 *
5897 * First, identify all the named constraints and make a list of their
5898 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5899 * the same name within a schema, the specifications are not
5900 * necessarily unique. Our strategy is to target all matching
5901 * constraints within the first search-path schema that has any
5902 * matches, but disregard matches in schemas beyond the first match.
5903 * (This is a bit odd but it's the historical behavior.)
5904 *
5905 * A constraint in a partitioned table may have corresponding
5906 * constraints in the partitions. Grab those too.
5907 */
5909
5910 foreach(lc, stmt->constraints)
5911 {
5912 RangeVar *constraint = lfirst(lc);
5913 bool found;
5915 ListCell *nslc;
5916
5917 if (constraint->catalogname)
5918 {
5919 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5920 ereport(ERROR,
5922 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5923 constraint->catalogname, constraint->schemaname,
5924 constraint->relname)));
5925 }
5926
5927 /*
5928 * If we're given the schema name with the constraint, look only
5929 * in that schema. If given a bare constraint name, use the
5930 * search path to find the first matching constraint.
5931 */
5932 if (constraint->schemaname)
5933 {
5935 false);
5936
5938 }
5939 else
5940 {
5942 }
5943
5944 found = false;
5945 foreach(nslc, namespacelist)
5946 {
5949 ScanKeyData skey[2];
5950 HeapTuple tup;
5951
5952 ScanKeyInit(&skey[0],
5955 CStringGetDatum(constraint->relname));
5956 ScanKeyInit(&skey[1],
5960
5962 true, NULL, 2, skey);
5963
5965 {
5967
5968 if (con->condeferrable)
5969 conoidlist = lappend_oid(conoidlist, con->oid);
5970 else if (stmt->deferred)
5971 ereport(ERROR,
5973 errmsg("constraint \"%s\" is not deferrable",
5974 constraint->relname)));
5975 found = true;
5976 }
5977
5979
5980 /*
5981 * Once we've found a matching constraint we do not search
5982 * later parts of the search path.
5983 */
5984 if (found)
5985 break;
5986 }
5987
5989
5990 /*
5991 * Not found ?
5992 */
5993 if (!found)
5994 ereport(ERROR,
5996 errmsg("constraint \"%s\" does not exist",
5997 constraint->relname)));
5998 }
5999
6000 /*
6001 * Scan for any possible descendants of the constraints. We append
6002 * whatever we find to the same list that we're scanning; this has the
6003 * effect that we create new scans for those, too, so if there are
6004 * further descendents, we'll also catch them.
6005 */
6006 foreach(lc, conoidlist)
6007 {
6008 Oid parent = lfirst_oid(lc);
6010 SysScanDesc scan;
6011 HeapTuple tuple;
6012
6013 ScanKeyInit(&key,
6016 ObjectIdGetDatum(parent));
6017
6018 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
6019
6020 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
6021 {
6023
6024 conoidlist = lappend_oid(conoidlist, con->oid);
6025 }
6026
6027 systable_endscan(scan);
6028 }
6029
6031
6032 /*
6033 * Now, locate the trigger(s) implementing each of these constraints,
6034 * and make a list of their OIDs.
6035 */
6037
6038 foreach(lc, conoidlist)
6039 {
6040 Oid conoid = lfirst_oid(lc);
6043 HeapTuple htup;
6044
6048 ObjectIdGetDatum(conoid));
6049
6051 NULL, 1, &skey);
6052
6054 {
6056
6057 /*
6058 * Silently skip triggers that are marked as non-deferrable in
6059 * pg_trigger. This is not an error condition, since a
6060 * deferrable RI constraint may have some non-deferrable
6061 * actions.
6062 */
6063 if (pg_trigger->tgdeferrable)
6065 }
6066
6068 }
6069
6071
6072 /*
6073 * Now we can set the trigger states of individual triggers for this
6074 * xact.
6075 */
6076 foreach(lc, tgoidlist)
6077 {
6078 Oid tgoid = lfirst_oid(lc);
6080 bool found = false;
6081 int i;
6082
6083 for (i = 0; i < state->numstates; i++)
6084 {
6085 if (state->trigstates[i].sct_tgoid == tgoid)
6086 {
6087 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
6088 found = true;
6089 break;
6090 }
6091 }
6092 if (!found)
6093 {
6095 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
6096 }
6097 }
6098 }
6099
6100 /*
6101 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
6102 * checks against that constraint must be made when the SET CONSTRAINTS
6103 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
6104 * apply retroactively. We've updated the constraints state, so scan the
6105 * list of previously deferred events to fire any that have now become
6106 * immediate.
6107 *
6108 * Obviously, if this was SET ... DEFERRED then it can't have converted
6109 * any unfired events to immediate, so we need do nothing in that case.
6110 */
6111 if (!stmt->deferred)
6112 {
6114 bool snapshot_set = false;
6115
6117 while (afterTriggerMarkEvents(events, NULL, true))
6118 {
6120
6121 /*
6122 * Make sure a snapshot has been established in case trigger
6123 * functions need one. Note that we avoid setting a snapshot if
6124 * we don't find at least one trigger that has to be fired now.
6125 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6126 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6127 * at the start of a transaction it's not possible for any trigger
6128 * events to be queued yet.)
6129 */
6130 if (!snapshot_set)
6131 {
6133 snapshot_set = true;
6134 }
6135
6136 /*
6137 * We can delete fired events if we are at top transaction level,
6138 * but we'd better not if inside a subtransaction, since the
6139 * subtransaction could later get rolled back.
6140 */
6142 !IsSubTransaction()))
6143 break; /* all fired */
6144 }
6145
6146 /*
6147 * Flush any fast-path batches accumulated by the triggers just fired.
6148 */
6153
6154 if (snapshot_set)
6156 }
6157}
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
Oid MyDatabaseId
Definition globals.c:96
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
int i
Definition isn.c:77
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
void list_free(List *list)
Definition list.c:1546
#define AccessShareLock
Definition lockdefs.h:36
char * get_database_name(Oid dbid)
Definition lsyscache.c:1392
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition namespace.c:3457
List * fetch_search_path(bool includeImplicit)
Definition namespace.c:4891
static char * errmsg
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
#define lfirst(lc)
Definition pg_list.h:172
#define list_make1_oid(x1)
Definition pg_list.h:274
#define lfirst_oid(lc)
Definition pg_list.h:174
END_CATALOG_STRUCT typedef FormData_pg_trigger * Form_pg_trigger
Definition pg_trigger.h:84
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
#define BTEqualStrategyNumber
Definition stratnum.h:31
char * relname
Definition primnodes.h:84
char * catalogname
Definition primnodes.h:78
char * schemaname
Definition primnodes.h:81
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
static SetConstraintState SetConstraintStateCopy(SetConstraintState origstate)
Definition trigger.c:5801
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition trigger.c:5821
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition trigger.c:5776
bool IsSubTransaction(void)
Definition xact.c:5098

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, AfterTriggersData::batch_callbacks, BTEqualStrategyNumber, RangeVar::catalogname, CStringGetDatum(), ereport, errcode(), errmsg, ERROR, AfterTriggersData::events, fb(), fetch_search_path(), FireAfterTriggerBatchCallbacks(), AfterTriggersData::firing_counter, AfterTriggersData::firing_depth, Form_pg_constraint, Form_pg_trigger, get_database_name(), GetCurrentTransactionNestLevel(), GETSTRUCT(), GetTransactionSnapshot(), HeapTupleIsValid, i, IsSubTransaction(), lappend_oid(), lfirst, lfirst_oid, list_free(), list_free_deep(), list_make1_oid, LookupExplicitNamespace(), MyDatabaseId, NIL, SetConstraintStateData::numstates, ObjectIdGetDatum(), PopActiveSnapshot(), PushActiveSnapshot(), RangeVar::relname, ScanKeyInit(), RangeVar::schemaname, SetConstraintStateAddItem(), SetConstraintStateCopy(), SetConstraintStateCreate(), AfterTriggersData::state, AfterTriggersTransData::state, stmt, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and AfterTriggersData::trans_stack.

Referenced by standard_ProcessUtility().

◆ AtEOXact_RI()

void AtEOXact_RI ( bool  isCommit)
extern

Definition at line 4301 of file ri_triggers.c.

4302{
4303 /*
4304 * The cache must be empty on a clean commit or prepare; a survivor means
4305 * a trigger batch went unflushed. Assert for assert-enabled builds and,
4306 * since the transaction is already committed by now and FK checks may
4307 * have been skipped, also warn in production builds.
4308 */
4311 elog(WARNING, "RI fast-path cache not flushed at end of transaction");
4312
4313 /*
4314 * Clear the static pointers/flags. The cache memory lives in
4315 * TopTransactionContext and is freed by the end-of-transaction
4316 * memory-context reset; here we only drop the references to it.
4317 */
4320
4321 /*
4322 * Also clear the in-flush flag. ri_FastPathEndBatch() already clears it
4323 * via PG_FINALLY, so this is just defensive: it keeps a stale flag from
4324 * surviving into the next transaction should any future path leave it
4325 * set.
4326 */
4327 ri_fastpath_flushing = false;
4328}
#define WARNING
Definition elog.h:37
#define elog(elevel,...)
Definition elog.h:228
static bool ri_fastpath_callback_registered
static HTAB * ri_fastpath_cache
static bool ri_fastpath_flushing

References Assert, elog, fb(), ri_fastpath_cache, ri_fastpath_callback_registered, ri_fastpath_flushing, and WARNING.

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

◆ CopyTriggerDesc()

TriggerDesc * CopyTriggerDesc ( TriggerDesc trigdesc)
extern

Definition at line 2117 of file trigger.c.

2118{
2121 int i;
2122
2123 if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2124 return NULL;
2125
2127 memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2128
2129 trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2130 memcpy(trigger, trigdesc->triggers,
2131 trigdesc->numtriggers * sizeof(Trigger));
2132 newdesc->triggers = trigger;
2133
2134 for (i = 0; i < trigdesc->numtriggers; i++)
2135 {
2136 trigger->tgname = pstrdup(trigger->tgname);
2137 if (trigger->tgnattr > 0)
2138 {
2139 int16 *newattr;
2140
2141 newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2142 memcpy(newattr, trigger->tgattr,
2143 trigger->tgnattr * sizeof(int16));
2144 trigger->tgattr = newattr;
2145 }
2146 if (trigger->tgnargs > 0)
2147 {
2148 char **newargs;
2149 int16 j;
2150
2151 newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2152 for (j = 0; j < trigger->tgnargs; j++)
2153 newargs[j] = pstrdup(trigger->tgargs[j]);
2154 trigger->tgargs = newargs;
2155 }
2156 if (trigger->tgqual)
2157 trigger->tgqual = pstrdup(trigger->tgqual);
2158 if (trigger->tgoldtable)
2159 trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2160 if (trigger->tgnewtable)
2161 trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2162 trigger++;
2163 }
2164
2165 return newdesc;
2166}
int16_t int16
Definition c.h:678
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
#define palloc_object(type)
Definition fe_memutils.h:89
int j
Definition isn.c:78
char * pstrdup(const char *in)
Definition mcxt.c:1910
void * palloc(Size size)
Definition mcxt.c:1390
int numtriggers
Definition reltrigger.h:50
Trigger * triggers
Definition reltrigger.h:49

References fb(), i, j, memcpy(), TriggerDesc::numtriggers, palloc(), palloc_object, pstrdup(), and TriggerDesc::triggers.

Referenced by InitResultRelInfo(), and RelationBuildTriggers().

◆ CreateTrigger()

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

Definition at line 162 of file trigger.c.

166{
167 return
168 CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid,
169 constraintOid, indexOid, funcoid,
170 parentTriggerOid, whenClause, isInternal,
172}
ObjectAddress CreateTriggerFiringOn(const CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition trigger.c:179
#define TRIGGER_FIRES_ON_ORIGIN
Definition trigger.h:151

References CreateTriggerFiringOn(), fb(), stmt, and TRIGGER_FIRES_ON_ORIGIN.

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

◆ CreateTriggerFiringOn()

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

Definition at line 179 of file trigger.c.

184{
185 int16 tgtype;
186 int ncolumns;
187 int16 *columns;
188 int2vector *tgattr;
190 char *qual;
192 bool nulls[Natts_pg_trigger];
193 Relation rel;
197 HeapTuple tuple = NULL;
201 char *trigname;
205 char *oldtablename = NULL;
206 char *newtablename = NULL;
208 bool trigger_exists = false;
210 bool existing_isInternal = false;
211 bool existing_isClone = false;
212
213 if (OidIsValid(relOid))
214 rel = table_open(relOid, ShareRowExclusiveLock);
215 else
216 rel = table_openrv(stmt->relation, ShareRowExclusiveLock);
217
218 /*
219 * Triggers must be on tables or views, and there are additional
220 * relation-type-specific restrictions.
221 */
222 if (rel->rd_rel->relkind == RELKIND_RELATION)
223 {
224 /* Tables can't have INSTEAD OF triggers */
225 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
226 stmt->timing != TRIGGER_TYPE_AFTER)
229 errmsg("\"%s\" is a table",
231 errdetail("Tables cannot have INSTEAD OF triggers.")));
232 }
233 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
234 {
235 /* Partitioned tables can't have INSTEAD OF triggers */
236 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
237 stmt->timing != TRIGGER_TYPE_AFTER)
240 errmsg("\"%s\" is a table",
242 errdetail("Tables cannot have INSTEAD OF triggers.")));
243
244 /*
245 * FOR EACH ROW triggers have further restrictions
246 */
247 if (stmt->row)
248 {
249 /*
250 * Disallow use of transition tables.
251 *
252 * Note that we have another restriction about transition tables
253 * in partitions; search for 'has_superclass' below for an
254 * explanation. The check here is just to protect from the fact
255 * that if we allowed it here, the creation would succeed for a
256 * partitioned table with no partitions, but would be blocked by
257 * the other restriction when the first partition was created,
258 * which is very unfriendly behavior.
259 */
260 if (stmt->transitionRels != NIL)
263 errmsg("\"%s\" is a partitioned table",
265 errdetail("ROW triggers with transition tables are not supported on partitioned tables.")));
266 }
267 }
268 else if (rel->rd_rel->relkind == RELKIND_VIEW)
269 {
270 /*
271 * Views can have INSTEAD OF triggers (which we check below are
272 * row-level), or statement-level BEFORE/AFTER triggers.
273 */
274 if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row)
277 errmsg("\"%s\" is a view",
279 errdetail("Views cannot have row-level BEFORE or AFTER triggers.")));
280 /* Disallow TRUNCATE triggers on VIEWs */
281 if (TRIGGER_FOR_TRUNCATE(stmt->events))
284 errmsg("\"%s\" is a view",
286 errdetail("Views cannot have TRUNCATE triggers.")));
287 }
288 else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
289 {
290 if (stmt->timing != TRIGGER_TYPE_BEFORE &&
291 stmt->timing != TRIGGER_TYPE_AFTER)
294 errmsg("\"%s\" is a foreign table",
296 errdetail("Foreign tables cannot have INSTEAD OF triggers.")));
297
298 /*
299 * We disallow constraint triggers to protect the assumption that
300 * triggers on FKs can't be deferred. See notes with AfterTriggers
301 * data structures, below.
302 */
303 if (stmt->isconstraint)
306 errmsg("\"%s\" is a foreign table",
308 errdetail("Foreign tables cannot have constraint triggers.")));
309 }
310 else
313 errmsg("relation \"%s\" cannot have triggers",
316
317 /*
318 * Conflict log tables are used internally for logical replication
319 * conflict logging and should not have triggers, as it could disrupt
320 * conflict logging.
321 */
325 errmsg("cannot create trigger on conflict log table \"%s\"",
327 errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
328
332 errmsg("permission denied: \"%s\" is a system catalog",
334
335 if (stmt->isconstraint)
336 {
337 /*
338 * We must take a lock on the target relation to protect against
339 * concurrent drop. It's not clear that AccessShareLock is strong
340 * enough, but we certainly need at least that much... otherwise, we
341 * might end up creating a pg_constraint entry referencing a
342 * nonexistent table.
343 */
345 {
348 }
349 else if (stmt->constrrel != NULL)
351 false);
352 }
353
354 /* permission checks */
355 if (!isInternal)
356 {
359 if (aclresult != ACLCHECK_OK)
362
364 {
367 if (aclresult != ACLCHECK_OK)
370 }
371 }
372
373 /*
374 * When called on a partitioned table to create a FOR EACH ROW trigger
375 * that's not internal, we create one trigger for each partition, too.
376 *
377 * For that, we'd better hold lock on all of them ahead of time.
378 */
379 partition_recurse = !isInternal && stmt->row &&
380 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
384
385 /* Compute tgtype */
386 TRIGGER_CLEAR_TYPE(tgtype);
387 if (stmt->row)
388 TRIGGER_SETT_ROW(tgtype);
389 tgtype |= stmt->timing;
390 tgtype |= stmt->events;
391
392 /* Disallow ROW-level TRUNCATE triggers */
393 if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
396 errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
397
398 /* INSTEAD triggers must be row-level, and can't have WHEN or columns */
399 if (TRIGGER_FOR_INSTEAD(tgtype))
400 {
401 if (!TRIGGER_FOR_ROW(tgtype))
404 errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
405 if (stmt->whenClause)
408 errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
409 if (stmt->columns != NIL)
412 errmsg("INSTEAD OF triggers cannot have column lists")));
413 }
414
415 /*
416 * We don't yet support naming ROW transition variables, but the parser
417 * recognizes the syntax so we can give a nicer message here.
418 *
419 * Per standard, REFERENCING TABLE names are only allowed on AFTER
420 * triggers. Per standard, REFERENCING ROW names are not allowed with FOR
421 * EACH STATEMENT. Per standard, each OLD/NEW, ROW/TABLE permutation is
422 * only allowed once. Per standard, OLD may not be specified when
423 * creating a trigger only for INSERT, and NEW may not be specified when
424 * creating a trigger only for DELETE.
425 *
426 * Notice that the standard allows an AFTER ... FOR EACH ROW trigger to
427 * reference both ROW and TABLE transition data.
428 */
429 if (stmt->transitionRels != NIL)
430 {
431 List *varList = stmt->transitionRels;
432 ListCell *lc;
433
434 foreach(lc, varList)
435 {
437
438 if (!(tt->isTable))
441 errmsg("ROW variable naming in the REFERENCING clause is not supported"),
442 errhint("Use OLD TABLE or NEW TABLE for naming transition tables.")));
443
444 /*
445 * Because of the above test, we omit further ROW-related testing
446 * below. If we later allow naming OLD and NEW ROW variables,
447 * adjustments will be needed below.
448 */
449
450 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
453 errmsg("\"%s\" is a foreign table",
455 errdetail("Triggers on foreign tables cannot have transition tables.")));
456
457 if (rel->rd_rel->relkind == RELKIND_VIEW)
460 errmsg("\"%s\" is a view",
462 errdetail("Triggers on views cannot have transition tables.")));
463
464 /*
465 * We currently don't allow row-level triggers with transition
466 * tables on partition or inheritance children. Such triggers
467 * would somehow need to see tuples converted to the format of the
468 * table they're attached to, and it's not clear which subset of
469 * tuples each child should see. See also the prohibitions in
470 * ATExecAttachPartition() and ATExecAddInherit().
471 */
472 if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id))
473 {
474 /* Use appropriate error message. */
475 if (rel->rd_rel->relispartition)
478 errmsg("ROW triggers with transition tables are not supported on partitions")));
479 else
482 errmsg("ROW triggers with transition tables are not supported on inheritance children")));
483 }
484
485 if (stmt->timing != TRIGGER_TYPE_AFTER)
488 errmsg("transition table name can only be specified for an AFTER trigger")));
489
490 if (TRIGGER_FOR_TRUNCATE(tgtype))
493 errmsg("TRUNCATE triggers with transition tables are not supported")));
494
495 /*
496 * We currently don't allow multi-event triggers ("INSERT OR
497 * UPDATE") with transition tables, because it's not clear how to
498 * handle INSERT ... ON CONFLICT statements which can fire both
499 * INSERT and UPDATE triggers. We show the inserted tuples to
500 * INSERT triggers and the updated tuples to UPDATE triggers, but
501 * it's not yet clear what INSERT OR UPDATE trigger should see.
502 * This restriction could be lifted if we can decide on the right
503 * semantics in a later release.
504 */
505 if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) +
506 (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) +
507 (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1)
510 errmsg("transition tables cannot be specified for triggers with more than one event")));
511
512 /*
513 * We currently don't allow column-specific triggers with
514 * transition tables. Per spec, that seems to require
515 * accumulating separate transition tables for each combination of
516 * columns, which is a lot of work for a rather marginal feature.
517 */
518 if (stmt->columns != NIL)
521 errmsg("transition tables cannot be specified for triggers with column lists")));
522
523 /*
524 * We disallow constraint triggers with transition tables, to
525 * protect the assumption that such triggers can't be deferred.
526 * See notes with AfterTriggers data structures, below.
527 *
528 * Currently this is enforced by the grammar, so just Assert here.
529 */
530 Assert(!stmt->isconstraint);
531
532 if (tt->isNew)
533 {
534 if (!(TRIGGER_FOR_INSERT(tgtype) ||
535 TRIGGER_FOR_UPDATE(tgtype)))
538 errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger")));
539
540 if (newtablename != NULL)
543 errmsg("NEW TABLE cannot be specified multiple times")));
544
545 newtablename = tt->name;
546 }
547 else
548 {
549 if (!(TRIGGER_FOR_DELETE(tgtype) ||
550 TRIGGER_FOR_UPDATE(tgtype)))
553 errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger")));
554
555 if (oldtablename != NULL)
558 errmsg("OLD TABLE cannot be specified multiple times")));
559
560 oldtablename = tt->name;
561 }
562 }
563
564 if (newtablename != NULL && oldtablename != NULL &&
568 errmsg("OLD TABLE name and NEW TABLE name cannot be the same")));
569 }
570
571 /*
572 * Parse the WHEN clause, if any and we weren't passed an already
573 * transformed one.
574 *
575 * Note that as a side effect, we fill whenRtable when parsing. If we got
576 * an already parsed clause, this does not occur, which is what we want --
577 * no point in adding redundant dependencies below.
578 */
579 if (!whenClause && stmt->whenClause)
580 {
581 ParseState *pstate;
583 List *varList;
584 ListCell *lc;
585
586 /* Set up a pstate to parse with */
587 pstate = make_parsestate(NULL);
588 pstate->p_sourcetext = queryString;
589
590 /*
591 * Set up nsitems for OLD and NEW references.
592 *
593 * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
594 */
597 makeAlias("old", NIL),
598 false, false);
599 addNSItemToQuery(pstate, nsitem, false, true, true);
602 makeAlias("new", NIL),
603 false, false);
604 addNSItemToQuery(pstate, nsitem, false, true, true);
605
606 /* Transform expression. Copy to be sure we don't modify original */
607 whenClause = transformWhereClause(pstate,
608 copyObject(stmt->whenClause),
610 "WHEN");
611 /* we have to fix its collations too */
612 assign_expr_collations(pstate, whenClause);
613
614 /*
615 * Check for disallowed references to OLD/NEW.
616 *
617 * NB: pull_var_clause is okay here only because we don't allow
618 * subselects in WHEN clauses; it would fail to examine the contents
619 * of subselects.
620 */
621 varList = pull_var_clause(whenClause, 0);
622 foreach(lc, varList)
623 {
624 Var *var = (Var *) lfirst(lc);
625
626 switch (var->varno)
627 {
628 case PRS2_OLD_VARNO:
629 if (!TRIGGER_FOR_ROW(tgtype))
632 errmsg("statement trigger's WHEN condition cannot reference column values"),
633 parser_errposition(pstate, var->location)));
634 if (TRIGGER_FOR_INSERT(tgtype))
637 errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
638 parser_errposition(pstate, var->location)));
639 /* system columns are okay here */
640 break;
641 case PRS2_NEW_VARNO:
642 if (!TRIGGER_FOR_ROW(tgtype))
645 errmsg("statement trigger's WHEN condition cannot reference column values"),
646 parser_errposition(pstate, var->location)));
647 if (TRIGGER_FOR_DELETE(tgtype))
650 errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
651 parser_errposition(pstate, var->location)));
652 if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
655 errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
656 parser_errposition(pstate, var->location)));
657 if (TRIGGER_FOR_BEFORE(tgtype) &&
658 var->varattno == 0 &&
659 RelationGetDescr(rel)->constr &&
660 (RelationGetDescr(rel)->constr->has_generated_stored ||
661 RelationGetDescr(rel)->constr->has_generated_virtual))
664 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
665 errdetail("A whole-row reference is used and the table contains generated columns."),
666 parser_errposition(pstate, var->location)));
667 if (TRIGGER_FOR_BEFORE(tgtype) &&
668 var->varattno > 0 &&
669 TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attgenerated)
672 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
673 errdetail("Column \"%s\" is a generated column.",
674 NameStr(TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attname)),
675 parser_errposition(pstate, var->location)));
676 break;
677 default:
678 /* can't happen without add_missing_from, so just elog */
679 elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
680 break;
681 }
682 }
683
684 /* we'll need the rtable for recordDependencyOnExpr */
685 whenRtable = pstate->p_rtable;
686
687 qual = nodeToString(whenClause);
688
689 free_parsestate(pstate);
690 }
691 else if (!whenClause)
692 {
693 whenClause = NULL;
694 whenRtable = NIL;
695 qual = NULL;
696 }
697 else
698 {
699 qual = nodeToString(whenClause);
700 whenRtable = NIL;
701 }
702
703 /*
704 * Find and validate the trigger function.
705 */
706 if (!OidIsValid(funcoid))
707 funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
708 if (!isInternal)
709 {
711 if (aclresult != ACLCHECK_OK)
713 NameListToString(stmt->funcname));
714 }
716 if (funcrettype != TRIGGEROID)
719 errmsg("function %s must return type %s",
720 NameListToString(stmt->funcname), "trigger")));
721
722 /*
723 * Scan pg_trigger to see if there is already a trigger of the same name.
724 * Skip this for internally generated triggers, since we'll modify the
725 * name to be unique below.
726 *
727 * NOTE that this is cool only because we have ShareRowExclusiveLock on
728 * the relation, so the trigger set won't be changing underneath us.
729 */
731 if (!isInternal)
732 {
735
736 ScanKeyInit(&skeys[0],
740
741 ScanKeyInit(&skeys[1],
744 CStringGetDatum(stmt->trigname));
745
747 NULL, 2, skeys);
748
749 /* There should be at most one matching tuple */
751 {
753
754 trigoid = oldtrigger->oid;
755 existing_constraint_oid = oldtrigger->tgconstraint;
756 existing_isInternal = oldtrigger->tgisinternal;
758 trigger_exists = true;
759 /* copy the tuple to use in CatalogTupleUpdate() */
760 tuple = heap_copytuple(tuple);
761 }
763 }
764
765 if (!trigger_exists)
766 {
767 /* Generate the OID for the new trigger. */
770 }
771 else
772 {
773 /*
774 * If OR REPLACE was specified, we'll replace the old trigger;
775 * otherwise complain about the duplicate name.
776 */
777 if (!stmt->replace)
780 errmsg("trigger \"%s\" for relation \"%s\" already exists",
781 stmt->trigname, RelationGetRelationName(rel))));
782
783 /*
784 * An internal trigger or a child trigger (isClone) cannot be replaced
785 * by a user-defined trigger. However, skip this test when
786 * in_partition, because then we're recursing from a partitioned table
787 * and the check was made at the parent level.
788 */
793 errmsg("trigger \"%s\" for relation \"%s\" is an internal or a child trigger",
794 stmt->trigname, RelationGetRelationName(rel))));
795
796 /*
797 * It is not allowed to replace with a constraint trigger; gram.y
798 * should have enforced this already.
799 */
800 Assert(!stmt->isconstraint);
801
802 /*
803 * It is not allowed to replace an existing constraint trigger,
804 * either. (The reason for these restrictions is partly that it seems
805 * difficult to deal with pending trigger events in such cases, and
806 * partly that the command might imply changing the constraint's
807 * properties as well, which doesn't seem nice.)
808 */
812 errmsg("trigger \"%s\" for relation \"%s\" is a constraint trigger",
813 stmt->trigname, RelationGetRelationName(rel))));
814 }
815
816 /*
817 * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
818 * corresponding pg_constraint entry.
819 */
820 if (stmt->isconstraint && !OidIsValid(constraintOid))
821 {
822 /* Internal callers should have made their own constraints */
827 stmt->deferrable,
828 stmt->initdeferred,
829 true, /* Is Enforced */
830 true,
831 InvalidOid, /* no parent */
832 RelationGetRelid(rel),
833 NULL, /* no conkey */
834 0,
835 0,
836 InvalidOid, /* no domain */
837 InvalidOid, /* no index */
838 InvalidOid, /* no foreign key */
839 NULL,
840 NULL,
841 NULL,
842 NULL,
843 0,
844 ' ',
845 ' ',
846 NULL,
847 0,
848 ' ',
849 NULL, /* no exclusion */
850 NULL, /* no check constraint */
851 NULL,
852 true, /* islocal */
853 0, /* inhcount */
854 true, /* noinherit */
855 false, /* conperiod */
856 isInternal); /* is_internal */
857 }
858
859 /*
860 * If trigger is internally generated, modify the provided trigger name to
861 * ensure uniqueness by appending the trigger OID. (Callers will usually
862 * supply a simple constant trigger name in these cases.)
863 */
864 if (isInternal)
865 {
867 "%s_%u", stmt->trigname, trigoid);
868 trigname = internaltrigname;
869 }
870 else
871 {
872 /* user-defined trigger; use the specified trigger name as-is */
873 trigname = stmt->trigname;
874 }
875
876 /*
877 * Build the new pg_trigger tuple.
878 */
879 memset(nulls, false, sizeof(nulls));
880
885 CStringGetDatum(trigname));
895
896 if (stmt->args)
897 {
898 ListCell *le;
899 char *args;
900 int16 nargs = list_length(stmt->args);
901 int len = 0;
902
903 foreach(le, stmt->args)
904 {
905 char *ar = strVal(lfirst(le));
906
907 len += strlen(ar) + 4;
908 for (; *ar; ar++)
909 {
910 if (*ar == '\\')
911 len++;
912 }
913 }
914 args = (char *) palloc(len + 1);
915 args[0] = '\0';
916 foreach(le, stmt->args)
917 {
918 char *s = strVal(lfirst(le));
919 char *d = args + strlen(args);
920
921 while (*s)
922 {
923 if (*s == '\\')
924 *d++ = '\\';
925 *d++ = *s++;
926 }
927 strcpy(d, "\\000");
928 }
931 CStringGetDatum(args));
932 }
933 else
934 {
937 CStringGetDatum(""));
938 }
939
940 /* build column number array if it's a column-specific trigger */
941 ncolumns = list_length(stmt->columns);
942 if (ncolumns == 0)
943 columns = NULL;
944 else
945 {
946 ListCell *cell;
947 int i = 0;
948
949 columns = (int16 *) palloc(ncolumns * sizeof(int16));
950 foreach(cell, stmt->columns)
951 {
952 char *name = strVal(lfirst(cell));
954 int j;
955
956 /* Lookup column name. System columns are not allowed */
957 attnum = attnameAttNum(rel, name, false);
961 errmsg("column \"%s\" of relation \"%s\" does not exist",
963
964 /* Check for duplicates */
965 for (j = i - 1; j >= 0; j--)
966 {
967 if (columns[j] == attnum)
970 errmsg("column \"%s\" specified more than once",
971 name)));
972 }
973
974 columns[i++] = attnum;
975 }
976 }
977 tgattr = buildint2vector(columns, ncolumns);
979
980 /* set tgqual if trigger has WHEN clause */
981 if (qual)
983 else
984 nulls[Anum_pg_trigger_tgqual - 1] = true;
985
986 if (oldtablename)
989 else
990 nulls[Anum_pg_trigger_tgoldtable - 1] = true;
991 if (newtablename)
994 else
995 nulls[Anum_pg_trigger_tgnewtable - 1] = true;
996
997 /*
998 * Insert or replace tuple in pg_trigger.
999 */
1000 if (!trigger_exists)
1001 {
1002 tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
1003 CatalogTupleInsert(tgrel, tuple);
1004 }
1005 else
1006 {
1008
1009 newtup = heap_form_tuple(tgrel->rd_att, values, nulls);
1012 }
1013
1014 heap_freetuple(tuple); /* free either original or new tuple */
1016
1020 if (oldtablename)
1022 if (newtablename)
1024
1025 /*
1026 * Update relation's pg_class entry; if necessary; and if not, send an SI
1027 * message to make other backends (and this one) rebuild relcache entries.
1028 */
1032 if (!HeapTupleIsValid(tuple))
1033 elog(ERROR, "cache lookup failed for relation %u",
1034 RelationGetRelid(rel));
1035 if (!((Form_pg_class) GETSTRUCT(tuple))->relhastriggers)
1036 {
1037 ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
1038
1039 CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
1040
1042 }
1043 else
1045
1046 heap_freetuple(tuple);
1048
1049 /*
1050 * If we're replacing a trigger, flush all the old dependencies before
1051 * recording new ones.
1052 */
1053 if (trigger_exists)
1055
1056 /*
1057 * Record dependencies for trigger. Always place a normal dependency on
1058 * the function.
1059 */
1060 myself.classId = TriggerRelationId;
1061 myself.objectId = trigoid;
1062 myself.objectSubId = 0;
1063
1065 referenced.objectId = funcoid;
1066 referenced.objectSubId = 0;
1068
1070 {
1071 /*
1072 * Internally-generated trigger for a constraint, so make it an
1073 * internal dependency of the constraint. We can skip depending on
1074 * the relation(s), as there'll be an indirect dependency via the
1075 * constraint.
1076 */
1078 referenced.objectId = constraintOid;
1079 referenced.objectSubId = 0;
1081 }
1082 else
1083 {
1084 /*
1085 * User CREATE TRIGGER, so place dependencies. We make trigger be
1086 * auto-dropped if its relation is dropped or if the FK relation is
1087 * dropped. (Auto drop is compatible with our pre-7.3 behavior.)
1088 */
1090 referenced.objectId = RelationGetRelid(rel);
1091 referenced.objectSubId = 0;
1093
1095 {
1097 referenced.objectId = constrrelid;
1098 referenced.objectSubId = 0;
1100 }
1101 /* Not possible to have an index dependency in this case */
1102 Assert(!OidIsValid(indexOid));
1103
1104 /*
1105 * If it's a user-specified constraint trigger, make the constraint
1106 * internally dependent on the trigger instead of vice versa.
1107 */
1109 {
1111 referenced.objectId = constraintOid;
1112 referenced.objectSubId = 0;
1114 }
1115
1116 /*
1117 * If it's a partition trigger, create the partition dependencies.
1118 */
1120 {
1125 }
1126 }
1127
1128 /* If column-specific trigger, add normal dependencies on columns */
1129 if (columns != NULL)
1130 {
1131 int i;
1132
1134 referenced.objectId = RelationGetRelid(rel);
1135 for (i = 0; i < ncolumns; i++)
1136 {
1137 referenced.objectSubId = columns[i];
1139 }
1140 }
1141
1142 /*
1143 * If it has a WHEN clause, add dependencies on objects mentioned in the
1144 * expression (eg, functions, as well as any columns used).
1145 */
1146 if (whenRtable != NIL)
1149
1150 /* Post creation hook for new trigger */
1152 isInternal);
1153
1154 /*
1155 * Lastly, create the trigger on child relations, if needed.
1156 */
1158 {
1159 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1160 int i;
1163
1165 "part trig clone",
1167
1168 /*
1169 * We don't currently expect to be called with a valid indexOid. If
1170 * that ever changes then we'll need to write code here to find the
1171 * corresponding child index.
1172 */
1173 Assert(!OidIsValid(indexOid));
1174
1176
1177 /* Iterate to create the trigger on each existing partition */
1178 for (i = 0; i < partdesc->nparts; i++)
1179 {
1182 Node *qual;
1183
1185
1186 /*
1187 * Initialize our fabricated parse node by copying the original
1188 * one, then resetting fields that we pass separately.
1189 */
1191 childStmt->funcname = NIL;
1192 childStmt->whenClause = NULL;
1193
1194 /* If there is a WHEN clause, create a modified copy of it */
1195 qual = copyObject(whenClause);
1196 qual = (Node *)
1198 childTbl, rel);
1199 qual = (Node *)
1201 childTbl, rel);
1202
1203 CreateTriggerFiringOn(childStmt, queryString,
1204 partdesc->oids[i], refRelOid,
1206 funcoid, trigoid, qual,
1208
1210
1212 }
1213
1216 }
1217
1218 /* Keep lock on target rel until end of xact */
1219 table_close(rel, NoLock);
1220
1221 return myself;
1222}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2672
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3902
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4105
#define InvalidAttrNumber
Definition attnum.h:23
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define CStringGetTextDatum(s)
Definition builtins.h:98
Datum byteain(PG_FUNCTION_ARGS)
Definition bytea.c:201
#define NameStr(name)
Definition c.h:894
#define OidIsValid(objectId)
Definition c.h:917
bool IsConflictLogTableClass(Form_pg_class reltuple)
Definition catalog.c:242
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:475
void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior)
@ DEPENDENCY_AUTO
Definition dependency.h:34
@ DEPENDENCY_INTERNAL
Definition dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition dependency.h:37
@ DEPENDENCY_NORMAL
Definition dependency.h:33
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:688
bool allowSystemTableMods
Definition globals.c:132
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:686
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition indexing.c:233
int2vector * buildint2vector(const int16 *int2s, int n)
Definition int.c:114
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition inval.c:1666
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:107
#define NoLock
Definition lockdefs.h:34
#define ShareRowExclusiveLock
Definition lockdefs.h:41
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_rel_name(Oid relid)
Definition lsyscache.c:2242
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2317
Oid get_func_rettype(Oid funcid)
Definition lsyscache.c:1969
Alias * makeAlias(const char *aliasname, List *colnames)
Definition makefuncs.c:438
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
Oid GetUserId(void)
Definition miscinit.c:470
Datum namein(PG_FUNCTION_ARGS)
Definition name.c:48
char * NameListToString(const List *names)
Definition namespace.c:3666
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition namespace.h:98
#define copyObject(obj)
Definition nodes.h:230
#define InvokeObjectPostCreateHookArg(classId, objectId, subId, is_internal)
ObjectType get_relkind_objtype(char relkind)
#define ObjectAddressSet(addr, class_id, object_id)
char * nodeToString(const void *obj)
Definition outfuncs.c:811
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
void assign_expr_collations(ParseState *pstate, Node *expr)
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
void free_parsestate(ParseState *pstate)
Definition parse_node.c:72
int parser_errposition(ParseState *pstate, int location)
Definition parse_node.c:106
ParseState * make_parsestate(ParseState *parentParseState)
Definition parse_node.c:39
@ EXPR_KIND_TRIGGER_WHEN
Definition parse_node.h:78
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, LOCKMODE lockmode, Alias *alias, bool inh, bool inFromCl)
int attnameAttNum(Relation rd, const char *attname, bool sysColOK)
@ OBJECT_FUNCTION
#define ACL_EXECUTE
Definition parsenodes.h:83
#define ACL_TRIGGER
Definition parsenodes.h:82
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition partdesc.c:71
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition partition.c:222
int16 attnum
int errdetail_relkind_not_supported(char relkind)
Definition pg_class.c:24
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
#define NAMEDATALEN
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
const void size_t len
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:51
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition pg_depend.c:314
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
bool has_superclass(Oid relationId)
#define lfirst_node(type, lc)
Definition pg_list.h:176
static int list_length(const List *l)
Definition pg_list.h:152
#define snprintf
Definition port.h:261
static Datum Int16GetDatum(int16 X)
Definition postgres.h:172
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
#define PointerGetDatum(X)
Definition postgres.h:354
static Datum CharGetDatum(char X)
Definition postgres.h:132
#define InvalidOid
#define PRS2_OLD_VARNO
Definition primnodes.h:251
#define PRS2_NEW_VARNO
Definition primnodes.h:252
#define RelationGetRelid(relation)
Definition rel.h:516
#define RelationGetDescr(relation)
Definition rel.h:542
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationGetNamespace(relation)
Definition rel.h:557
#define ERRCODE_DUPLICATE_OBJECT
Definition streamutil.c:30
ItemPointerData t_self
Definition htup.h:65
const char * p_sourcetext
Definition parse_node.h:214
List * p_rtable
Definition parse_node.h:215
Oid rd_id
Definition rel.h:113
Form_pg_class rd_rel
Definition rel.h:111
ParseLoc location
Definition primnodes.h:311
AttrNumber varattno
Definition primnodes.h:275
int varno
Definition primnodes.h:270
#define SearchSysCacheCopy1(cacheId, key1)
Definition syscache.h:91
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition table.c:83
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
#define strVal(v)
Definition value.h:82
List * pull_var_clause(Node *node, int flags)
Definition var.c:653
const char * name
void CommandCounterIncrement(void)
Definition xact.c:1130

References AccessShareLock, ACL_EXECUTE, ACL_TRIGGER, aclcheck_error(), ACLCHECK_OK, addNSItemToQuery(), addRangeTableEntryForRelation(), ALLOCSET_SMALL_SIZES, AllocSetContextCreate, allowSystemTableMods, Assert, assign_expr_collations(), attnameAttNum(), attnum, BoolGetDatum(), BTEqualStrategyNumber, buildint2vector(), byteain(), CacheInvalidateRelcacheByTuple(), CatalogTupleInsert(), CatalogTupleUpdate(), CharGetDatum(), CommandCounterIncrement(), copyObject, CreateConstraintEntry(), CreateTriggerFiringOn(), CStringGetDatum(), CStringGetTextDatum, CurrentMemoryContext, DatumGetPointer(), deleteDependencyRecordsFor(), DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, DirectFunctionCall1, elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errdetail_relkind_not_supported(), errhint(), errmsg, ERROR, EXPR_KIND_TRIGGER_WHEN, fb(), find_all_inheritors(), Form_pg_trigger, free_parsestate(), get_func_rettype(), get_rel_name(), get_rel_relkind(), get_relkind_objtype(), GetNewOidWithIndex(), GETSTRUCT(), GetUserId(), has_superclass(), heap_copytuple(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, Int16GetDatum(), InvalidAttrNumber, InvalidOid, InvokeObjectPostCreateHookArg, IsConflictLogTableClass(), IsSystemRelation(), j, len, lfirst, lfirst_node, list_free(), list_length(), Var::location, LockRelationOid(), LookupFuncName(), make_parsestate(), makeAlias(), map_partition_varattnos(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), name, NAMEDATALEN, namein(), NameListToString(), NameStr, NIL, nodeToString(), NoLock, PartitionDescData::nparts, object_aclcheck(), OBJECT_FUNCTION, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, ParseState::p_rtable, ParseState::p_sourcetext, palloc(), parser_errposition(), pfree(), pg_class_aclcheck(), PointerGetDatum, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pull_var_clause(), RangeVarGetRelid, RelationData::rd_id, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnExpr(), RelationGetDescr, RelationGetNamespace, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, ShareRowExclusiveLock, snprintf, stmt, strVal, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), table_openrv(), transformWhereClause(), TupleDescAttr(), values, Var::varattno, and Var::varno.

Referenced by CloneRowTriggersToPartition(), CreateTrigger(), and CreateTriggerFiringOn().

◆ EnableDisableTrigger()

void EnableDisableTrigger ( Relation  rel,
const char tgname,
Oid  tgparent,
char  fires_when,
bool  skip_system,
bool  recurse,
LOCKMODE  lockmode 
)
extern

Definition at line 1753 of file trigger.c.

1756{
1758 int nkeys;
1759 ScanKeyData keys[2];
1761 HeapTuple tuple;
1762 bool found;
1763 bool changed;
1764
1765 /* Scan the relevant entries in pg_triggers */
1767
1768 ScanKeyInit(&keys[0],
1772 if (tgname)
1773 {
1774 ScanKeyInit(&keys[1],
1777 CStringGetDatum(tgname));
1778 nkeys = 2;
1779 }
1780 else
1781 nkeys = 1;
1782
1784 NULL, nkeys, keys);
1785
1786 found = changed = false;
1787
1788 while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1789 {
1791
1792 if (OidIsValid(tgparent) && tgparent != oldtrig->tgparentid)
1793 continue;
1794
1795 if (oldtrig->tgisinternal)
1796 {
1797 /* system trigger ... ok to process? */
1798 if (skip_system)
1799 continue;
1800 if (!superuser())
1801 ereport(ERROR,
1803 errmsg("permission denied: \"%s\" is a system trigger",
1804 NameStr(oldtrig->tgname))));
1805 }
1806
1807 found = true;
1808
1809 if (oldtrig->tgenabled != fires_when)
1810 {
1811 /* need to change this one ... make a copy to scribble on */
1814
1815 newtrig->tgenabled = fires_when;
1816
1818
1820
1821 changed = true;
1822 }
1823
1824 /*
1825 * When altering FOR EACH ROW triggers on a partitioned table, do the
1826 * same on the partitions as well, unless ONLY is specified.
1827 *
1828 * Note that we recurse even if we didn't change the trigger above,
1829 * because the partitions' copy of the trigger may have a different
1830 * value of tgenabled than the parent's trigger and thus might need to
1831 * be changed.
1832 */
1833 if (recurse &&
1834 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
1835 (TRIGGER_FOR_ROW(oldtrig->tgtype)))
1836 {
1837 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1838 int i;
1839
1840 for (i = 0; i < partdesc->nparts; i++)
1841 {
1842 Relation part;
1843
1844 part = relation_open(partdesc->oids[i], lockmode);
1845 /* Match on child triggers' tgparentid, not their name */
1847 fires_when, skip_system, recurse,
1848 lockmode);
1849 table_close(part, NoLock); /* keep lock till commit */
1850 }
1851 }
1852
1854 oldtrig->oid, 0);
1855 }
1856
1858
1860
1861 if (tgname && !found)
1862 ereport(ERROR,
1864 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1865 tgname, RelationGetRelationName(rel))));
1866
1867 /*
1868 * If we changed anything, broadcast a SI inval message to force each
1869 * backend (including our own!) to rebuild relation's relcache entry.
1870 * Otherwise they will fail to apply the change promptly.
1871 */
1872 if (changed)
1874}
void CacheInvalidateRelcache(Relation relation)
Definition inval.c:1632
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
bool superuser(void)
Definition superuser.c:47
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition trigger.c:1753

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), CStringGetDatum(), EnableDisableTrigger(), ereport, errcode(), errmsg, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, i, InvokeObjectPostAlterHook, NameStr, NoLock, PartitionDescData::nparts, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, RelationData::rd_rel, relation_open(), RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), superuser(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecEnableDisableTrigger(), and EnableDisableTrigger().

◆ ExecARDeleteTriggers()

void ExecARDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TransitionCaptureState transition_capture,
bool  is_crosspart_update 
)
extern

Definition at line 2828 of file trigger.c.

2834{
2835 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2836
2837 if (relinfo->ri_FdwRoutine && transition_capture &&
2838 transition_capture->tcs_delete_old_table)
2839 {
2840 Assert(relinfo->ri_RootResultRelInfo);
2841 ereport(ERROR,
2843 errmsg("cannot collect transition tuples from child foreign tables")));
2844 }
2845
2846 if ((trigdesc && trigdesc->trig_delete_after_row) ||
2847 (transition_capture && transition_capture->tcs_delete_old_table))
2848 {
2850
2852 if (fdw_trigtuple == NULL)
2853 GetTupleForTrigger(estate,
2854 NULL,
2855 relinfo,
2856 tupleid,
2858 slot,
2859 false,
2860 NULL,
2861 NULL,
2862 NULL);
2863 else
2865
2868 true, slot, NULL, NIL, NULL,
2869 transition_capture,
2871 }
2872}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition execUtils.c:1230
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition itemptr.h:83
@ LockTupleExclusive
Definition lockoptions.h:59
bool trig_delete_after_row
Definition reltrigger.h:67
static bool GetTupleForTrigger(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, TupleTableSlot *oldslot, bool do_epq_recheck, TupleTableSlot **epqslot, TM_Result *tmresultp, TM_FailureData *tmfdp)
Definition trigger.c:3371
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldslot, TupleTableSlot *newslot, List *recheckIndexes, Bitmapset *modifiedCols, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition trigger.c:6262
#define TRIGGER_EVENT_DELETE
Definition trigger.h:95

References AfterTriggerSaveEvent(), Assert, ereport, errcode(), errmsg, ERROR, ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), fb(), GetTupleForTrigger(), HeapTupleIsValid, ItemPointerIsValid(), LockTupleExclusive, NIL, TransitionCaptureState::tcs_delete_old_table, TriggerDesc::trig_delete_after_row, and TRIGGER_EVENT_DELETE.

Referenced by ExecDeleteEpilogue(), and ExecSimpleRelationDelete().

◆ ExecARInsertTriggers()

void ExecARInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot,
List recheckIndexes,
TransitionCaptureState transition_capture 
)
extern

Definition at line 2570 of file trigger.c.

2573{
2574 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2575
2576 if (relinfo->ri_FdwRoutine && transition_capture &&
2577 transition_capture->tcs_insert_new_table)
2578 {
2579 Assert(relinfo->ri_RootResultRelInfo);
2580 ereport(ERROR,
2582 errmsg("cannot collect transition tuples from child foreign tables")));
2583 }
2584
2585 if ((trigdesc && trigdesc->trig_insert_after_row) ||
2586 (transition_capture && transition_capture->tcs_insert_new_table))
2589 true, NULL, slot,
2591 transition_capture,
2592 false);
2593}
bool trig_insert_after_row
Definition reltrigger.h:57
#define TRIGGER_EVENT_INSERT
Definition trigger.h:94

References AfterTriggerSaveEvent(), Assert, ereport, errcode(), errmsg, ERROR, fb(), TransitionCaptureState::tcs_insert_new_table, TriggerDesc::trig_insert_after_row, and TRIGGER_EVENT_INSERT.

Referenced by CopyFrom(), CopyMultiInsertBufferFlush(), ExecBatchInsert(), ExecInsert(), and ExecSimpleRelationInsert().

◆ ExecARUpdateTriggers()

void ExecARUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
ResultRelInfo src_partinfo,
ResultRelInfo dst_partinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot newslot,
List recheckIndexes,
TransitionCaptureState transition_capture,
bool  is_crosspart_update 
)
extern

Definition at line 3171 of file trigger.c.

3180{
3181 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3182
3183 if (relinfo->ri_FdwRoutine && transition_capture &&
3184 (transition_capture->tcs_update_old_table ||
3185 transition_capture->tcs_update_new_table))
3186 {
3187 Assert(relinfo->ri_RootResultRelInfo);
3188 ereport(ERROR,
3190 errmsg("cannot collect transition tuples from child foreign tables")));
3191 }
3192
3193 if ((trigdesc && trigdesc->trig_update_after_row) ||
3194 (transition_capture &&
3195 (transition_capture->tcs_update_old_table ||
3196 transition_capture->tcs_update_new_table)))
3197 {
3198 /*
3199 * Note: if the UPDATE is converted into a DELETE+INSERT as part of
3200 * update-partition-key operation, then this function is also called
3201 * separately for DELETE and INSERT to capture transition table rows.
3202 * In such case, either old tuple or new tuple can be NULL.
3203 */
3206
3209
3212
3214 GetTupleForTrigger(estate,
3215 NULL,
3216 tupsrc,
3217 tupleid,
3219 oldslot,
3220 false,
3221 NULL,
3222 NULL,
3223 NULL);
3224 else if (fdw_trigtuple != NULL)
3226 else
3228
3232 true,
3235 transition_capture,
3237 }
3238}
Bitmapset * ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition execUtils.c:1444
bool trig_update_after_row
Definition reltrigger.h:62
#define TRIGGER_EVENT_UPDATE
Definition trigger.h:96
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476

References AfterTriggerSaveEvent(), Assert, ereport, errcode(), errmsg, ERROR, ExecClearTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), fb(), GetTupleForTrigger(), ItemPointerIsValid(), LockTupleExclusive, TransitionCaptureState::tcs_update_new_table, TransitionCaptureState::tcs_update_old_table, TriggerDesc::trig_update_after_row, and TRIGGER_EVENT_UPDATE.

Referenced by ExecCrossPartitionUpdateForeignKey(), ExecDeleteEpilogue(), ExecInsert(), ExecSimpleRelationUpdate(), and ExecUpdateEpilogue().

◆ ExecASDeleteTriggers()

void ExecASDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)
extern

Definition at line 2708 of file trigger.c.

2710{
2711 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2712
2713 if (trigdesc && trigdesc->trig_delete_after_statement)
2716 false, NULL, NULL, NIL, NULL, transition_capture,
2717 false);
2718}
bool trig_delete_after_statement
Definition reltrigger.h:70

References AfterTriggerSaveEvent(), fb(), NIL, TriggerDesc::trig_delete_after_statement, and TRIGGER_EVENT_DELETE.

Referenced by fireASTriggers().

◆ ExecASInsertTriggers()

void ExecASInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)
extern

Definition at line 2479 of file trigger.c.

2481{
2482 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2483
2484 if (trigdesc && trigdesc->trig_insert_after_statement)
2487 false, NULL, NULL, NIL, NULL, transition_capture,
2488 false);
2489}
bool trig_insert_after_statement
Definition reltrigger.h:60

References AfterTriggerSaveEvent(), fb(), NIL, TriggerDesc::trig_insert_after_statement, and TRIGGER_EVENT_INSERT.

Referenced by CopyFrom(), and fireASTriggers().

◆ ExecASTruncateTriggers()

void ExecASTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)
extern

Definition at line 3354 of file trigger.c.

3355{
3356 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3357
3358 if (trigdesc && trigdesc->trig_truncate_after_statement)
3360 NULL, NULL,
3362 false, NULL, NULL, NIL, NULL, NULL,
3363 false);
3364}
bool trig_truncate_after_statement
Definition reltrigger.h:73
#define TRIGGER_EVENT_TRUNCATE
Definition trigger.h:97

References AfterTriggerSaveEvent(), fb(), NIL, TriggerDesc::trig_truncate_after_statement, and TRIGGER_EVENT_TRUNCATE.

Referenced by ExecuteTruncateGuts().

◆ ExecASUpdateTriggers()

void ExecASUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
TransitionCaptureState transition_capture 
)
extern

Definition at line 2980 of file trigger.c.

2982{
2983 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2984
2985 /* statement-level triggers operate on the parent table */
2986 Assert(relinfo->ri_RootResultRelInfo == NULL);
2987
2988 if (trigdesc && trigdesc->trig_update_after_statement)
2991 false, NULL, NULL, NIL,
2993 transition_capture,
2994 false);
2995}
bool trig_update_after_statement
Definition reltrigger.h:65

References AfterTriggerSaveEvent(), Assert, ExecGetAllUpdatedCols(), fb(), NIL, TriggerDesc::trig_update_after_statement, and TRIGGER_EVENT_UPDATE.

Referenced by fireASTriggers().

◆ ExecBRDeleteTriggers()

bool ExecBRDeleteTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot **  epqslot,
TM_Result tmresult,
TM_FailureData tmfd,
bool  is_merge_delete 
)
extern

Definition at line 2728 of file trigger.c.

2736{
2738 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2739 bool result = true;
2742 bool should_free = false;
2743 int i;
2744
2746 if (fdw_trigtuple == NULL)
2747 {
2749
2750 /*
2751 * Get a copy of the on-disk tuple we are planning to delete. In
2752 * general, if the tuple has been concurrently updated, we should
2753 * recheck it using EPQ. However, if this is a MERGE DELETE action,
2754 * we skip this EPQ recheck and leave it to the caller (it must do
2755 * additional rechecking, and might end up executing a different
2756 * action entirely).
2757 */
2758 if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2760 &epqslot_candidate, tmresult, tmfd))
2761 return false;
2762
2763 /*
2764 * If the tuple was concurrently updated and the caller of this
2765 * function requested for the updated tuple, skip the trigger
2766 * execution.
2767 */
2768 if (epqslot_candidate != NULL && epqslot != NULL)
2769 {
2771 return false;
2772 }
2773
2775 }
2776 else
2777 {
2779 ExecForceStoreHeapTuple(trigtuple, slot, false);
2780 }
2781
2786 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2787 for (i = 0; i < trigdesc->numtriggers; i++)
2788 {
2789 HeapTuple newtuple;
2790 Trigger *trigger = &trigdesc->triggers[i];
2791
2792 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2796 continue;
2797 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2798 NULL, slot, NULL))
2799 continue;
2800
2801 LocTriggerData.tg_trigslot = slot;
2802 LocTriggerData.tg_trigtuple = trigtuple;
2803 LocTriggerData.tg_trigger = trigger;
2805 i,
2806 relinfo->ri_TrigFunctions,
2807 relinfo->ri_TrigInstrument,
2808 GetPerTupleMemoryContext(estate));
2809 if (newtuple == NULL)
2810 {
2811 result = false; /* tell caller to suppress delete */
2812 break;
2813 }
2814 if (newtuple != trigtuple)
2815 heap_freetuple(newtuple);
2816 }
2817 if (should_free)
2819
2820 return result;
2821}
uint32 result
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
#define GetPerTupleMemoryContext(estate)
Definition executor.h:670
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition trigger.c:3509
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, TriggerInstrumentation *instr, MemoryContext per_tuple_context)
Definition trigger.c:2336
#define TRIGGER_EVENT_ROW
Definition trigger.h:100
#define TRIGGER_EVENT_BEFORE
Definition trigger.h:102

References Assert, ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), fb(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), HeapTupleIsValid, i, ItemPointerIsValid(), LockTupleExclusive, TriggerDesc::numtriggers, result, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_ROW, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by ExecDeletePrologue(), and ExecSimpleRelationDelete().

◆ ExecBRInsertTriggers()

bool ExecBRInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot 
)
extern

Definition at line 2492 of file trigger.c.

2494{
2495 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2496 HeapTuple newtuple = NULL;
2497 bool should_free;
2499 int i;
2500
2505 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2506 for (i = 0; i < trigdesc->numtriggers; i++)
2507 {
2508 Trigger *trigger = &trigdesc->triggers[i];
2509 HeapTuple oldtuple;
2510
2511 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2515 continue;
2516 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2517 NULL, NULL, slot))
2518 continue;
2519
2520 if (!newtuple)
2521 newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2522
2523 LocTriggerData.tg_trigslot = slot;
2524 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2525 LocTriggerData.tg_trigger = trigger;
2527 i,
2528 relinfo->ri_TrigFunctions,
2529 relinfo->ri_TrigInstrument,
2530 GetPerTupleMemoryContext(estate));
2531 if (newtuple == NULL)
2532 {
2533 if (should_free)
2534 heap_freetuple(oldtuple);
2535 return false; /* "do nothing" */
2536 }
2537 else if (newtuple != oldtuple)
2538 {
2539 newtuple = check_modified_virtual_generated(RelationGetDescr(relinfo->ri_RelationDesc), newtuple);
2540
2541 ExecForceStoreHeapTuple(newtuple, slot, false);
2542
2543 /*
2544 * After a tuple in a partition goes through a trigger, the user
2545 * could have changed the partition key enough that the tuple no
2546 * longer fits the partition. Verify that.
2547 */
2548 if (trigger->tgisclone &&
2549 !ExecPartitionCheck(relinfo, slot, estate, false))
2550 ereport(ERROR,
2552 errmsg("moving row to another partition during a BEFORE FOR EACH ROW trigger is not supported"),
2553 errdetail("Before executing trigger \"%s\", the row was to be in partition \"%s.%s\".",
2554 trigger->tgname,
2556 RelationGetRelationName(relinfo->ri_RelationDesc))));
2557
2558 if (should_free)
2559 heap_freetuple(oldtuple);
2560
2561 /* signal tuple should be re-fetched if used */
2562 newtuple = NULL;
2563 }
2564 }
2565
2566 return true;
2567}
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition execMain.c:1922
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
NodeTag type
Definition trigger.h:33
static HeapTuple check_modified_virtual_generated(TupleDesc tupdesc, HeapTuple tuple)
Definition trigger.c:6828

References check_modified_virtual_generated(), ereport, errcode(), errdetail(), errmsg, ERROR, ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecPartitionCheck(), fb(), get_namespace_name(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, RelationGetDescr, RelationGetNamespace, RelationGetRelationName, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by CopyFrom(), ExecInsert(), and ExecSimpleRelationInsert().

◆ ExecBRUpdateTriggers()

bool ExecBRUpdateTriggers ( EState estate,
EPQState epqstate,
ResultRelInfo relinfo,
ItemPointer  tupleid,
HeapTuple  fdw_trigtuple,
TupleTableSlot newslot,
TM_Result tmresult,
TM_FailureData tmfd,
bool  is_merge_update 
)
extern

Definition at line 2998 of file trigger.c.

3006{
3007 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3009 HeapTuple newtuple = NULL;
3011 bool should_free_trig = false;
3012 bool should_free_new = false;
3014 int i;
3015 Bitmapset *updatedCols;
3016 LockTupleMode lockmode;
3017
3018 /* Determine lock mode to use */
3019 lockmode = ExecUpdateLockMode(estate, relinfo);
3020
3022 if (fdw_trigtuple == NULL)
3023 {
3025
3026 /*
3027 * Get a copy of the on-disk tuple we are planning to update. In
3028 * general, if the tuple has been concurrently updated, we should
3029 * recheck it using EPQ. However, if this is a MERGE UPDATE action,
3030 * we skip this EPQ recheck and leave it to the caller (it must do
3031 * additional rechecking, and might end up executing a different
3032 * action entirely).
3033 */
3034 if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
3035 lockmode, oldslot, !is_merge_update,
3036 &epqslot_candidate, tmresult, tmfd))
3037 return false; /* cancel the update action */
3038
3039 /*
3040 * In READ COMMITTED isolation level it's possible that target tuple
3041 * was changed due to concurrent update. In that case we have a raw
3042 * subplan output tuple in epqslot_candidate, and need to form a new
3043 * insertable tuple using ExecGetUpdateNewTuple to replace the one we
3044 * received in newslot. Neither we nor our callers have any further
3045 * interest in the passed-in tuple, so it's okay to overwrite newslot
3046 * with the newer data.
3047 */
3048 if (epqslot_candidate != NULL)
3049 {
3051
3053 oldslot);
3054
3055 /*
3056 * Typically, the caller's newslot was also generated by
3057 * ExecGetUpdateNewTuple, so that epqslot_clean will be the same
3058 * slot and copying is not needed. But do the right thing if it
3059 * isn't.
3060 */
3063
3064 /*
3065 * At this point newslot contains a virtual tuple that may
3066 * reference some fields of oldslot's tuple in some disk buffer.
3067 * If that tuple is in a different page than the original target
3068 * tuple, then our only pin on that buffer is oldslot's, and we're
3069 * about to release it. Hence we'd better materialize newslot to
3070 * ensure it doesn't contain references into an unpinned buffer.
3071 * (We'd materialize it below anyway, but too late for safety.)
3072 */
3074 }
3075
3076 /*
3077 * Here we convert oldslot to a materialized slot holding trigtuple.
3078 * Neither slot passed to the triggers will hold any buffer pin.
3079 */
3081 }
3082 else
3083 {
3084 /* Put the FDW-supplied tuple into oldslot to unify the cases */
3087 }
3088
3093 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3094 updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
3095 LocTriggerData.tg_updatedcols = updatedCols;
3096 for (i = 0; i < trigdesc->numtriggers; i++)
3097 {
3098 Trigger *trigger = &trigdesc->triggers[i];
3099 HeapTuple oldtuple;
3100
3101 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3105 continue;
3106 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3107 updatedCols, oldslot, newslot))
3108 continue;
3109
3110 if (!newtuple)
3112
3113 LocTriggerData.tg_trigslot = oldslot;
3114 LocTriggerData.tg_trigtuple = trigtuple;
3115 LocTriggerData.tg_newtuple = oldtuple = newtuple;
3116 LocTriggerData.tg_newslot = newslot;
3117 LocTriggerData.tg_trigger = trigger;
3119 i,
3120 relinfo->ri_TrigFunctions,
3121 relinfo->ri_TrigInstrument,
3122 GetPerTupleMemoryContext(estate));
3123
3124 if (newtuple == NULL)
3125 {
3126 if (should_free_trig)
3128 if (should_free_new)
3129 heap_freetuple(oldtuple);
3130 return false; /* "do nothing" */
3131 }
3132 else if (newtuple != oldtuple)
3133 {
3134 newtuple = check_modified_virtual_generated(RelationGetDescr(relinfo->ri_RelationDesc), newtuple);
3135
3136 ExecForceStoreHeapTuple(newtuple, newslot, false);
3137
3138 /*
3139 * If the tuple returned by the trigger / being stored, is the old
3140 * row version, and the heap tuple passed to the trigger was
3141 * allocated locally, materialize the slot. Otherwise we might
3142 * free it while still referenced by the slot.
3143 */
3144 if (should_free_trig && newtuple == trigtuple)
3146
3147 if (should_free_new)
3148 heap_freetuple(oldtuple);
3149
3150 /* signal tuple should be re-fetched if used */
3151 newtuple = NULL;
3152 }
3153 }
3154 if (should_free_trig)
3156
3157 return true;
3158}
#define unlikely(x)
Definition c.h:497
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition execMain.c:2596
LockTupleMode
Definition lockoptions.h:51
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:544
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition tuptable.h:495

References Assert, check_modified_virtual_generated(), ExecCallTriggerFunc(), ExecCopySlot(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetAllUpdatedCols(), ExecGetTriggerOldSlot(), ExecGetUpdateNewTuple(), ExecMaterializeSlot(), ExecUpdateLockMode(), fb(), GetPerTupleMemoryContext, GetTupleForTrigger(), heap_freetuple(), HeapTupleIsValid, i, ItemPointerIsValid(), TriggerDesc::numtriggers, RelationGetDescr, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and unlikely.

Referenced by ExecSimpleRelationUpdate(), and ExecUpdatePrologue().

◆ ExecBSDeleteTriggers()

void ExecBSDeleteTriggers ( EState estate,
ResultRelInfo relinfo 
)
extern

Definition at line 2657 of file trigger.c.

2658{
2659 TriggerDesc *trigdesc;
2660 int i;
2662
2663 trigdesc = relinfo->ri_TrigDesc;
2664
2665 if (trigdesc == NULL)
2666 return;
2667 if (!trigdesc->trig_delete_before_statement)
2668 return;
2669
2670 /* no-op if we already fired BS triggers in this context */
2672 CMD_DELETE))
2673 return;
2674
2678 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2679 for (i = 0; i < trigdesc->numtriggers; i++)
2680 {
2681 Trigger *trigger = &trigdesc->triggers[i];
2682 HeapTuple newtuple;
2683
2684 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2688 continue;
2689 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2690 NULL, NULL, NULL))
2691 continue;
2692
2693 LocTriggerData.tg_trigger = trigger;
2695 i,
2696 relinfo->ri_TrigFunctions,
2697 relinfo->ri_TrigInstrument,
2698 GetPerTupleMemoryContext(estate));
2699
2700 if (newtuple)
2701 ereport(ERROR,
2703 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2704 }
2705}
@ CMD_DELETE
Definition nodes.h:276
bool trig_delete_before_statement
Definition reltrigger.h:69
static bool before_stmt_triggers_fired(Oid relid, CmdType cmdType)
Definition trigger.c:6677

References before_stmt_triggers_fired(), CMD_DELETE, ereport, errcode(), errmsg, ERROR, ExecCallTriggerFunc(), fb(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, TriggerDesc::trig_delete_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_DELETE, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by fireBSTriggers().

◆ ExecBSInsertTriggers()

void ExecBSInsertTriggers ( EState estate,
ResultRelInfo relinfo 
)
extern

Definition at line 2428 of file trigger.c.

2429{
2430 TriggerDesc *trigdesc;
2431 int i;
2433
2434 trigdesc = relinfo->ri_TrigDesc;
2435
2436 if (trigdesc == NULL)
2437 return;
2438 if (!trigdesc->trig_insert_before_statement)
2439 return;
2440
2441 /* no-op if we already fired BS triggers in this context */
2443 CMD_INSERT))
2444 return;
2445
2449 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2450 for (i = 0; i < trigdesc->numtriggers; i++)
2451 {
2452 Trigger *trigger = &trigdesc->triggers[i];
2453 HeapTuple newtuple;
2454
2455 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2459 continue;
2460 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2461 NULL, NULL, NULL))
2462 continue;
2463
2464 LocTriggerData.tg_trigger = trigger;
2466 i,
2467 relinfo->ri_TrigFunctions,
2468 relinfo->ri_TrigInstrument,
2469 GetPerTupleMemoryContext(estate));
2470
2471 if (newtuple)
2472 ereport(ERROR,
2474 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2475 }
2476}
@ CMD_INSERT
Definition nodes.h:275
bool trig_insert_before_statement
Definition reltrigger.h:59

References before_stmt_triggers_fired(), CMD_INSERT, ereport, errcode(), errmsg, ERROR, ExecCallTriggerFunc(), fb(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, TriggerDesc::trig_insert_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_INSERT, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by CopyFrom(), and fireBSTriggers().

◆ ExecBSTruncateTriggers()

void ExecBSTruncateTriggers ( EState estate,
ResultRelInfo relinfo 
)
extern

Definition at line 3307 of file trigger.c.

3308{
3309 TriggerDesc *trigdesc;
3310 int i;
3312
3313 trigdesc = relinfo->ri_TrigDesc;
3314
3315 if (trigdesc == NULL)
3316 return;
3317 if (!trigdesc->trig_truncate_before_statement)
3318 return;
3319
3323 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3324
3325 for (i = 0; i < trigdesc->numtriggers; i++)
3326 {
3327 Trigger *trigger = &trigdesc->triggers[i];
3328 HeapTuple newtuple;
3329
3330 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3334 continue;
3335 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3336 NULL, NULL, NULL))
3337 continue;
3338
3339 LocTriggerData.tg_trigger = trigger;
3341 i,
3342 relinfo->ri_TrigFunctions,
3343 relinfo->ri_TrigInstrument,
3344 GetPerTupleMemoryContext(estate));
3345
3346 if (newtuple)
3347 ereport(ERROR,
3349 errmsg("BEFORE STATEMENT trigger cannot return a value")));
3350 }
3351}
bool trig_truncate_before_statement
Definition reltrigger.h:72

References ereport, errcode(), errmsg, ERROR, ExecCallTriggerFunc(), fb(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, TriggerDesc::trig_truncate_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_TRUNCATE, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by ExecuteTruncateGuts().

◆ ExecBSUpdateTriggers()

void ExecBSUpdateTriggers ( EState estate,
ResultRelInfo relinfo 
)
extern

Definition at line 2922 of file trigger.c.

2923{
2924 TriggerDesc *trigdesc;
2925 int i;
2927 Bitmapset *updatedCols;
2928
2929 trigdesc = relinfo->ri_TrigDesc;
2930
2931 if (trigdesc == NULL)
2932 return;
2933 if (!trigdesc->trig_update_before_statement)
2934 return;
2935
2936 /* no-op if we already fired BS triggers in this context */
2938 CMD_UPDATE))
2939 return;
2940
2941 /* statement-level triggers operate on the parent table */
2942 Assert(relinfo->ri_RootResultRelInfo == NULL);
2943
2944 updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
2945
2949 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2950 LocTriggerData.tg_updatedcols = updatedCols;
2951 for (i = 0; i < trigdesc->numtriggers; i++)
2952 {
2953 Trigger *trigger = &trigdesc->triggers[i];
2954 HeapTuple newtuple;
2955
2956 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2960 continue;
2961 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2962 updatedCols, NULL, NULL))
2963 continue;
2964
2965 LocTriggerData.tg_trigger = trigger;
2967 i,
2968 relinfo->ri_TrigFunctions,
2969 relinfo->ri_TrigInstrument,
2970 GetPerTupleMemoryContext(estate));
2971
2972 if (newtuple)
2973 ereport(ERROR,
2975 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2976 }
2977}
@ CMD_UPDATE
Definition nodes.h:274
bool trig_update_before_statement
Definition reltrigger.h:64

References Assert, before_stmt_triggers_fired(), CMD_UPDATE, ereport, errcode(), errmsg, ERROR, ExecCallTriggerFunc(), ExecGetAllUpdatedCols(), fb(), GetPerTupleMemoryContext, i, TriggerDesc::numtriggers, RelationGetRelid, TriggerDesc::trig_update_before_statement, TRIGGER_EVENT_BEFORE, TRIGGER_EVENT_UPDATE, TriggerEnabled(), and TriggerDesc::triggers.

Referenced by fireBSTriggers().

◆ ExecIRDeleteTriggers()

bool ExecIRDeleteTriggers ( EState estate,
ResultRelInfo relinfo,
HeapTuple  trigtuple 
)
extern

Definition at line 2875 of file trigger.c.

2877{
2878 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2881 int i;
2882
2887 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2888
2889 ExecForceStoreHeapTuple(trigtuple, slot, false);
2890
2891 for (i = 0; i < trigdesc->numtriggers; i++)
2892 {
2894 Trigger *trigger = &trigdesc->triggers[i];
2895
2896 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2900 continue;
2901 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2902 NULL, slot, NULL))
2903 continue;
2904
2905 LocTriggerData.tg_trigslot = slot;
2906 LocTriggerData.tg_trigtuple = trigtuple;
2907 LocTriggerData.tg_trigger = trigger;
2909 i,
2910 relinfo->ri_TrigFunctions,
2911 relinfo->ri_TrigInstrument,
2912 GetPerTupleMemoryContext(estate));
2913 if (rettuple == NULL)
2914 return false; /* Delete was suppressed */
2915 if (rettuple != trigtuple)
2917 }
2918 return true;
2919}
#define TRIGGER_EVENT_INSTEAD
Definition trigger.h:104

References ExecCallTriggerFunc(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), fb(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, TRIGGER_EVENT_DELETE, TRIGGER_EVENT_INSTEAD, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by ExecDelete(), and ExecMergeMatched().

◆ ExecIRInsertTriggers()

bool ExecIRInsertTriggers ( EState estate,
ResultRelInfo relinfo,
TupleTableSlot slot 
)
extern

Definition at line 2596 of file trigger.c.

2598{
2599 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2600 HeapTuple newtuple = NULL;
2601 bool should_free;
2603 int i;
2604
2609 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2610 for (i = 0; i < trigdesc->numtriggers; i++)
2611 {
2612 Trigger *trigger = &trigdesc->triggers[i];
2613 HeapTuple oldtuple;
2614
2615 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2619 continue;
2620 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2621 NULL, NULL, slot))
2622 continue;
2623
2624 if (!newtuple)
2625 newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2626
2627 LocTriggerData.tg_trigslot = slot;
2628 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2629 LocTriggerData.tg_trigger = trigger;
2631 i,
2632 relinfo->ri_TrigFunctions,
2633 relinfo->ri_TrigInstrument,
2634 GetPerTupleMemoryContext(estate));
2635 if (newtuple == NULL)
2636 {
2637 if (should_free)
2638 heap_freetuple(oldtuple);
2639 return false; /* "do nothing" */
2640 }
2641 else if (newtuple != oldtuple)
2642 {
2643 ExecForceStoreHeapTuple(newtuple, slot, false);
2644
2645 if (should_free)
2646 heap_freetuple(oldtuple);
2647
2648 /* signal tuple should be re-fetched if used */
2649 newtuple = NULL;
2650 }
2651 }
2652
2653 return true;
2654}

References ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), fb(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_INSTEAD, TRIGGER_EVENT_ROW, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by CopyFrom(), and ExecInsert().

◆ ExecIRUpdateTriggers()

bool ExecIRUpdateTriggers ( EState estate,
ResultRelInfo relinfo,
HeapTuple  trigtuple,
TupleTableSlot newslot 
)
extern

Definition at line 3241 of file trigger.c.

3243{
3244 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3246 HeapTuple newtuple = NULL;
3247 bool should_free;
3249 int i;
3250
3255 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3256
3258
3259 for (i = 0; i < trigdesc->numtriggers; i++)
3260 {
3261 Trigger *trigger = &trigdesc->triggers[i];
3262 HeapTuple oldtuple;
3263
3264 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3268 continue;
3269 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3270 NULL, oldslot, newslot))
3271 continue;
3272
3273 if (!newtuple)
3274 newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free);
3275
3276 LocTriggerData.tg_trigslot = oldslot;
3277 LocTriggerData.tg_trigtuple = trigtuple;
3278 LocTriggerData.tg_newslot = newslot;
3279 LocTriggerData.tg_newtuple = oldtuple = newtuple;
3280
3281 LocTriggerData.tg_trigger = trigger;
3283 i,
3284 relinfo->ri_TrigFunctions,
3285 relinfo->ri_TrigInstrument,
3286 GetPerTupleMemoryContext(estate));
3287 if (newtuple == NULL)
3288 {
3289 return false; /* "do nothing" */
3290 }
3291 else if (newtuple != oldtuple)
3292 {
3293 ExecForceStoreHeapTuple(newtuple, newslot, false);
3294
3295 if (should_free)
3296 heap_freetuple(oldtuple);
3297
3298 /* signal tuple should be re-fetched if used */
3299 newtuple = NULL;
3300 }
3301 }
3302
3303 return true;
3304}

References ExecCallTriggerFunc(), ExecFetchSlotHeapTuple(), ExecForceStoreHeapTuple(), ExecGetTriggerOldSlot(), fb(), GetPerTupleMemoryContext, heap_freetuple(), i, TriggerDesc::numtriggers, TRIGGER_EVENT_INSTEAD, TRIGGER_EVENT_ROW, TRIGGER_EVENT_UPDATE, TriggerEnabled(), TriggerDesc::triggers, and TriggerData::type.

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ FindTriggerIncompatibleWithInheritance()

const char * FindTriggerIncompatibleWithInheritance ( TriggerDesc trigdesc)
extern

Definition at line 2304 of file trigger.c.

2305{
2306 if (trigdesc != NULL)
2307 {
2308 int i;
2309
2310 for (i = 0; i < trigdesc->numtriggers; ++i)
2311 {
2312 Trigger *trigger = &trigdesc->triggers[i];
2313
2314 if (!TRIGGER_FOR_ROW(trigger->tgtype))
2315 continue;
2316 if (trigger->tgoldtable != NULL || trigger->tgnewtable != NULL)
2317 return trigger->tgname;
2318 }
2319 }
2320
2321 return NULL;
2322}
char * tgname
Definition reltrigger.h:27

References fb(), i, TriggerDesc::numtriggers, Trigger::tgname, and TriggerDesc::triggers.

Referenced by ATExecAddInherit(), and ATExecAttachPartition().

◆ FreeTriggerDesc()

void FreeTriggerDesc ( TriggerDesc trigdesc)
extern

Definition at line 2172 of file trigger.c.

2173{
2175 int i;
2176
2177 if (trigdesc == NULL)
2178 return;
2179
2180 trigger = trigdesc->triggers;
2181 for (i = 0; i < trigdesc->numtriggers; i++)
2182 {
2183 pfree(trigger->tgname);
2184 if (trigger->tgnattr > 0)
2185 pfree(trigger->tgattr);
2186 if (trigger->tgnargs > 0)
2187 {
2188 while (--(trigger->tgnargs) >= 0)
2189 pfree(trigger->tgargs[trigger->tgnargs]);
2190 pfree(trigger->tgargs);
2191 }
2192 if (trigger->tgqual)
2193 pfree(trigger->tgqual);
2194 if (trigger->tgoldtable)
2195 pfree(trigger->tgoldtable);
2196 if (trigger->tgnewtable)
2197 pfree(trigger->tgnewtable);
2198 trigger++;
2199 }
2200 pfree(trigdesc->triggers);
2201 pfree(trigdesc);
2202}

References fb(), i, TriggerDesc::numtriggers, pfree(), and TriggerDesc::triggers.

Referenced by RelationBuildTriggers(), and RelationDestroyRelation().

◆ get_trigger_oid()

Oid get_trigger_oid ( Oid  relid,
const char trigname,
bool  missing_ok 
)
extern

Definition at line 1384 of file trigger.c.

1385{
1387 ScanKeyData skey[2];
1389 HeapTuple tup;
1390 Oid oid;
1391
1392 /*
1393 * Find the trigger, verify permissions, set up object address
1394 */
1396
1397 ScanKeyInit(&skey[0],
1400 ObjectIdGetDatum(relid));
1401 ScanKeyInit(&skey[1],
1404 CStringGetDatum(trigname));
1405
1407 NULL, 2, skey);
1408
1410
1411 if (!HeapTupleIsValid(tup))
1412 {
1413 if (!missing_ok)
1414 ereport(ERROR,
1416 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1417 trigname, get_rel_name(relid))));
1418 oid = InvalidOid;
1419 }
1420 else
1421 {
1422 oid = ((Form_pg_trigger) GETSTRUCT(tup))->oid;
1423 }
1424
1427 return oid;
1428}

References AccessShareLock, BTEqualStrategyNumber, CStringGetDatum(), ereport, errcode(), errmsg, ERROR, fb(), Form_pg_trigger, get_rel_name(), GETSTRUCT(), HeapTupleIsValid, InvalidOid, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by get_object_address_relobject().

◆ MakeTransitionCaptureState()

TransitionCaptureState * MakeTransitionCaptureState ( TriggerDesc trigdesc,
Oid  relid,
CmdType  cmdType 
)
extern

Definition at line 5005 of file trigger.c.

5006{
5008 bool need_old_upd,
5017
5018 if (trigdesc == NULL)
5019 return NULL;
5020
5021 /* Detect which table(s) we need. */
5022 switch (cmdType)
5023 {
5024 case CMD_INSERT:
5027 break;
5028 case CMD_UPDATE:
5031 need_old_del = need_new_ins = false;
5032 break;
5033 case CMD_DELETE:
5036 break;
5037 case CMD_MERGE:
5042 break;
5043 default:
5044 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
5045 /* keep compiler quiet */
5047 break;
5048 }
5050 return NULL;
5051
5052 /* Check state, like AfterTriggerSaveEvent. */
5053 if (afterTriggers.query_depth < 0)
5054 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
5055
5056 /* Be sure we have enough space to record events at this query depth. */
5059
5060 /*
5061 * Find or create AfterTriggersTableData struct(s) to hold the
5062 * tuplestore(s). If there's a matching struct but it's marked closed,
5063 * ignore it; we need a newer one.
5064 *
5065 * Note: MERGE must use the same AfterTriggersTableData structs as INSERT,
5066 * UPDATE, and DELETE, so that any MERGE'd tuples are added to the same
5067 * tuplestores as tuples from any INSERT, UPDATE, or DELETE commands
5068 * running in the same top-level command (e.g., in a writable CTE).
5069 *
5070 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
5071 * allocated in the current (sub)transaction's CurTransactionContext, and
5072 * the tuplestores are managed by the (sub)transaction's resource owner.
5073 * This is sufficient lifespan because we do not allow triggers using
5074 * transition tables to be deferrable; they will be fired during
5075 * AfterTriggerEndQuery, after which it's okay to delete the data.
5076 */
5077 if (need_new_ins)
5079 else
5080 ins_table = NULL;
5081
5084 else
5085 upd_table = NULL;
5086
5087 if (need_old_del)
5089 else
5090 del_table = NULL;
5091
5092 /* Now create required tuplestore(s), if we don't have them already. */
5096
5097 if (need_old_upd && upd_table->old_tuplestore == NULL)
5098 upd_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5099 if (need_new_upd && upd_table->new_tuplestore == NULL)
5100 upd_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5101 if (need_old_del && del_table->old_tuplestore == NULL)
5102 del_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5103 if (need_new_ins && ins_table->new_tuplestore == NULL)
5104 ins_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5105
5108
5109 /* Now build the TransitionCaptureState struct, in caller's context */
5111 state->tcs_delete_old_table = need_old_del;
5112 state->tcs_update_old_table = need_old_upd;
5113 state->tcs_update_new_table = need_new_upd;
5114 state->tcs_insert_new_table = need_new_ins;
5115 state->tcs_insert_private = ins_table;
5116 state->tcs_update_private = upd_table;
5117 state->tcs_delete_private = del_table;
5118
5119 return state;
5120}
#define palloc0_object(type)
Definition fe_memutils.h:90
int work_mem
Definition globals.c:133
MemoryContext CurTransactionContext
Definition mcxt.c:173
@ CMD_MERGE
Definition nodes.h:277
ResourceOwner CurrentResourceOwner
Definition resowner.c:173
ResourceOwner CurTransactionResourceOwner
Definition resowner.c:174
bool trig_update_new_table
Definition reltrigger.h:77
bool trig_insert_new_table
Definition reltrigger.h:75
bool trig_delete_old_table
Definition reltrigger.h:78
bool trig_update_old_table
Definition reltrigger.h:76
static AfterTriggersTableData * GetAfterTriggersTableData(Oid relid, CmdType cmdType)
Definition trigger.c:4914
static void AfterTriggerEnlargeQueryState(void)
Definition trigger.c:5728
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition tuplestore.c:331

References AfterTriggerEnlargeQueryState(), afterTriggers, CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, CurrentResourceOwner, CurTransactionContext, CurTransactionResourceOwner, elog, ERROR, fb(), GetAfterTriggersTableData(), AfterTriggersData::maxquerydepth, MemoryContextSwitchTo(), palloc0_object, AfterTriggersData::query_depth, TriggerDesc::trig_delete_old_table, TriggerDesc::trig_insert_new_table, TriggerDesc::trig_update_new_table, TriggerDesc::trig_update_old_table, tuplestore_begin_heap(), and work_mem.

Referenced by CopyFrom(), and ExecSetupTransitionCaptureState().

◆ RegisterAfterTriggerBatchCallback()

void RegisterAfterTriggerBatchCallback ( AfterTriggerBatchCallback  callback,
void arg 
)
extern

Definition at line 6863 of file trigger.c.

6865{
6868
6869 /*
6870 * Allocate in TopTransactionContext so the item survives for the duration
6871 * of the batch, which may span multiple trigger invocations.
6872 *
6873 * Must be called while afterTriggers is active; callbacks registered
6874 * outside a trigger-firing context would never fire.
6875 */
6879 item = palloc(sizeof(AfterTriggerCallbackItem));
6880 item->callback = callback;
6881 item->arg = arg;
6882 if (afterTriggers.query_depth >= 0)
6883 {
6886
6887 qs->batch_callbacks = lappend(qs->batch_callbacks, item);
6888 }
6889 else
6893}
List * lappend(List *list, void *datum)
Definition list.c:339
AfterTriggerBatchCallback callback
Definition trigger.c:3973

References afterTriggers, AfterTriggerCallbackItem::arg, arg, Assert, AfterTriggersData::batch_callbacks, AfterTriggersQueryData::batch_callbacks, AfterTriggerCallbackItem::callback, callback(), fb(), AfterTriggersData::firing_batch_callbacks, AfterTriggersData::firing_depth, lappend(), MemoryContextSwitchTo(), palloc(), AfterTriggersData::query_depth, AfterTriggersData::query_stack, and TopTransactionContext.

Referenced by ri_FastPathGetEntry().

◆ RelationBuildTriggers()

void RelationBuildTriggers ( Relation  relation)
extern

Definition at line 1888 of file trigger.c.

1889{
1890 TriggerDesc *trigdesc;
1891 int numtrigs;
1892 int maxtrigs;
1893 Trigger *triggers;
1897 HeapTuple htup;
1899 int i;
1900
1901 /*
1902 * Allocate a working array to hold the triggers (the array is extended if
1903 * necessary)
1904 */
1905 maxtrigs = 16;
1906 triggers = (Trigger *) palloc(maxtrigs * sizeof(Trigger));
1907 numtrigs = 0;
1908
1909 /*
1910 * Note: since we scan the triggers using TriggerRelidNameIndexId, we will
1911 * be reading the triggers in name order, except possibly during
1912 * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn
1913 * ensures that triggers will be fired in name order.
1914 */
1919
1922 NULL, 1, &skey);
1923
1925 {
1927 Trigger *build;
1928 Datum datum;
1929 bool isnull;
1930
1931 if (numtrigs >= maxtrigs)
1932 {
1933 maxtrigs *= 2;
1934 triggers = (Trigger *) repalloc(triggers, maxtrigs * sizeof(Trigger));
1935 }
1936 build = &(triggers[numtrigs]);
1937
1938 build->tgoid = pg_trigger->oid;
1940 NameGetDatum(&pg_trigger->tgname)));
1941 build->tgfoid = pg_trigger->tgfoid;
1942 build->tgtype = pg_trigger->tgtype;
1943 build->tgenabled = pg_trigger->tgenabled;
1944 build->tgisinternal = pg_trigger->tgisinternal;
1945 build->tgisclone = OidIsValid(pg_trigger->tgparentid);
1946 build->tgconstrrelid = pg_trigger->tgconstrrelid;
1947 build->tgconstrindid = pg_trigger->tgconstrindid;
1948 build->tgconstraint = pg_trigger->tgconstraint;
1949 build->tgdeferrable = pg_trigger->tgdeferrable;
1950 build->tginitdeferred = pg_trigger->tginitdeferred;
1951 build->tgnargs = pg_trigger->tgnargs;
1952 /* tgattr is first var-width field, so OK to access directly */
1953 build->tgnattr = pg_trigger->tgattr.dim1;
1954 if (build->tgnattr > 0)
1955 {
1956 build->tgattr = (int16 *) palloc(build->tgnattr * sizeof(int16));
1957 memcpy(build->tgattr, &(pg_trigger->tgattr.values),
1958 build->tgnattr * sizeof(int16));
1959 }
1960 else
1961 build->tgattr = NULL;
1962 if (build->tgnargs > 0)
1963 {
1964 bytea *val;
1965 char *p;
1966
1969 tgrel->rd_att, &isnull));
1970 if (isnull)
1971 elog(ERROR, "tgargs is null in trigger for relation \"%s\"",
1972 RelationGetRelationName(relation));
1973 p = (char *) VARDATA_ANY(val);
1974 build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
1975 for (i = 0; i < build->tgnargs; i++)
1976 {
1977 build->tgargs[i] = pstrdup(p);
1978 p += strlen(p) + 1;
1979 }
1980 }
1981 else
1982 build->tgargs = NULL;
1983
1985 tgrel->rd_att, &isnull);
1986 if (!isnull)
1987 build->tgoldtable =
1989 else
1990 build->tgoldtable = NULL;
1991
1993 tgrel->rd_att, &isnull);
1994 if (!isnull)
1995 build->tgnewtable =
1997 else
1998 build->tgnewtable = NULL;
1999
2000 datum = fastgetattr(htup, Anum_pg_trigger_tgqual,
2001 tgrel->rd_att, &isnull);
2002 if (!isnull)
2003 build->tgqual = TextDatumGetCString(datum);
2004 else
2005 build->tgqual = NULL;
2006
2007 numtrigs++;
2008 }
2009
2012
2013 /* There might not be any triggers */
2014 if (numtrigs == 0)
2015 {
2016 pfree(triggers);
2017 return;
2018 }
2019
2020 /* Build trigdesc */
2021 trigdesc = palloc0_object(TriggerDesc);
2022 trigdesc->triggers = triggers;
2023 trigdesc->numtriggers = numtrigs;
2024 for (i = 0; i < numtrigs; i++)
2025 SetTriggerFlags(trigdesc, &(triggers[i]));
2026
2027 /* Copy completed trigdesc into cache storage */
2029 relation->trigdesc = CopyTriggerDesc(trigdesc);
2031
2032 /* Release working memory */
2033 FreeTriggerDesc(trigdesc);
2034}
#define TextDatumGetCString(d)
Definition builtins.h:99
#define DatumGetByteaPP(X)
Definition fmgr.h:292
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
long val
Definition informix.c:689
MemoryContext CacheMemoryContext
Definition mcxt.c:170
Datum nameout(PG_FUNCTION_ARGS)
Definition name.c:71
static char * DatumGetCString(Datum X)
Definition postgres.h:365
static Datum NameGetDatum(const NameData *X)
Definition postgres.h:406
TriggerDesc * trigdesc
Definition rel.h:117
Oid tgoid
Definition reltrigger.h:25
Definition c.h:835
void FreeTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2172
static void SetTriggerFlags(TriggerDesc *trigdesc, Trigger *trigger)
Definition trigger.c:2040
TriggerDesc * CopyTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2117
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486

References AccessShareLock, BTEqualStrategyNumber, CacheMemoryContext, CopyTriggerDesc(), DatumGetByteaPP, DatumGetCString(), DirectFunctionCall1, elog, ERROR, fastgetattr(), fb(), Form_pg_trigger, FreeTriggerDesc(), GETSTRUCT(), HeapTupleIsValid, i, memcpy(), MemoryContextSwitchTo(), NameGetDatum(), nameout(), TriggerDesc::numtriggers, ObjectIdGetDatum(), OidIsValid, palloc(), palloc0_object, pfree(), pstrdup(), RelationGetRelationName, RelationGetRelid, repalloc(), ScanKeyInit(), SetTriggerFlags(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TextDatumGetCString, Trigger::tgoid, RelationData::trigdesc, TriggerDesc::triggers, val, and VARDATA_ANY().

Referenced by RelationBuildDesc(), and RelationCacheInitializePhase3().

◆ RemoveTriggerById()

void RemoveTriggerById ( Oid  trigOid)
extern

Definition at line 1305 of file trigger.c.

1306{
1309 ScanKeyData skey[1];
1310 HeapTuple tup;
1311 Oid relid;
1312 Relation rel;
1313
1315
1316 /*
1317 * Find the trigger to delete.
1318 */
1319 ScanKeyInit(&skey[0],
1322 ObjectIdGetDatum(trigOid));
1323
1325 NULL, 1, skey);
1326
1328 if (!HeapTupleIsValid(tup))
1329 elog(ERROR, "could not find tuple for trigger %u", trigOid);
1330
1331 /*
1332 * Open and exclusive-lock the relation the trigger belongs to.
1333 */
1334 relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
1335
1336 rel = table_open(relid, AccessExclusiveLock);
1337
1338 if (rel->rd_rel->relkind != RELKIND_RELATION &&
1339 rel->rd_rel->relkind != RELKIND_VIEW &&
1340 rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
1341 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1342 ereport(ERROR,
1344 errmsg("relation \"%s\" cannot have triggers",
1346 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
1347
1349 ereport(ERROR,
1351 errmsg("permission denied: \"%s\" is a system catalog",
1353
1354 /*
1355 * Delete the pg_trigger tuple.
1356 */
1357 CatalogTupleDelete(tgrel, &tup->t_self);
1358
1361
1362 /*
1363 * We do not bother to try to determine whether any other triggers remain,
1364 * which would be needed in order to decide whether it's safe to clear the
1365 * relation's relhastriggers. (In any case, there might be a concurrent
1366 * process adding new triggers.) Instead, just force a relcache inval to
1367 * make other backends (and this one too!) rebuild their relcache entries.
1368 * There's no great harm in leaving relhastriggers true even if there are
1369 * no triggers left.
1370 */
1372
1373 /* Keep lock on trigger's rel until end of xact */
1374 table_close(rel, NoLock);
1375}
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
Definition indexing.c:365
#define AccessExclusiveLock
Definition lockdefs.h:43

References AccessExclusiveLock, allowSystemTableMods, BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleDelete(), elog, ereport, errcode(), errdetail_relkind_not_supported(), errmsg, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), HeapTupleIsValid, IsSystemRelation(), NoLock, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelationName, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by doDeletion().

◆ renametrig()

ObjectAddress renametrig ( RenameStmt stmt)
extern

Definition at line 1494 of file trigger.c.

1495{
1496 Oid tgoid;
1499 HeapTuple tuple;
1501 ScanKeyData key[2];
1502 Oid relid;
1503 ObjectAddress address;
1504
1505 /*
1506 * Look up name, check permissions, and acquire lock (which we will NOT
1507 * release until end of transaction).
1508 */
1510 0,
1512 NULL);
1513
1514 /* Have lock already, so just need to build relcache entry. */
1515 targetrel = relation_open(relid, NoLock);
1516
1517 /*
1518 * On partitioned tables, this operation recurses to partitions. Lock all
1519 * tables upfront.
1520 */
1521 if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1523
1525
1526 /*
1527 * Search for the trigger to modify.
1528 */
1529 ScanKeyInit(&key[0],
1532 ObjectIdGetDatum(relid));
1533 ScanKeyInit(&key[1],
1536 PointerGetDatum(stmt->subname));
1538 NULL, 2, key);
1540 {
1542
1544 tgoid = trigform->oid;
1545
1546 /*
1547 * If the trigger descends from a trigger on a parent partitioned
1548 * table, reject the rename. We don't allow a trigger in a partition
1549 * to differ in name from that of its parent: that would lead to an
1550 * inconsistency that pg_dump would not reproduce.
1551 */
1552 if (OidIsValid(trigform->tgparentid))
1553 ereport(ERROR,
1555 errmsg("cannot rename trigger \"%s\" on table \"%s\"",
1557 errhint("Rename the trigger on the partitioned table \"%s\" instead.",
1558 get_rel_name(get_partition_parent(relid, false))));
1559
1560
1561 /* Rename the trigger on this relation ... */
1562 renametrig_internal(tgrel, targetrel, tuple, stmt->newname,
1563 stmt->subname);
1564
1565 /* ... and if it is partitioned, recurse to its partitions */
1566 if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1567 {
1569
1570 for (int i = 0; i < partdesc->nparts; i++)
1571 {
1572 Oid partitionId = partdesc->oids[i];
1573
1575 stmt->newname, stmt->subname);
1576 }
1577 }
1578 }
1579 else
1580 {
1581 ereport(ERROR,
1583 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1585 }
1586
1587 ObjectAddressSet(address, TriggerRelationId, tgoid);
1588
1590
1592
1593 /*
1594 * Close rel, but keep exclusive lock!
1595 */
1597
1598 return address;
1599}
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition namespace.c:442
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition partition.c:53
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
static void renametrig_internal(Relation tgrel, Relation targetrel, HeapTuple trigtup, const char *newname, const char *expected_name)
Definition trigger.c:1609
static void RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition trigger.c:1434
static void renametrig_partition(Relation tgrel, Oid partitionId, Oid parentTriggerOid, const char *newname, const char *expected_name)
Definition trigger.c:1680

References AccessExclusiveLock, BTEqualStrategyNumber, ereport, errcode(), errhint(), errmsg, ERROR, fb(), find_all_inheritors(), Form_pg_trigger, get_partition_parent(), get_rel_name(), GETSTRUCT(), HeapTupleIsValid, i, NoLock, PartitionDescData::nparts, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, PartitionDescData::oids, PointerGetDatum, RangeVarCallbackForRenameTrigger(), RangeVarGetRelidExtended(), relation_close(), relation_open(), RelationGetPartitionDesc(), RelationGetRelationName, renametrig_internal(), renametrig_partition(), RowExclusiveLock, ScanKeyInit(), stmt, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ExecRenameStmt().

◆ RI_FKey_fk_upd_check_required()

bool RI_FKey_fk_upd_check_required ( Trigger trigger,
Relation  fk_rel,
TupleTableSlot oldslot,
TupleTableSlot newslot 
)
extern

Definition at line 1572 of file ri_triggers.c.

1574{
1576 int ri_nullcheck;
1577
1578 /*
1579 * AfterTriggerSaveEvent() handles things such that this function is never
1580 * called for partitioned tables.
1581 */
1582 Assert(fk_rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE);
1583
1585
1587
1588 /*
1589 * If all new key values are NULL, the row satisfies the constraint, so no
1590 * check is needed.
1591 */
1593 return false;
1594
1595 /*
1596 * If some new key values are NULL, the behavior depends on the match
1597 * type.
1598 */
1599 else if (ri_nullcheck == RI_KEYS_SOME_NULL)
1600 {
1601 switch (riinfo->confmatchtype)
1602 {
1604
1605 /*
1606 * If any new key value is NULL, the row must satisfy the
1607 * constraint, so no check is needed.
1608 */
1609 return false;
1610
1612
1613 /*
1614 * Don't know, must run full check.
1615 */
1616 break;
1617
1619
1620 /*
1621 * If some new key values are NULL, the row fails the
1622 * constraint. We must not throw error here, because the row
1623 * might get invalidated before the constraint is to be
1624 * checked, but we should queue the event to apply the check
1625 * later.
1626 */
1627 return true;
1628 }
1629 }
1630
1631 /*
1632 * Continues here for no new key values are NULL, or we couldn't decide
1633 * yet.
1634 */
1635
1636 /*
1637 * If the original row was inserted by our own transaction, we must fire
1638 * the trigger whether or not the keys are equal. This is because our
1639 * UPDATE will invalidate the INSERT so that the INSERT RI trigger will
1640 * not do anything; so we had better do the UPDATE check. (We could skip
1641 * this if we knew the INSERT trigger already fired, but there is no easy
1642 * way to know that.)
1643 */
1645 return true;
1646
1647 /* If all old and new key values are equal, no check is needed */
1648 if (ri_KeysEqual(fk_rel, oldslot, newslot, riinfo, false))
1649 return false;
1650
1651 /* Else we need to fire the trigger. */
1652 return true;
1653}
#define FKCONSTR_MATCH_SIMPLE
#define FKCONSTR_MATCH_PARTIAL
#define FKCONSTR_MATCH_FULL
#define RI_KEYS_SOME_NULL
Definition ri_triggers.c:70
static int ri_NullCheck(TupleDesc tupDesc, TupleTableSlot *slot, const RI_ConstraintInfo *riinfo, bool rel_is_pk)
static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot, const RI_ConstraintInfo *riinfo, bool rel_is_pk)
#define RI_KEYS_ALL_NULL
Definition ri_triggers.c:69
static RI_ConstraintInfo * ri_FetchConstraintInfo(Trigger *trigger, Relation trig_rel, bool rel_is_pk)
static bool slot_is_current_xact_tuple(TupleTableSlot *slot)
Definition tuptable.h:467

References Assert, fb(), FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_PARTIAL, FKCONSTR_MATCH_SIMPLE, RelationGetDescr, ri_FetchConstraintInfo(), RI_KEYS_ALL_NULL, RI_KEYS_SOME_NULL, ri_KeysEqual(), ri_NullCheck(), and slot_is_current_xact_tuple().

Referenced by AfterTriggerSaveEvent().

◆ RI_FKey_pk_upd_check_required()

bool RI_FKey_pk_upd_check_required ( Trigger trigger,
Relation  pk_rel,
TupleTableSlot oldslot,
TupleTableSlot newslot 
)
extern

Definition at line 1540 of file ri_triggers.c.

1542{
1544
1545 riinfo = ri_FetchConstraintInfo(trigger, pk_rel, true);
1546
1547 /*
1548 * If any old key value is NULL, the row could not have been referenced by
1549 * an FK row, so no check is needed.
1550 */
1552 return false;
1553
1554 /* If all old and new key values are equal, no check is needed */
1555 if (newslot && ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
1556 return false;
1557
1558 /* Else we need to fire the trigger. */
1559 return true;
1560}
#define RI_KEYS_NONE_NULL
Definition ri_triggers.c:71

References fb(), RelationGetDescr, ri_FetchConstraintInfo(), RI_KEYS_NONE_NULL, ri_KeysEqual(), and ri_NullCheck().

Referenced by AfterTriggerSaveEvent().

◆ RI_FKey_trigger_type()

int RI_FKey_trigger_type ( Oid  tgfoid)
extern

Definition at line 4159 of file ri_triggers.c.

4160{
4161 switch (tgfoid)
4162 {
4173 return RI_TRIGGER_PK;
4174
4177 return RI_TRIGGER_FK;
4178 }
4179
4180 return RI_TRIGGER_NONE;
4181}
#define RI_TRIGGER_FK
Definition trigger.h:287
#define RI_TRIGGER_NONE
Definition trigger.h:288
#define RI_TRIGGER_PK
Definition trigger.h:286

References fb(), RI_TRIGGER_FK, RI_TRIGGER_NONE, and RI_TRIGGER_PK.

Referenced by AfterTriggerSaveEvent(), ExecCrossPartitionUpdateForeignKey(), GetForeignKeyActionTriggers(), and GetForeignKeyCheckTriggers().

◆ RI_Initial_Check()

bool RI_Initial_Check ( Trigger trigger,
Relation  fk_rel,
Relation  pk_rel 
)
extern

Definition at line 1673 of file ri_triggers.c.

1674{
1684 List *rtes = NIL;
1685 List *perminfos = NIL;
1686 const char *sep;
1687 const char *fk_only;
1688 const char *pk_only;
1689 int save_nestlevel;
1690 char workmembuf[32];
1691 int spi_result;
1693
1695
1696 /*
1697 * Check to make sure current user has enough permissions to do the test
1698 * query. (If not, caller can fall back to the trigger method, which
1699 * works because it changes user IDs on the fly.)
1700 *
1701 * XXX are there any other show-stopper conditions to check?
1702 */
1704 pk_perminfo->relid = RelationGetRelid(pk_rel);
1705 pk_perminfo->requiredPerms = ACL_SELECT;
1708 rte->rtekind = RTE_RELATION;
1709 rte->relid = RelationGetRelid(pk_rel);
1710 rte->relkind = pk_rel->rd_rel->relkind;
1711 rte->rellockmode = AccessShareLock;
1712 rte->perminfoindex = list_length(perminfos);
1713 rtes = lappend(rtes, rte);
1714
1717 fk_perminfo->requiredPerms = ACL_SELECT;
1720 rte->rtekind = RTE_RELATION;
1721 rte->relid = RelationGetRelid(fk_rel);
1722 rte->relkind = fk_rel->rd_rel->relkind;
1723 rte->rellockmode = AccessShareLock;
1724 rte->perminfoindex = list_length(perminfos);
1725 rtes = lappend(rtes, rte);
1726
1727 for (int i = 0; i < riinfo->nkeys; i++)
1728 {
1729 int attno;
1730
1731 attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
1732 pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno);
1733
1734 attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
1735 fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno);
1736 }
1737
1738 if (!ExecCheckPermissions(rtes, perminfos, false))
1739 return false;
1740
1741 /*
1742 * Also punt if RLS is enabled on either table unless this role has the
1743 * bypassrls right or is the table owner of the table(s) involved which
1744 * have RLS enabled.
1745 */
1747 ((pk_rel->rd_rel->relrowsecurity &&
1749 GetUserId())) ||
1750 (fk_rel->rd_rel->relrowsecurity &&
1752 GetUserId()))))
1753 return false;
1754
1755 /*----------
1756 * The query string built is:
1757 * SELECT fk.keycols FROM [ONLY] relname fk
1758 * LEFT OUTER JOIN [ONLY] pkrelname pk
1759 * ON (pk.pkkeycol1=fk.keycol1 [AND ...])
1760 * WHERE pk.pkkeycol1 IS NULL AND
1761 * For MATCH SIMPLE:
1762 * (fk.keycol1 IS NOT NULL [AND ...])
1763 * For MATCH FULL:
1764 * (fk.keycol1 IS NOT NULL [OR ...])
1765 *
1766 * We attach COLLATE clauses to the operators when comparing columns
1767 * that have different collations.
1768 *----------
1769 */
1771 appendStringInfoString(&querybuf, "SELECT ");
1772 sep = "";
1773 for (int i = 0; i < riinfo->nkeys; i++)
1774 {
1776 RIAttName(fk_rel, riinfo->fk_attnums[i]));
1777 appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
1778 sep = ", ";
1779 }
1780
1783 fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
1784 "" : "ONLY ";
1785 pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
1786 "" : "ONLY ";
1788 " FROM %s%s fk LEFT OUTER JOIN %s%s pk ON",
1790
1791 strcpy(pkattname, "pk.");
1792 strcpy(fkattname, "fk.");
1793 sep = "(";
1794 for (int i = 0; i < riinfo->nkeys; i++)
1795 {
1796 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
1797 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
1798 Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
1799 Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
1800
1802 RIAttName(pk_rel, riinfo->pk_attnums[i]));
1804 RIAttName(fk_rel, riinfo->fk_attnums[i]));
1807 riinfo->pf_eq_oprs[i],
1809 if (pk_coll != fk_coll)
1811 sep = "AND";
1812 }
1813
1814 /*
1815 * It's sufficient to test any one pk attribute for null to detect a join
1816 * failure.
1817 */
1818 quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0]));
1819 appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
1820
1821 sep = "";
1822 for (int i = 0; i < riinfo->nkeys; i++)
1823 {
1824 quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
1826 "%sfk.%s IS NOT NULL",
1827 sep, fkattname);
1828 switch (riinfo->confmatchtype)
1829 {
1831 sep = " AND ";
1832 break;
1834 sep = " OR ";
1835 break;
1836 }
1837 }
1839
1840 /*
1841 * Temporarily increase work_mem so that the check query can be executed
1842 * more efficiently. It seems okay to do this because the query is simple
1843 * enough to not use a multiple of work_mem, and one typically would not
1844 * have many large foreign-key validations happening concurrently. So
1845 * this seems to meet the criteria for being considered a "maintenance"
1846 * operation, and accordingly we use maintenance_work_mem. However, we
1847 * must also set hash_mem_multiplier to 1, since it is surely not okay to
1848 * let that get applied to the maintenance_work_mem value.
1849 *
1850 * We use the equivalent of a function SET option to allow the setting to
1851 * persist for exactly the duration of the check query. guc.c also takes
1852 * care of undoing the setting on error.
1853 */
1854 save_nestlevel = NewGUCNestLevel();
1855
1857 (void) set_config_option("work_mem", workmembuf,
1859 GUC_ACTION_SAVE, true, 0, false);
1860 (void) set_config_option("hash_mem_multiplier", "1",
1862 GUC_ACTION_SAVE, true, 0, false);
1863
1864 SPI_connect();
1865
1866 /*
1867 * Generate the plan. We don't need to cache it, and there are no
1868 * arguments to the plan.
1869 */
1870 qplan = SPI_prepare(querybuf.data, 0, NULL);
1871
1872 if (qplan == NULL)
1873 elog(ERROR, "SPI_prepare returned %s for %s",
1875
1876 /*
1877 * Run the plan. For safety we force a current snapshot to be used. (In
1878 * transaction-snapshot mode, this arguably violates transaction isolation
1879 * rules, but we really haven't got much choice.) We don't need to
1880 * register the snapshot, because SPI_execute_snapshot will see to it. We
1881 * need at most one tuple returned, so pass limit = 1.
1882 */
1884 NULL, NULL,
1887 true, false, 1);
1888
1889 /* Check result */
1891 elog(ERROR, "SPI_execute_snapshot returned %s", SPI_result_code_string(spi_result));
1892
1893 /* Did we find a tuple violating the constraint? */
1894 if (SPI_processed > 0)
1895 {
1896 TupleTableSlot *slot;
1897 HeapTuple tuple = SPI_tuptable->vals[0];
1898 TupleDesc tupdesc = SPI_tuptable->tupdesc;
1900
1901 slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
1902
1903 heap_deform_tuple(tuple, tupdesc,
1904 slot->tts_values, slot->tts_isnull);
1906
1907 /*
1908 * The columns to look at in the result tuple are 1..N, not whatever
1909 * they are in the fk_rel. Hack up riinfo so that the subroutines
1910 * called here will behave properly.
1911 *
1912 * In addition to this, we have to pass the correct tupdesc to
1913 * ri_ReportViolation, overriding its normal habit of using the pk_rel
1914 * or fk_rel's tupdesc.
1915 */
1917 for (int i = 0; i < fake_riinfo.nkeys; i++)
1918 fake_riinfo.fk_attnums[i] = i + 1;
1919
1920 /*
1921 * If it's MATCH FULL, and there are any nulls in the FK keys,
1922 * complain about that rather than the lack of a match. MATCH FULL
1923 * disallows partially-null FK rows.
1924 */
1925 if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
1926 ri_NullCheck(tupdesc, slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
1927 ereport(ERROR,
1929 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
1931 NameStr(fake_riinfo.conname)),
1932 errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
1934 NameStr(fake_riinfo.conname))));
1935
1936 /*
1937 * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
1938 * query, which isn't true, but will cause it to use
1939 * fake_riinfo.fk_attnums as we need.
1940 */
1942 pk_rel, fk_rel,
1943 slot, tupdesc,
1944 RI_PLAN_CHECK_LOOKUPPK, false, false);
1945
1947 }
1948
1949 if (SPI_finish() != SPI_OK_FINISH)
1950 elog(ERROR, "SPI_finish failed");
1951
1952 /*
1953 * Restore work_mem and hash_mem_multiplier.
1954 */
1955 AtEOXact_GUC(true, save_nestlevel);
1956
1957 return true;
1958}
bool has_bypassrls_privilege(Oid roleid)
Definition aclchk.c:4254
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4156
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:934
bool ExecCheckPermissions(List *rangeTable, List *rteperminfos, bool ereport_on_violation)
Definition execMain.c:593
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
int maintenance_work_mem
Definition globals.c:135
int NewGUCNestLevel(void)
Definition guc.c:2142
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition guc.c:3248
@ GUC_ACTION_SAVE
Definition guc.h:205
@ PGC_S_SESSION
Definition guc.h:126
@ PGC_USERSET
Definition guc.h:79
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition heaptuple.c:1254
#define makeNode(_type_)
Definition nodes.h:159
@ RTE_RELATION
#define ACL_SELECT
Definition parsenodes.h:77
int errtableconstraint(Relation rel, const char *conname)
Definition relcache.c:6138
static pg_noreturn void ri_ReportViolation(const RI_ConstraintInfo *riinfo, Relation pk_rel, Relation fk_rel, TupleTableSlot *violatorslot, TupleDesc tupdesc, int queryno, bool is_restrict, bool partgone)
static void quoteOneName(char *buffer, const char *name)
#define RIAttType(rel, attnum)
Definition ri_triggers.c:93
static void ri_GenerateQualCollation(StringInfo buf, Oid collation)
#define MAX_QUOTED_REL_NAME_LEN
Definition ri_triggers.c:90
static void quoteRelationName(char *buffer, Relation rel)
static void ri_GenerateQual(StringInfo buf, const char *sep, const char *leftop, Oid leftoptype, Oid opoid, const char *rightop, Oid rightoptype)
#define RIAttCollation(rel, attnum)
Definition ri_triggers.c:94
#define RI_PLAN_CHECK_LOOKUPPK
Definition ri_triggers.c:75
#define MAX_QUOTED_NAME_LEN
Definition ri_triggers.c:89
#define RIAttName(rel, attnum)
Definition ri_triggers.c:92
Snapshot GetLatestSnapshot(void)
Definition snapmgr.c:354
#define InvalidSnapshot
Definition snapshot.h:119
uint64 SPI_processed
Definition spi.c:45
const char * SPI_result_code_string(int code)
Definition spi.c:1973
SPITupleTable * SPI_tuptable
Definition spi.c:46
int SPI_connect(void)
Definition spi.c:95
int SPI_result
Definition spi.c:47
int SPI_finish(void)
Definition spi.c:183
int SPI_execute_snapshot(SPIPlanPtr plan, const Datum *Values, const char *Nulls, Snapshot snapshot, Snapshot crosscheck_snapshot, bool read_only, bool fire_triggers, long tcount)
Definition spi.c:774
SPIPlanPtr SPI_prepare(const char *src, int nargs, const Oid *argtypes)
Definition spi.c:861
#define SPI_OK_FINISH
Definition spi.h:83
#define SPI_OK_SELECT
Definition spi.h:86
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
TupleDesc tupdesc
Definition spi.h:25
HeapTuple * vals
Definition spi.h:26
bool * tts_isnull
Definition tuptable.h:133
Datum * tts_values
Definition tuptable.h:131
#define FirstLowInvalidHeapAttributeNumber
Definition sysattr.h:27

References AccessShareLock, ACL_SELECT, appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), AtEOXact_GUC(), bms_add_member(), elog, ereport, errcode(), errdetail(), errmsg, ERROR, errtableconstraint(), ExecCheckPermissions(), ExecDropSingleTupleTableSlot(), ExecStoreVirtualTuple(), fb(), FirstLowInvalidHeapAttributeNumber, FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_SIMPLE, GetLatestSnapshot(), GetUserId(), GUC_ACTION_SAVE, has_bypassrls_privilege(), heap_deform_tuple(), i, initStringInfo(), InvalidSnapshot, lappend(), list_length(), maintenance_work_mem, makeNode, MakeSingleTupleTableSlot(), MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, memcpy(), NameStr, NewGUCNestLevel(), NIL, object_ownercheck(), PGC_S_SESSION, PGC_USERSET, quoteOneName(), quoteRelationName(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ri_FetchConstraintInfo(), ri_GenerateQual(), ri_GenerateQualCollation(), RI_KEYS_NONE_NULL, ri_NullCheck(), RI_PLAN_CHECK_LOOKUPPK, ri_ReportViolation(), RIAttCollation, RIAttName, RIAttType, RTE_RELATION, set_config_option(), snprintf, SPI_connect(), SPI_execute_snapshot(), SPI_finish(), SPI_OK_FINISH, SPI_OK_SELECT, SPI_prepare(), SPI_processed, SPI_result, SPI_result_code_string(), SPI_tuptable, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, TTSOpsVirtual, SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by validateForeignKeyConstraint().

◆ RI_PartitionRemove_Check()

void RI_PartitionRemove_Check ( Trigger trigger,
Relation  fk_rel,
Relation  pk_rel 
)
extern

Definition at line 1967 of file ri_triggers.c.

1968{
1971 char *constraintDef;
1976 const char *sep;
1977 const char *fk_only;
1978 int save_nestlevel;
1979 char workmembuf[32];
1980 int spi_result;
1982 int i;
1983
1985
1986 /*
1987 * We don't check permissions before displaying the error message, on the
1988 * assumption that the user detaching the partition must have enough
1989 * privileges to examine the table contents anyhow.
1990 */
1991
1992 /*----------
1993 * The query string built is:
1994 * SELECT fk.keycols FROM [ONLY] relname fk
1995 * JOIN pkrelname pk
1996 * ON (pk.pkkeycol1=fk.keycol1 [AND ...])
1997 * WHERE (<partition constraint>) AND
1998 * For MATCH SIMPLE:
1999 * (fk.keycol1 IS NOT NULL [AND ...])
2000 * For MATCH FULL:
2001 * (fk.keycol1 IS NOT NULL [OR ...])
2002 *
2003 * We attach COLLATE clauses to the operators when comparing columns
2004 * that have different collations.
2005 *----------
2006 */
2008 appendStringInfoString(&querybuf, "SELECT ");
2009 sep = "";
2010 for (i = 0; i < riinfo->nkeys; i++)
2011 {
2013 RIAttName(fk_rel, riinfo->fk_attnums[i]));
2014 appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
2015 sep = ", ";
2016 }
2017
2020 fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
2021 "" : "ONLY ";
2023 " FROM %s%s fk JOIN %s pk ON",
2025 strcpy(pkattname, "pk.");
2026 strcpy(fkattname, "fk.");
2027 sep = "(";
2028 for (i = 0; i < riinfo->nkeys; i++)
2029 {
2030 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
2031 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
2032 Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
2033 Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
2034
2036 RIAttName(pk_rel, riinfo->pk_attnums[i]));
2038 RIAttName(fk_rel, riinfo->fk_attnums[i]));
2041 riinfo->pf_eq_oprs[i],
2043 if (pk_coll != fk_coll)
2045 sep = "AND";
2046 }
2047
2048 /*
2049 * Start the WHERE clause with the partition constraint (except if this is
2050 * the default partition and there's no other partition, because the
2051 * partition constraint is the empty string in that case.)
2052 */
2054 if (constraintDef && constraintDef[0] != '\0')
2055 appendStringInfo(&querybuf, ") WHERE %s AND (",
2057 else
2058 appendStringInfoString(&querybuf, ") WHERE (");
2059
2060 sep = "";
2061 for (i = 0; i < riinfo->nkeys; i++)
2062 {
2063 quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
2065 "%sfk.%s IS NOT NULL",
2066 sep, fkattname);
2067 switch (riinfo->confmatchtype)
2068 {
2070 sep = " AND ";
2071 break;
2073 sep = " OR ";
2074 break;
2075 }
2076 }
2078
2079 /*
2080 * Temporarily increase work_mem so that the check query can be executed
2081 * more efficiently. It seems okay to do this because the query is simple
2082 * enough to not use a multiple of work_mem, and one typically would not
2083 * have many large foreign-key validations happening concurrently. So
2084 * this seems to meet the criteria for being considered a "maintenance"
2085 * operation, and accordingly we use maintenance_work_mem. However, we
2086 * must also set hash_mem_multiplier to 1, since it is surely not okay to
2087 * let that get applied to the maintenance_work_mem value.
2088 *
2089 * We use the equivalent of a function SET option to allow the setting to
2090 * persist for exactly the duration of the check query. guc.c also takes
2091 * care of undoing the setting on error.
2092 */
2093 save_nestlevel = NewGUCNestLevel();
2094
2096 (void) set_config_option("work_mem", workmembuf,
2098 GUC_ACTION_SAVE, true, 0, false);
2099 (void) set_config_option("hash_mem_multiplier", "1",
2101 GUC_ACTION_SAVE, true, 0, false);
2102
2103 SPI_connect();
2104
2105 /*
2106 * Generate the plan. We don't need to cache it, and there are no
2107 * arguments to the plan.
2108 */
2109 qplan = SPI_prepare(querybuf.data, 0, NULL);
2110
2111 if (qplan == NULL)
2112 elog(ERROR, "SPI_prepare returned %s for %s",
2114
2115 /*
2116 * Run the plan. For safety we force a current snapshot to be used. (In
2117 * transaction-snapshot mode, this arguably violates transaction isolation
2118 * rules, but we really haven't got much choice.) We don't need to
2119 * register the snapshot, because SPI_execute_snapshot will see to it. We
2120 * need at most one tuple returned, so pass limit = 1.
2121 */
2123 NULL, NULL,
2126 true, false, 1);
2127
2128 /* Check result */
2130 elog(ERROR, "SPI_execute_snapshot returned %s", SPI_result_code_string(spi_result));
2131
2132 /* Did we find a tuple that would violate the constraint? */
2133 if (SPI_processed > 0)
2134 {
2135 TupleTableSlot *slot;
2136 HeapTuple tuple = SPI_tuptable->vals[0];
2137 TupleDesc tupdesc = SPI_tuptable->tupdesc;
2139
2140 slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
2141
2142 heap_deform_tuple(tuple, tupdesc,
2143 slot->tts_values, slot->tts_isnull);
2145
2146 /*
2147 * The columns to look at in the result tuple are 1..N, not whatever
2148 * they are in the fk_rel. Hack up riinfo so that ri_ReportViolation
2149 * will behave properly.
2150 *
2151 * In addition to this, we have to pass the correct tupdesc to
2152 * ri_ReportViolation, overriding its normal habit of using the pk_rel
2153 * or fk_rel's tupdesc.
2154 */
2156 for (i = 0; i < fake_riinfo.nkeys; i++)
2157 fake_riinfo.pk_attnums[i] = i + 1;
2158
2160 slot, tupdesc, 0, false, true);
2161 }
2162
2163 if (SPI_finish() != SPI_OK_FINISH)
2164 elog(ERROR, "SPI_finish failed");
2165
2166 /*
2167 * Restore work_mem and hash_mem_multiplier.
2168 */
2169 AtEOXact_GUC(true, save_nestlevel);
2170}
char * pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
Definition ruleutils.c:2488

References appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), AtEOXact_GUC(), elog, ERROR, ExecStoreVirtualTuple(), fb(), FKCONSTR_MATCH_FULL, FKCONSTR_MATCH_SIMPLE, GetLatestSnapshot(), GUC_ACTION_SAVE, heap_deform_tuple(), i, initStringInfo(), InvalidSnapshot, maintenance_work_mem, MakeSingleTupleTableSlot(), MAX_QUOTED_NAME_LEN, MAX_QUOTED_REL_NAME_LEN, memcpy(), NewGUCNestLevel(), pg_get_partconstrdef_string(), PGC_S_SESSION, PGC_USERSET, quoteOneName(), quoteRelationName(), RelationGetRelid, ri_FetchConstraintInfo(), ri_GenerateQual(), ri_GenerateQualCollation(), ri_ReportViolation(), RIAttCollation, RIAttName, RIAttType, set_config_option(), snprintf, SPI_connect(), SPI_execute_snapshot(), SPI_finish(), SPI_OK_FINISH, SPI_OK_SELECT, SPI_prepare(), SPI_processed, SPI_result, SPI_result_code_string(), SPI_tuptable, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, TTSOpsVirtual, SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by ATDetachCheckNoForeignKeyRefs().

◆ TriggerSetParentTrigger()

void TriggerSetParentTrigger ( Relation  trigRel,
Oid  childTrigId,
Oid  parentTrigId,
Oid  childTableId 
)
extern

Definition at line 1234 of file trigger.c.

1238{
1240 ScanKeyData skey[1];
1242 HeapTuple tuple,
1243 newtup;
1246
1247 /*
1248 * Find the trigger to delete.
1249 */
1250 ScanKeyInit(&skey[0],
1254
1256 NULL, 1, skey);
1257
1258 tuple = systable_getnext(tgscan);
1259 if (!HeapTupleIsValid(tuple))
1260 elog(ERROR, "could not find tuple for trigger %u", childTrigId);
1261 newtup = heap_copytuple(tuple);
1264 {
1265 /* don't allow setting parent for a constraint that already has one */
1266 if (OidIsValid(trigForm->tgparentid))
1267 elog(ERROR, "trigger %u already has a parent trigger",
1268 childTrigId);
1269
1270 trigForm->tgparentid = parentTrigId;
1271
1273
1275
1278
1281 }
1282 else
1283 {
1284 trigForm->tgparentid = InvalidOid;
1285
1287
1294 }
1295
1298}
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:364

References BTEqualStrategyNumber, CatalogTupleUpdate(), deleteDependencyRecordsForClass(), DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, elog, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvalidOid, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, recordDependencyOn(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.

Referenced by AttachPartitionForeignKey(), and DetachPartitionFinalize().

Variable Documentation

◆ SessionReplicationRole

PGDLLIMPORT int SessionReplicationRole
extern