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
 

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)
 

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,
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#endif /* TRIGGER_H */
#define PGDLLIMPORT
Definition c.h:1423
#define stmt
int LOCKMODE
Definition lockdefs.h:26
CmdType
Definition nodes.h:273
NodeTag
Definition nodes.h:27
unsigned int Oid
static int fb(int x)
Definition pg_list.h:54
Definition nodes.h:135
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:73
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:2973
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition trigger.c:4959
void AfterTriggerBeginXact(void)
Definition trigger.c:5085
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition trigger.c:2803
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2403
bool ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition trigger.c:2467
void AfterTriggerEndSubXact(bool isCommit)
Definition trigger.c:5440
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:3282
PGDLLIMPORT int SessionReplicationRole
Definition trigger.c:65
void FreeTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2147
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition trigger.c:2850
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2632
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:1728
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition trigger.c:2279
bool ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition trigger.c:2571
void ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:3329
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:3146
void AfterTriggerSetState(ConstraintsSetStmt *stmt)
Definition trigger.c:5768
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:2703
Oid get_trigger_oid(Oid relid, const char *trigname, bool missing_ok)
Definition trigger.c:1372
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2955
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2683
ObjectAddress renametrig(RenameStmt *stmt)
Definition trigger.c:1469
void AfterTriggerFireDeferred(void)
Definition trigger.c:5288
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition trigger.c:2545
void TriggerSetParentTrigger(Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
Definition trigger.c:1222
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2454
TriggerDesc * CopyTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2092
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition trigger.c:3216
void AfterTriggerEndXact(bool isCommit)
Definition trigger.c:5344
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6083
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
void RelationBuildTriggers(Relation relation)
Definition trigger.c:1863
void AfterTriggerBeginSubXact(void)
Definition trigger.c:5392
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:5137
void RemoveTriggerById(Oid trigOid)
Definition trigger.c:1293
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2897
void AfterTriggerBeginQuery(void)
Definition trigger.c:5117
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

◆ TransitionCaptureState

◆ TriggerData

◆ TriggerEvent

Definition at line 29 of file trigger.h.

Function Documentation

◆ AfterTriggerBeginQuery()

void AfterTriggerBeginQuery ( void  )
extern

Definition at line 5117 of file trigger.c.

5118{
5119 /* Increase the query stack depth */
5121}
static AfterTriggersData afterTriggers
Definition trigger.c:3932

References afterTriggers, and AfterTriggersData::query_depth.

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

◆ AfterTriggerBeginSubXact()

void AfterTriggerBeginSubXact ( void  )
extern

Definition at line 5392 of file trigger.c.

5393{
5394 int my_level = GetCurrentTransactionNestLevel();
5395
5396 /*
5397 * Allocate more space in the trans_stack if needed. (Note: because the
5398 * minimum nest level of a subtransaction is 2, we waste the first couple
5399 * entries of the array; not worth the notational effort to avoid it.)
5400 */
5401 while (my_level >= afterTriggers.maxtransdepth)
5402 {
5404 {
5405 /* Arbitrarily initialize for max of 8 subtransaction levels */
5408 8 * sizeof(AfterTriggersTransData));
5410 }
5411 else
5412 {
5413 /* repalloc will keep the stack in the same context */
5415
5420 }
5421 }
5422
5423 /*
5424 * Push the current information into the stack. The SET CONSTRAINTS state
5425 * is not saved until/unless changed. Likewise, we don't make a
5426 * per-subtransaction event context until needed.
5427 */
5428 afterTriggers.trans_stack[my_level].state = NULL;
5432}
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1232
MemoryContext TopTransactionContext
Definition mcxt.c:171
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
CommandId firing_counter
Definition trigger.c:3883
AfterTriggersTransData * trans_stack
Definition trigger.c:3894
AfterTriggerEventList events
Definition trigger.c:3885
AfterTriggerEventList events
Definition trigger.c:3909
SetConstraintState state
Definition trigger.c:3908
CommandId firing_counter
Definition trigger.c:3911
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 5085 of file trigger.c.

5086{
5087 /*
5088 * Initialize after-trigger state structure to empty
5089 */
5090 afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */
5092
5093 /*
5094 * Verify that there is no leftover state remaining. If these assertions
5095 * trip, it means that AfterTriggerEndXact wasn't called or didn't clean
5096 * up properly.
5097 */
5105}
#define Assert(condition)
Definition c.h:945
uint32 CommandId
Definition c.h:752
AfterTriggerEventChunk * head
Definition trigger.c:3777
SetConstraintState state
Definition trigger.c:3884
AfterTriggersQueryData * query_stack
Definition trigger.c:3889
MemoryContext event_cxt
Definition trigger.c:3886

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

Referenced by StartTransaction().

◆ AfterTriggerEndQuery()

void AfterTriggerEndQuery ( EState estate)
extern

Definition at line 5137 of file trigger.c.

5138{
5140
5141 /* Must be inside a query, too */
5143
5144 /*
5145 * If we never even got as far as initializing the event stack, there
5146 * certainly won't be any events, so exit quickly.
5147 */
5149 {
5151 return;
5152 }
5153
5154 /*
5155 * Process all immediate-mode triggers queued by the query, and move the
5156 * deferred ones to the main list of deferred events.
5157 *
5158 * Notice that we decide which ones will be fired, and put the deferred
5159 * ones on the main list, before anything is actually fired. This ensures
5160 * reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
5161 * IMMEDIATE: all events we have decided to defer will be available for it
5162 * to fire.
5163 *
5164 * We loop in case a trigger queues more events at the same query level.
5165 * Ordinary trigger functions, including all PL/pgSQL trigger functions,
5166 * will instead fire any triggers in a dedicated query level. Foreign key
5167 * enforcement triggers do add to the current query level, thanks to their
5168 * passing fire_triggers = false to SPI_execute_snapshot(). Other
5169 * C-language triggers might do likewise.
5170 *
5171 * If we find no firable events, we don't have to increment
5172 * firing_counter.
5173 */
5175
5176 for (;;)
5177 {
5178 if (afterTriggerMarkEvents(&qs->events, &afterTriggers.events, true))
5179 {
5181 AfterTriggerEventChunk *oldtail = qs->events.tail;
5182
5183 if (afterTriggerInvokeEvents(&qs->events, firing_id, estate, false))
5184 break; /* all fired */
5185
5186 /*
5187 * Firing a trigger could result in query_stack being repalloc'd,
5188 * so we must recalculate qs after each afterTriggerInvokeEvents
5189 * call. Furthermore, it's unsafe to pass delete_ok = true here,
5190 * because that could cause afterTriggerInvokeEvents to try to
5191 * access qs->events after the stack has been repalloc'd.
5192 */
5194
5195 /*
5196 * We'll need to scan the events list again. To reduce the cost
5197 * of doing so, get rid of completely-fired chunks. We know that
5198 * all events were marked IN_PROGRESS or DONE at the conclusion of
5199 * afterTriggerMarkEvents, so any still-interesting events must
5200 * have been added after that, and so must be in the chunk that
5201 * was then the tail chunk, or in later chunks. So, zap all
5202 * chunks before oldtail. This is approximately the same set of
5203 * events we would have gotten rid of by passing delete_ok = true.
5204 */
5205 Assert(oldtail != NULL);
5206 while (qs->events.head != oldtail)
5208 }
5209 else
5210 break;
5211 }
5212
5213 /* Release query-level-local storage, including tuplestores if any */
5215
5217}
static void afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
Definition trigger.c:4267
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
Definition trigger.c:5228
static bool afterTriggerMarkEvents(AfterTriggerEventList *events, AfterTriggerEventList *move_list, bool immediate_only)
Definition trigger.c:4615
static bool afterTriggerInvokeEvents(AfterTriggerEventList *events, CommandId firing_id, EState *estate, bool delete_ok)
Definition trigger.c:4699

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

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

◆ AfterTriggerEndSubXact()

void AfterTriggerEndSubXact ( bool  isCommit)
extern

Definition at line 5440 of file trigger.c.

5441{
5442 int my_level = GetCurrentTransactionNestLevel();
5444 AfterTriggerEvent event;
5447
5448 /*
5449 * Pop the prior state if needed.
5450 */
5451 if (isCommit)
5452 {
5454 /* If we saved a prior state, we don't need it anymore */
5456 if (state != NULL)
5457 pfree(state);
5458 /* this avoids double pfree if error later: */
5459 afterTriggers.trans_stack[my_level].state = NULL;
5462 }
5463 else
5464 {
5465 /*
5466 * Aborting. It is possible subxact start failed before calling
5467 * AfterTriggerBeginSubXact, in which case we mustn't risk touching
5468 * trans_stack levels that aren't there.
5469 */
5470 if (my_level >= afterTriggers.maxtransdepth)
5471 return;
5472
5473 /*
5474 * Release query-level storage for queries being aborted, and restore
5475 * query_depth to its pre-subxact value. This assumes that a
5476 * subtransaction will not add events to query levels started in a
5477 * earlier transaction state.
5478 */
5480 {
5484 }
5487
5488 /*
5489 * Restore the global deferred-event list to its former length,
5490 * discarding any events queued by the subxact.
5491 */
5493 &afterTriggers.trans_stack[my_level].events);
5494
5495 /*
5496 * Restore the trigger state. If the saved state is NULL, then this
5497 * subxact didn't save it, so it doesn't need restoring.
5498 */
5500 if (state != NULL)
5501 {
5504 }
5505 /* this avoids double pfree if error later: */
5506 afterTriggers.trans_stack[my_level].state = NULL;
5507
5508 /*
5509 * Scan for any remaining deferred events that were marked DONE or IN
5510 * PROGRESS by this subxact or a child, and un-mark them. We can
5511 * recognize such events because they have a firing ID greater than or
5512 * equal to the firing_counter value we saved at subtransaction start.
5513 * (This essentially assumes that the current subxact includes all
5514 * subxacts started after it.)
5515 */
5518 {
5520
5521 if (event->ate_flags &
5523 {
5524 if (evtshared->ats_firing_id >= subxact_firing_id)
5525 event->ate_flags &=
5527 }
5528 }
5529 }
5530}
void pfree(void *pointer)
Definition mcxt.c:1616
TriggerFlags ate_flags
Definition trigger.c:3710
#define AFTER_TRIGGER_IN_PROGRESS
Definition trigger.c:3685
#define GetTriggerSharedData(evt)
Definition trigger.c:3753
static void afterTriggerRestoreEventList(AfterTriggerEventList *events, const AfterTriggerEventList *old_events)
Definition trigger.c:4227
#define AFTER_TRIGGER_DONE
Definition trigger.c:3684
#define for_each_event_chunk(eptr, cptr, evtlist)
Definition trigger.c:3790

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

5345{
5346 /*
5347 * Forget the pending-events list.
5348 *
5349 * Since all the info is in TopTransactionContext or children thereof, we
5350 * don't really need to do anything to reclaim memory. However, the
5351 * pending-events list could be large, and so it's useful to discard it as
5352 * soon as possible --- especially if we are aborting because we ran out
5353 * of memory for the list!
5354 */
5356 {
5362 }
5363
5364 /*
5365 * Forget any subtransaction state as well. Since this can't be very
5366 * large, we let the eventual reset of TopTransactionContext free the
5367 * memory instead of doing it here.
5368 */
5371
5372
5373 /*
5374 * Forget the query stack and constraint-related state information. As
5375 * with the subtransaction state information, we don't bother freeing the
5376 * memory here.
5377 */
5381
5382 /* No more afterTriggers manipulation until next transaction starts. */
5384}
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
AfterTriggerEventChunk * tail
Definition trigger.c:3778

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

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

◆ AfterTriggerFireDeferred()

void AfterTriggerFireDeferred ( void  )
extern

Definition at line 5288 of file trigger.c.

5289{
5290 AfterTriggerEventList *events;
5291 bool snap_pushed = false;
5292
5293 /* Must not be inside a query */
5295
5296 /*
5297 * If there are any triggers to fire, make sure we have set a snapshot for
5298 * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
5299 * can't assume ActiveSnapshot is valid on entry.)
5300 */
5301 events = &afterTriggers.events;
5302 if (events->head != NULL)
5303 {
5305 snap_pushed = true;
5306 }
5307
5308 /*
5309 * Run all the remaining triggers. Loop until they are all gone, in case
5310 * some trigger queues more for us to do.
5311 */
5312 while (afterTriggerMarkEvents(events, NULL, false))
5313 {
5315
5316 if (afterTriggerInvokeEvents(events, firing_id, NULL, true))
5317 break; /* all fired */
5318 }
5319
5320 /*
5321 * We don't bother freeing the event list, since it will go away anyway
5322 * (and more efficiently than via pfree) in AfterTriggerEndXact.
5323 */
5324
5325 if (snap_pushed)
5327}
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::events, fb(), AfterTriggersData::firing_counter, GetTransactionSnapshot(), AfterTriggerEventList::head, PopActiveSnapshot(), PushActiveSnapshot(), and AfterTriggersData::query_depth.

Referenced by CommitTransaction(), and PrepareTransaction().

◆ AfterTriggerPendingOnRel()

bool AfterTriggerPendingOnRel ( Oid  relid)
extern

Definition at line 6083 of file trigger.c.

6084{
6085 AfterTriggerEvent event;
6087 int depth;
6088
6089 /* Scan queued events */
6091 {
6093
6094 /*
6095 * We can ignore completed events. (Even if a DONE flag is rolled
6096 * back by subxact abort, it's OK because the effects of the TRUNCATE
6097 * or whatever must get rolled back too.)
6098 */
6099 if (event->ate_flags & AFTER_TRIGGER_DONE)
6100 continue;
6101
6102 if (evtshared->ats_relid == relid)
6103 return true;
6104 }
6105
6106 /*
6107 * Also scan events queued by incomplete queries. This could only matter
6108 * if TRUNCATE/etc is executed by a function or trigger within an updating
6109 * query on the same relation, which is pretty perverse, but let's check.
6110 */
6111 for (depth = 0; depth <= afterTriggers.query_depth && depth < afterTriggers.maxquerydepth; depth++)
6112 {
6114 {
6116
6117 if (event->ate_flags & AFTER_TRIGGER_DONE)
6118 continue;
6119
6120 if (evtshared->ats_relid == relid)
6121 return true;
6122 }
6123 }
6124
6125 return false;
6126}
AfterTriggerEventList events
Definition trigger.c:3900

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

5769{
5770 int my_level = GetCurrentTransactionNestLevel();
5771
5772 /* If we haven't already done so, initialize our state. */
5773 if (afterTriggers.state == NULL)
5775
5776 /*
5777 * If in a subtransaction, and we didn't save the current state already,
5778 * save it so it can be restored if the subtransaction aborts.
5779 */
5780 if (my_level > 1 &&
5781 afterTriggers.trans_stack[my_level].state == NULL)
5782 {
5783 afterTriggers.trans_stack[my_level].state =
5785 }
5786
5787 /*
5788 * Handle SET CONSTRAINTS ALL ...
5789 */
5790 if (stmt->constraints == NIL)
5791 {
5792 /*
5793 * Forget any previous SET CONSTRAINTS commands in this transaction.
5794 */
5796
5797 /*
5798 * Set the per-transaction ALL state to known.
5799 */
5802 }
5803 else
5804 {
5807 List *conoidlist = NIL;
5808 List *tgoidlist = NIL;
5809 ListCell *lc;
5810
5811 /*
5812 * Handle SET CONSTRAINTS constraint-name [, ...]
5813 *
5814 * First, identify all the named constraints and make a list of their
5815 * OIDs. Since, unlike the SQL spec, we allow multiple constraints of
5816 * the same name within a schema, the specifications are not
5817 * necessarily unique. Our strategy is to target all matching
5818 * constraints within the first search-path schema that has any
5819 * matches, but disregard matches in schemas beyond the first match.
5820 * (This is a bit odd but it's the historical behavior.)
5821 *
5822 * A constraint in a partitioned table may have corresponding
5823 * constraints in the partitions. Grab those too.
5824 */
5826
5827 foreach(lc, stmt->constraints)
5828 {
5829 RangeVar *constraint = lfirst(lc);
5830 bool found;
5832 ListCell *nslc;
5833
5834 if (constraint->catalogname)
5835 {
5836 if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
5837 ereport(ERROR,
5839 errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
5840 constraint->catalogname, constraint->schemaname,
5841 constraint->relname)));
5842 }
5843
5844 /*
5845 * If we're given the schema name with the constraint, look only
5846 * in that schema. If given a bare constraint name, use the
5847 * search path to find the first matching constraint.
5848 */
5849 if (constraint->schemaname)
5850 {
5852 false);
5853
5855 }
5856 else
5857 {
5859 }
5860
5861 found = false;
5862 foreach(nslc, namespacelist)
5863 {
5866 ScanKeyData skey[2];
5867 HeapTuple tup;
5868
5869 ScanKeyInit(&skey[0],
5872 CStringGetDatum(constraint->relname));
5873 ScanKeyInit(&skey[1],
5877
5879 true, NULL, 2, skey);
5880
5882 {
5884
5885 if (con->condeferrable)
5886 conoidlist = lappend_oid(conoidlist, con->oid);
5887 else if (stmt->deferred)
5888 ereport(ERROR,
5890 errmsg("constraint \"%s\" is not deferrable",
5891 constraint->relname)));
5892 found = true;
5893 }
5894
5896
5897 /*
5898 * Once we've found a matching constraint we do not search
5899 * later parts of the search path.
5900 */
5901 if (found)
5902 break;
5903 }
5904
5906
5907 /*
5908 * Not found ?
5909 */
5910 if (!found)
5911 ereport(ERROR,
5913 errmsg("constraint \"%s\" does not exist",
5914 constraint->relname)));
5915 }
5916
5917 /*
5918 * Scan for any possible descendants of the constraints. We append
5919 * whatever we find to the same list that we're scanning; this has the
5920 * effect that we create new scans for those, too, so if there are
5921 * further descendents, we'll also catch them.
5922 */
5923 foreach(lc, conoidlist)
5924 {
5925 Oid parent = lfirst_oid(lc);
5927 SysScanDesc scan;
5928 HeapTuple tuple;
5929
5930 ScanKeyInit(&key,
5933 ObjectIdGetDatum(parent));
5934
5935 scan = systable_beginscan(conrel, ConstraintParentIndexId, true, NULL, 1, &key);
5936
5937 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
5938 {
5940
5941 conoidlist = lappend_oid(conoidlist, con->oid);
5942 }
5943
5944 systable_endscan(scan);
5945 }
5946
5948
5949 /*
5950 * Now, locate the trigger(s) implementing each of these constraints,
5951 * and make a list of their OIDs.
5952 */
5954
5955 foreach(lc, conoidlist)
5956 {
5957 Oid conoid = lfirst_oid(lc);
5960 HeapTuple htup;
5961
5965 ObjectIdGetDatum(conoid));
5966
5968 NULL, 1, &skey);
5969
5971 {
5973
5974 /*
5975 * Silently skip triggers that are marked as non-deferrable in
5976 * pg_trigger. This is not an error condition, since a
5977 * deferrable RI constraint may have some non-deferrable
5978 * actions.
5979 */
5980 if (pg_trigger->tgdeferrable)
5982 }
5983
5985 }
5986
5988
5989 /*
5990 * Now we can set the trigger states of individual triggers for this
5991 * xact.
5992 */
5993 foreach(lc, tgoidlist)
5994 {
5995 Oid tgoid = lfirst_oid(lc);
5997 bool found = false;
5998 int i;
5999
6000 for (i = 0; i < state->numstates; i++)
6001 {
6002 if (state->trigstates[i].sct_tgoid == tgoid)
6003 {
6004 state->trigstates[i].sct_tgisdeferred = stmt->deferred;
6005 found = true;
6006 break;
6007 }
6008 }
6009 if (!found)
6010 {
6012 SetConstraintStateAddItem(state, tgoid, stmt->deferred);
6013 }
6014 }
6015 }
6016
6017 /*
6018 * SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
6019 * checks against that constraint must be made when the SET CONSTRAINTS
6020 * command is executed -- i.e. the effects of the SET CONSTRAINTS command
6021 * apply retroactively. We've updated the constraints state, so scan the
6022 * list of previously deferred events to fire any that have now become
6023 * immediate.
6024 *
6025 * Obviously, if this was SET ... DEFERRED then it can't have converted
6026 * any unfired events to immediate, so we need do nothing in that case.
6027 */
6028 if (!stmt->deferred)
6029 {
6031 bool snapshot_set = false;
6032
6033 while (afterTriggerMarkEvents(events, NULL, true))
6034 {
6036
6037 /*
6038 * Make sure a snapshot has been established in case trigger
6039 * functions need one. Note that we avoid setting a snapshot if
6040 * we don't find at least one trigger that has to be fired now.
6041 * This is so that BEGIN; SET CONSTRAINTS ...; SET TRANSACTION
6042 * ISOLATION LEVEL SERIALIZABLE; ... works properly. (If we are
6043 * at the start of a transaction it's not possible for any trigger
6044 * events to be queued yet.)
6045 */
6046 if (!snapshot_set)
6047 {
6049 snapshot_set = true;
6050 }
6051
6052 /*
6053 * We can delete fired events if we are at top transaction level,
6054 * but we'd better not if inside a subtransaction, since the
6055 * subtransaction could later get rolled back.
6056 */
6058 !IsSubTransaction()))
6059 break; /* all fired */
6060 }
6061
6062 if (snapshot_set)
6064 }
6065}
int errcode(int sqlerrcode)
Definition elog.c:874
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:150
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
Oid MyDatabaseId
Definition globals.c:94
#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:1312
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 NIL
Definition pg_list.h:68
#define list_make1_oid(x1)
Definition pg_list.h:242
#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:370
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:5718
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state, Oid tgoid, bool tgisdeferred)
Definition trigger.c:5738
static SetConstraintState SetConstraintStateCreate(int numalloc)
Definition trigger.c:5693
bool IsSubTransaction(void)
Definition xact.c:5067

References AccessShareLock, afterTriggerInvokeEvents(), afterTriggerMarkEvents(), afterTriggers, SetConstraintStateData::all_isdeferred, SetConstraintStateData::all_isset, BTEqualStrategyNumber, RangeVar::catalogname, CStringGetDatum(), ereport, errcode(), errmsg, ERROR, AfterTriggersData::events, fb(), fetch_search_path(), AfterTriggersData::firing_counter, Form_pg_constraint, Form_pg_trigger, get_database_name(), GetCurrentTransactionNestLevel(), GETSTRUCT(), GetTransactionSnapshot(), HeapTupleIsValid, i, IsSubTransaction(), lappend_oid(), lfirst, lfirst_oid, list_free(), list_make1_oid, LookupExplicitNamespace(), MyDatabaseId, NIL, SetConstraintStateData::numstates, ObjectIdGetDatum(), PopActiveSnapshot(), PushActiveSnapshot(), RangeVar::relname, ScanKeyInit(), RangeVar::schemaname, 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().

◆ CopyTriggerDesc()

TriggerDesc * CopyTriggerDesc ( TriggerDesc trigdesc)
extern

Definition at line 2092 of file trigger.c.

2093{
2096 int i;
2097
2098 if (trigdesc == NULL || trigdesc->numtriggers <= 0)
2099 return NULL;
2100
2102 memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
2103
2104 trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
2105 memcpy(trigger, trigdesc->triggers,
2106 trigdesc->numtriggers * sizeof(Trigger));
2107 newdesc->triggers = trigger;
2108
2109 for (i = 0; i < trigdesc->numtriggers; i++)
2110 {
2111 trigger->tgname = pstrdup(trigger->tgname);
2112 if (trigger->tgnattr > 0)
2113 {
2114 int16 *newattr;
2115
2116 newattr = (int16 *) palloc(trigger->tgnattr * sizeof(int16));
2117 memcpy(newattr, trigger->tgattr,
2118 trigger->tgnattr * sizeof(int16));
2119 trigger->tgattr = newattr;
2120 }
2121 if (trigger->tgnargs > 0)
2122 {
2123 char **newargs;
2124 int16 j;
2125
2126 newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
2127 for (j = 0; j < trigger->tgnargs; j++)
2128 newargs[j] = pstrdup(trigger->tgargs[j]);
2129 trigger->tgargs = newargs;
2130 }
2131 if (trigger->tgqual)
2132 trigger->tgqual = pstrdup(trigger->tgqual);
2133 if (trigger->tgoldtable)
2134 trigger->tgoldtable = pstrdup(trigger->tgoldtable);
2135 if (trigger->tgnewtable)
2136 trigger->tgnewtable = pstrdup(trigger->tgnewtable);
2137 trigger++;
2138 }
2139
2140 return newdesc;
2141}
int16_t int16
Definition c.h:613
#define palloc_object(type)
Definition fe_memutils.h:74
int j
Definition isn.c:78
char * pstrdup(const char *in)
Definition mcxt.c:1781
void * palloc(Size size)
Definition mcxt.c:1387
int numtriggers
Definition reltrigger.h:50
Trigger * triggers
Definition reltrigger.h:49

References fb(), i, j, 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
320 errmsg("permission denied: \"%s\" is a system catalog",
322
323 if (stmt->isconstraint)
324 {
325 /*
326 * We must take a lock on the target relation to protect against
327 * concurrent drop. It's not clear that AccessShareLock is strong
328 * enough, but we certainly need at least that much... otherwise, we
329 * might end up creating a pg_constraint entry referencing a
330 * nonexistent table.
331 */
333 {
336 }
337 else if (stmt->constrrel != NULL)
339 false);
340 }
341
342 /* permission checks */
343 if (!isInternal)
344 {
347 if (aclresult != ACLCHECK_OK)
350
352 {
355 if (aclresult != ACLCHECK_OK)
358 }
359 }
360
361 /*
362 * When called on a partitioned table to create a FOR EACH ROW trigger
363 * that's not internal, we create one trigger for each partition, too.
364 *
365 * For that, we'd better hold lock on all of them ahead of time.
366 */
367 partition_recurse = !isInternal && stmt->row &&
368 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
372
373 /* Compute tgtype */
374 TRIGGER_CLEAR_TYPE(tgtype);
375 if (stmt->row)
376 TRIGGER_SETT_ROW(tgtype);
377 tgtype |= stmt->timing;
378 tgtype |= stmt->events;
379
380 /* Disallow ROW-level TRUNCATE triggers */
381 if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype))
384 errmsg("TRUNCATE FOR EACH ROW triggers are not supported")));
385
386 /* INSTEAD triggers must be row-level, and can't have WHEN or columns */
387 if (TRIGGER_FOR_INSTEAD(tgtype))
388 {
389 if (!TRIGGER_FOR_ROW(tgtype))
392 errmsg("INSTEAD OF triggers must be FOR EACH ROW")));
393 if (stmt->whenClause)
396 errmsg("INSTEAD OF triggers cannot have WHEN conditions")));
397 if (stmt->columns != NIL)
400 errmsg("INSTEAD OF triggers cannot have column lists")));
401 }
402
403 /*
404 * We don't yet support naming ROW transition variables, but the parser
405 * recognizes the syntax so we can give a nicer message here.
406 *
407 * Per standard, REFERENCING TABLE names are only allowed on AFTER
408 * triggers. Per standard, REFERENCING ROW names are not allowed with FOR
409 * EACH STATEMENT. Per standard, each OLD/NEW, ROW/TABLE permutation is
410 * only allowed once. Per standard, OLD may not be specified when
411 * creating a trigger only for INSERT, and NEW may not be specified when
412 * creating a trigger only for DELETE.
413 *
414 * Notice that the standard allows an AFTER ... FOR EACH ROW trigger to
415 * reference both ROW and TABLE transition data.
416 */
417 if (stmt->transitionRels != NIL)
418 {
419 List *varList = stmt->transitionRels;
420 ListCell *lc;
421
422 foreach(lc, varList)
423 {
425
426 if (!(tt->isTable))
429 errmsg("ROW variable naming in the REFERENCING clause is not supported"),
430 errhint("Use OLD TABLE or NEW TABLE for naming transition tables.")));
431
432 /*
433 * Because of the above test, we omit further ROW-related testing
434 * below. If we later allow naming OLD and NEW ROW variables,
435 * adjustments will be needed below.
436 */
437
438 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
441 errmsg("\"%s\" is a foreign table",
443 errdetail("Triggers on foreign tables cannot have transition tables.")));
444
445 if (rel->rd_rel->relkind == RELKIND_VIEW)
448 errmsg("\"%s\" is a view",
450 errdetail("Triggers on views cannot have transition tables.")));
451
452 /*
453 * We currently don't allow row-level triggers with transition
454 * tables on partition or inheritance children. Such triggers
455 * would somehow need to see tuples converted to the format of the
456 * table they're attached to, and it's not clear which subset of
457 * tuples each child should see. See also the prohibitions in
458 * ATExecAttachPartition() and ATExecAddInherit().
459 */
460 if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id))
461 {
462 /* Use appropriate error message. */
463 if (rel->rd_rel->relispartition)
466 errmsg("ROW triggers with transition tables are not supported on partitions")));
467 else
470 errmsg("ROW triggers with transition tables are not supported on inheritance children")));
471 }
472
473 if (stmt->timing != TRIGGER_TYPE_AFTER)
476 errmsg("transition table name can only be specified for an AFTER trigger")));
477
478 if (TRIGGER_FOR_TRUNCATE(tgtype))
481 errmsg("TRUNCATE triggers with transition tables are not supported")));
482
483 /*
484 * We currently don't allow multi-event triggers ("INSERT OR
485 * UPDATE") with transition tables, because it's not clear how to
486 * handle INSERT ... ON CONFLICT statements which can fire both
487 * INSERT and UPDATE triggers. We show the inserted tuples to
488 * INSERT triggers and the updated tuples to UPDATE triggers, but
489 * it's not yet clear what INSERT OR UPDATE trigger should see.
490 * This restriction could be lifted if we can decide on the right
491 * semantics in a later release.
492 */
493 if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) +
494 (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) +
495 (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1)
498 errmsg("transition tables cannot be specified for triggers with more than one event")));
499
500 /*
501 * We currently don't allow column-specific triggers with
502 * transition tables. Per spec, that seems to require
503 * accumulating separate transition tables for each combination of
504 * columns, which is a lot of work for a rather marginal feature.
505 */
506 if (stmt->columns != NIL)
509 errmsg("transition tables cannot be specified for triggers with column lists")));
510
511 /*
512 * We disallow constraint triggers with transition tables, to
513 * protect the assumption that such triggers can't be deferred.
514 * See notes with AfterTriggers data structures, below.
515 *
516 * Currently this is enforced by the grammar, so just Assert here.
517 */
518 Assert(!stmt->isconstraint);
519
520 if (tt->isNew)
521 {
522 if (!(TRIGGER_FOR_INSERT(tgtype) ||
523 TRIGGER_FOR_UPDATE(tgtype)))
526 errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger")));
527
528 if (newtablename != NULL)
531 errmsg("NEW TABLE cannot be specified multiple times")));
532
533 newtablename = tt->name;
534 }
535 else
536 {
537 if (!(TRIGGER_FOR_DELETE(tgtype) ||
538 TRIGGER_FOR_UPDATE(tgtype)))
541 errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger")));
542
543 if (oldtablename != NULL)
546 errmsg("OLD TABLE cannot be specified multiple times")));
547
548 oldtablename = tt->name;
549 }
550 }
551
552 if (newtablename != NULL && oldtablename != NULL &&
556 errmsg("OLD TABLE name and NEW TABLE name cannot be the same")));
557 }
558
559 /*
560 * Parse the WHEN clause, if any and we weren't passed an already
561 * transformed one.
562 *
563 * Note that as a side effect, we fill whenRtable when parsing. If we got
564 * an already parsed clause, this does not occur, which is what we want --
565 * no point in adding redundant dependencies below.
566 */
567 if (!whenClause && stmt->whenClause)
568 {
569 ParseState *pstate;
571 List *varList;
572 ListCell *lc;
573
574 /* Set up a pstate to parse with */
575 pstate = make_parsestate(NULL);
576 pstate->p_sourcetext = queryString;
577
578 /*
579 * Set up nsitems for OLD and NEW references.
580 *
581 * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2.
582 */
585 makeAlias("old", NIL),
586 false, false);
587 addNSItemToQuery(pstate, nsitem, false, true, true);
590 makeAlias("new", NIL),
591 false, false);
592 addNSItemToQuery(pstate, nsitem, false, true, true);
593
594 /* Transform expression. Copy to be sure we don't modify original */
595 whenClause = transformWhereClause(pstate,
596 copyObject(stmt->whenClause),
598 "WHEN");
599 /* we have to fix its collations too */
600 assign_expr_collations(pstate, whenClause);
601
602 /*
603 * Check for disallowed references to OLD/NEW.
604 *
605 * NB: pull_var_clause is okay here only because we don't allow
606 * subselects in WHEN clauses; it would fail to examine the contents
607 * of subselects.
608 */
609 varList = pull_var_clause(whenClause, 0);
610 foreach(lc, varList)
611 {
612 Var *var = (Var *) lfirst(lc);
613
614 switch (var->varno)
615 {
616 case PRS2_OLD_VARNO:
617 if (!TRIGGER_FOR_ROW(tgtype))
620 errmsg("statement trigger's WHEN condition cannot reference column values"),
621 parser_errposition(pstate, var->location)));
622 if (TRIGGER_FOR_INSERT(tgtype))
625 errmsg("INSERT trigger's WHEN condition cannot reference OLD values"),
626 parser_errposition(pstate, var->location)));
627 /* system columns are okay here */
628 break;
629 case PRS2_NEW_VARNO:
630 if (!TRIGGER_FOR_ROW(tgtype))
633 errmsg("statement trigger's WHEN condition cannot reference column values"),
634 parser_errposition(pstate, var->location)));
635 if (TRIGGER_FOR_DELETE(tgtype))
638 errmsg("DELETE trigger's WHEN condition cannot reference NEW values"),
639 parser_errposition(pstate, var->location)));
640 if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype))
643 errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"),
644 parser_errposition(pstate, var->location)));
645 if (TRIGGER_FOR_BEFORE(tgtype) &&
646 var->varattno == 0 &&
647 RelationGetDescr(rel)->constr &&
648 (RelationGetDescr(rel)->constr->has_generated_stored ||
649 RelationGetDescr(rel)->constr->has_generated_virtual))
652 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
653 errdetail("A whole-row reference is used and the table contains generated columns."),
654 parser_errposition(pstate, var->location)));
655 if (TRIGGER_FOR_BEFORE(tgtype) &&
656 var->varattno > 0 &&
657 TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attgenerated)
660 errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"),
661 errdetail("Column \"%s\" is a generated column.",
662 NameStr(TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attname)),
663 parser_errposition(pstate, var->location)));
664 break;
665 default:
666 /* can't happen without add_missing_from, so just elog */
667 elog(ERROR, "trigger WHEN condition cannot contain references to other relations");
668 break;
669 }
670 }
671
672 /* we'll need the rtable for recordDependencyOnExpr */
673 whenRtable = pstate->p_rtable;
674
675 qual = nodeToString(whenClause);
676
677 free_parsestate(pstate);
678 }
679 else if (!whenClause)
680 {
681 whenClause = NULL;
682 whenRtable = NIL;
683 qual = NULL;
684 }
685 else
686 {
687 qual = nodeToString(whenClause);
688 whenRtable = NIL;
689 }
690
691 /*
692 * Find and validate the trigger function.
693 */
694 if (!OidIsValid(funcoid))
695 funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
696 if (!isInternal)
697 {
699 if (aclresult != ACLCHECK_OK)
701 NameListToString(stmt->funcname));
702 }
704 if (funcrettype != TRIGGEROID)
707 errmsg("function %s must return type %s",
708 NameListToString(stmt->funcname), "trigger")));
709
710 /*
711 * Scan pg_trigger to see if there is already a trigger of the same name.
712 * Skip this for internally generated triggers, since we'll modify the
713 * name to be unique below.
714 *
715 * NOTE that this is cool only because we have ShareRowExclusiveLock on
716 * the relation, so the trigger set won't be changing underneath us.
717 */
719 if (!isInternal)
720 {
723
724 ScanKeyInit(&skeys[0],
728
729 ScanKeyInit(&skeys[1],
732 CStringGetDatum(stmt->trigname));
733
735 NULL, 2, skeys);
736
737 /* There should be at most one matching tuple */
739 {
741
742 trigoid = oldtrigger->oid;
743 existing_constraint_oid = oldtrigger->tgconstraint;
744 existing_isInternal = oldtrigger->tgisinternal;
746 trigger_exists = true;
747 /* copy the tuple to use in CatalogTupleUpdate() */
748 tuple = heap_copytuple(tuple);
749 }
751 }
752
753 if (!trigger_exists)
754 {
755 /* Generate the OID for the new trigger. */
758 }
759 else
760 {
761 /*
762 * If OR REPLACE was specified, we'll replace the old trigger;
763 * otherwise complain about the duplicate name.
764 */
765 if (!stmt->replace)
768 errmsg("trigger \"%s\" for relation \"%s\" already exists",
769 stmt->trigname, RelationGetRelationName(rel))));
770
771 /*
772 * An internal trigger or a child trigger (isClone) cannot be replaced
773 * by a user-defined trigger. However, skip this test when
774 * in_partition, because then we're recursing from a partitioned table
775 * and the check was made at the parent level.
776 */
781 errmsg("trigger \"%s\" for relation \"%s\" is an internal or a child trigger",
782 stmt->trigname, RelationGetRelationName(rel))));
783
784 /*
785 * It is not allowed to replace with a constraint trigger; gram.y
786 * should have enforced this already.
787 */
788 Assert(!stmt->isconstraint);
789
790 /*
791 * It is not allowed to replace an existing constraint trigger,
792 * either. (The reason for these restrictions is partly that it seems
793 * difficult to deal with pending trigger events in such cases, and
794 * partly that the command might imply changing the constraint's
795 * properties as well, which doesn't seem nice.)
796 */
800 errmsg("trigger \"%s\" for relation \"%s\" is a constraint trigger",
801 stmt->trigname, RelationGetRelationName(rel))));
802 }
803
804 /*
805 * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
806 * corresponding pg_constraint entry.
807 */
808 if (stmt->isconstraint && !OidIsValid(constraintOid))
809 {
810 /* Internal callers should have made their own constraints */
815 stmt->deferrable,
816 stmt->initdeferred,
817 true, /* Is Enforced */
818 true,
819 InvalidOid, /* no parent */
820 RelationGetRelid(rel),
821 NULL, /* no conkey */
822 0,
823 0,
824 InvalidOid, /* no domain */
825 InvalidOid, /* no index */
826 InvalidOid, /* no foreign key */
827 NULL,
828 NULL,
829 NULL,
830 NULL,
831 0,
832 ' ',
833 ' ',
834 NULL,
835 0,
836 ' ',
837 NULL, /* no exclusion */
838 NULL, /* no check constraint */
839 NULL,
840 true, /* islocal */
841 0, /* inhcount */
842 true, /* noinherit */
843 false, /* conperiod */
844 isInternal); /* is_internal */
845 }
846
847 /*
848 * If trigger is internally generated, modify the provided trigger name to
849 * ensure uniqueness by appending the trigger OID. (Callers will usually
850 * supply a simple constant trigger name in these cases.)
851 */
852 if (isInternal)
853 {
855 "%s_%u", stmt->trigname, trigoid);
856 trigname = internaltrigname;
857 }
858 else
859 {
860 /* user-defined trigger; use the specified trigger name as-is */
861 trigname = stmt->trigname;
862 }
863
864 /*
865 * Build the new pg_trigger tuple.
866 */
867 memset(nulls, false, sizeof(nulls));
868
873 CStringGetDatum(trigname));
883
884 if (stmt->args)
885 {
886 ListCell *le;
887 char *args;
888 int16 nargs = list_length(stmt->args);
889 int len = 0;
890
891 foreach(le, stmt->args)
892 {
893 char *ar = strVal(lfirst(le));
894
895 len += strlen(ar) + 4;
896 for (; *ar; ar++)
897 {
898 if (*ar == '\\')
899 len++;
900 }
901 }
902 args = (char *) palloc(len + 1);
903 args[0] = '\0';
904 foreach(le, stmt->args)
905 {
906 char *s = strVal(lfirst(le));
907 char *d = args + strlen(args);
908
909 while (*s)
910 {
911 if (*s == '\\')
912 *d++ = '\\';
913 *d++ = *s++;
914 }
915 strcpy(d, "\\000");
916 }
919 CStringGetDatum(args));
920 }
921 else
922 {
925 CStringGetDatum(""));
926 }
927
928 /* build column number array if it's a column-specific trigger */
929 ncolumns = list_length(stmt->columns);
930 if (ncolumns == 0)
931 columns = NULL;
932 else
933 {
934 ListCell *cell;
935 int i = 0;
936
937 columns = (int16 *) palloc(ncolumns * sizeof(int16));
938 foreach(cell, stmt->columns)
939 {
940 char *name = strVal(lfirst(cell));
942 int j;
943
944 /* Lookup column name. System columns are not allowed */
945 attnum = attnameAttNum(rel, name, false);
949 errmsg("column \"%s\" of relation \"%s\" does not exist",
951
952 /* Check for duplicates */
953 for (j = i - 1; j >= 0; j--)
954 {
955 if (columns[j] == attnum)
958 errmsg("column \"%s\" specified more than once",
959 name)));
960 }
961
962 columns[i++] = attnum;
963 }
964 }
965 tgattr = buildint2vector(columns, ncolumns);
967
968 /* set tgqual if trigger has WHEN clause */
969 if (qual)
971 else
972 nulls[Anum_pg_trigger_tgqual - 1] = true;
973
974 if (oldtablename)
977 else
978 nulls[Anum_pg_trigger_tgoldtable - 1] = true;
979 if (newtablename)
982 else
983 nulls[Anum_pg_trigger_tgnewtable - 1] = true;
984
985 /*
986 * Insert or replace tuple in pg_trigger.
987 */
988 if (!trigger_exists)
989 {
990 tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
992 }
993 else
994 {
996
997 newtup = heap_form_tuple(tgrel->rd_att, values, nulls);
1000 }
1001
1002 heap_freetuple(tuple); /* free either original or new tuple */
1004
1008 if (oldtablename)
1010 if (newtablename)
1012
1013 /*
1014 * Update relation's pg_class entry; if necessary; and if not, send an SI
1015 * message to make other backends (and this one) rebuild relcache entries.
1016 */
1020 if (!HeapTupleIsValid(tuple))
1021 elog(ERROR, "cache lookup failed for relation %u",
1022 RelationGetRelid(rel));
1023 if (!((Form_pg_class) GETSTRUCT(tuple))->relhastriggers)
1024 {
1025 ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
1026
1027 CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
1028
1030 }
1031 else
1033
1034 heap_freetuple(tuple);
1036
1037 /*
1038 * If we're replacing a trigger, flush all the old dependencies before
1039 * recording new ones.
1040 */
1041 if (trigger_exists)
1043
1044 /*
1045 * Record dependencies for trigger. Always place a normal dependency on
1046 * the function.
1047 */
1048 myself.classId = TriggerRelationId;
1049 myself.objectId = trigoid;
1050 myself.objectSubId = 0;
1051
1053 referenced.objectId = funcoid;
1054 referenced.objectSubId = 0;
1056
1058 {
1059 /*
1060 * Internally-generated trigger for a constraint, so make it an
1061 * internal dependency of the constraint. We can skip depending on
1062 * the relation(s), as there'll be an indirect dependency via the
1063 * constraint.
1064 */
1066 referenced.objectId = constraintOid;
1067 referenced.objectSubId = 0;
1069 }
1070 else
1071 {
1072 /*
1073 * User CREATE TRIGGER, so place dependencies. We make trigger be
1074 * auto-dropped if its relation is dropped or if the FK relation is
1075 * dropped. (Auto drop is compatible with our pre-7.3 behavior.)
1076 */
1078 referenced.objectId = RelationGetRelid(rel);
1079 referenced.objectSubId = 0;
1081
1083 {
1085 referenced.objectId = constrrelid;
1086 referenced.objectSubId = 0;
1088 }
1089 /* Not possible to have an index dependency in this case */
1090 Assert(!OidIsValid(indexOid));
1091
1092 /*
1093 * If it's a user-specified constraint trigger, make the constraint
1094 * internally dependent on the trigger instead of vice versa.
1095 */
1097 {
1099 referenced.objectId = constraintOid;
1100 referenced.objectSubId = 0;
1102 }
1103
1104 /*
1105 * If it's a partition trigger, create the partition dependencies.
1106 */
1108 {
1113 }
1114 }
1115
1116 /* If column-specific trigger, add normal dependencies on columns */
1117 if (columns != NULL)
1118 {
1119 int i;
1120
1122 referenced.objectId = RelationGetRelid(rel);
1123 for (i = 0; i < ncolumns; i++)
1124 {
1125 referenced.objectSubId = columns[i];
1127 }
1128 }
1129
1130 /*
1131 * If it has a WHEN clause, add dependencies on objects mentioned in the
1132 * expression (eg, functions, as well as any columns used).
1133 */
1134 if (whenRtable != NIL)
1137
1138 /* Post creation hook for new trigger */
1140 isInternal);
1141
1142 /*
1143 * Lastly, create the trigger on child relations, if needed.
1144 */
1146 {
1147 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1148 int i;
1151
1153 "part trig clone",
1155
1156 /*
1157 * We don't currently expect to be called with a valid indexOid. If
1158 * that ever changes then we'll need to write code here to find the
1159 * corresponding child index.
1160 */
1161 Assert(!OidIsValid(indexOid));
1162
1164
1165 /* Iterate to create the trigger on each existing partition */
1166 for (i = 0; i < partdesc->nparts; i++)
1167 {
1170 Node *qual;
1171
1173
1174 /*
1175 * Initialize our fabricated parse node by copying the original
1176 * one, then resetting fields that we pass separately.
1177 */
1179 childStmt->funcname = NIL;
1180 childStmt->whenClause = NULL;
1181
1182 /* If there is a WHEN clause, create a modified copy of it */
1183 qual = copyObject(whenClause);
1184 qual = (Node *)
1186 childTbl, rel);
1187 qual = (Node *)
1189 childTbl, rel);
1190
1191 CreateTriggerFiringOn(childStmt, queryString,
1192 partdesc->oids[i], refRelOid,
1194 funcoid, trigoid, qual,
1196
1198
1200 }
1201
1204 }
1205
1206 /* Keep lock on target rel until end of xact */
1207 table_close(rel, NoLock);
1208
1209 return myself;
1210}
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:3879
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4082
#define InvalidAttrNumber
Definition attnum.h:23
static Datum values[MAXATTR]
Definition bootstrap.c:188
#define CStringGetTextDatum(s)
Definition builtins.h:98
Datum byteain(PG_FUNCTION_ARGS)
Definition bytea.c:202
#define NameStr(name)
Definition c.h:837
#define OidIsValid(objectId)
Definition c.h:860
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:448
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 elog(elevel,...)
Definition elog.h:226
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:684
bool allowSystemTableMods
Definition globals.c:130
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:698
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1037
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1384
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:1669
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:2148
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2223
Oid get_func_rettype(Oid funcid)
Definition lsyscache.c:1875
Alias * makeAlias(const char *aliasname, List *colnames)
Definition makefuncs.c:438
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:403
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#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:232
#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:124
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:77
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:47
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition pg_depend.c:303
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:260
static Datum PointerGetDatum(const void *X)
Definition postgres.h:342
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
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:514
#define RelationGetDescr(relation)
Definition rel.h:540
#define RelationGetRelationName(relation)
Definition rel.h:548
#define RelationGetNamespace(relation)
Definition rel.h:555
#define ERRCODE_DUPLICATE_OBJECT
Definition streamutil.c:30
ItemPointerData t_self
Definition htup.h:65
const char * p_sourcetext
Definition parse_node.h:210
List * p_rtable
Definition parse_node.h:211
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:1102

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

1731{
1733 int nkeys;
1734 ScanKeyData keys[2];
1736 HeapTuple tuple;
1737 bool found;
1738 bool changed;
1739
1740 /* Scan the relevant entries in pg_triggers */
1742
1743 ScanKeyInit(&keys[0],
1747 if (tgname)
1748 {
1749 ScanKeyInit(&keys[1],
1752 CStringGetDatum(tgname));
1753 nkeys = 2;
1754 }
1755 else
1756 nkeys = 1;
1757
1759 NULL, nkeys, keys);
1760
1761 found = changed = false;
1762
1763 while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
1764 {
1766
1767 if (OidIsValid(tgparent) && tgparent != oldtrig->tgparentid)
1768 continue;
1769
1770 if (oldtrig->tgisinternal)
1771 {
1772 /* system trigger ... ok to process? */
1773 if (skip_system)
1774 continue;
1775 if (!superuser())
1776 ereport(ERROR,
1778 errmsg("permission denied: \"%s\" is a system trigger",
1779 NameStr(oldtrig->tgname))));
1780 }
1781
1782 found = true;
1783
1784 if (oldtrig->tgenabled != fires_when)
1785 {
1786 /* need to change this one ... make a copy to scribble on */
1789
1790 newtrig->tgenabled = fires_when;
1791
1793
1795
1796 changed = true;
1797 }
1798
1799 /*
1800 * When altering FOR EACH ROW triggers on a partitioned table, do the
1801 * same on the partitions as well, unless ONLY is specified.
1802 *
1803 * Note that we recurse even if we didn't change the trigger above,
1804 * because the partitions' copy of the trigger may have a different
1805 * value of tgenabled than the parent's trigger and thus might need to
1806 * be changed.
1807 */
1808 if (recurse &&
1809 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
1810 (TRIGGER_FOR_ROW(oldtrig->tgtype)))
1811 {
1812 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
1813 int i;
1814
1815 for (i = 0; i < partdesc->nparts; i++)
1816 {
1817 Relation part;
1818
1819 part = relation_open(partdesc->oids[i], lockmode);
1820 /* Match on child triggers' tgparentid, not their name */
1822 fires_when, skip_system, recurse,
1823 lockmode);
1824 table_close(part, NoLock); /* keep lock till commit */
1825 }
1826 }
1827
1829 oldtrig->oid, 0);
1830 }
1831
1833
1835
1836 if (tgname && !found)
1837 ereport(ERROR,
1839 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1840 tgname, RelationGetRelationName(rel))));
1841
1842 /*
1843 * If we changed anything, broadcast a SI inval message to force each
1844 * backend (including our own!) to rebuild relation's relcache entry.
1845 * Otherwise they will fail to apply the change promptly.
1846 */
1847 if (changed)
1849}
void CacheInvalidateRelcache(Relation relation)
Definition inval.c:1635
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:47
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:1728

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

2809{
2810 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2811
2812 if (relinfo->ri_FdwRoutine && transition_capture &&
2813 transition_capture->tcs_delete_old_table)
2814 {
2815 Assert(relinfo->ri_RootResultRelInfo);
2816 ereport(ERROR,
2818 errmsg("cannot collect transition tuples from child foreign tables")));
2819 }
2820
2821 if ((trigdesc && trigdesc->trig_delete_after_row) ||
2822 (transition_capture && transition_capture->tcs_delete_old_table))
2823 {
2825
2827 if (fdw_trigtuple == NULL)
2828 GetTupleForTrigger(estate,
2829 NULL,
2830 relinfo,
2831 tupleid,
2833 slot,
2834 false,
2835 NULL,
2836 NULL,
2837 NULL);
2838 else
2840
2843 true, slot, NULL, NIL, NULL,
2844 transition_capture,
2846 }
2847}
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
TupleTableSlot * ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
Definition execUtils.c:1209
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:3346
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:6170
#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 2545 of file trigger.c.

2548{
2549 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2550
2551 if (relinfo->ri_FdwRoutine && transition_capture &&
2552 transition_capture->tcs_insert_new_table)
2553 {
2554 Assert(relinfo->ri_RootResultRelInfo);
2555 ereport(ERROR,
2557 errmsg("cannot collect transition tuples from child foreign tables")));
2558 }
2559
2560 if ((trigdesc && trigdesc->trig_insert_after_row) ||
2561 (transition_capture && transition_capture->tcs_insert_new_table))
2564 true, NULL, slot,
2566 transition_capture,
2567 false);
2568}
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 3146 of file trigger.c.

3155{
3156 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3157
3158 if (relinfo->ri_FdwRoutine && transition_capture &&
3159 (transition_capture->tcs_update_old_table ||
3160 transition_capture->tcs_update_new_table))
3161 {
3162 Assert(relinfo->ri_RootResultRelInfo);
3163 ereport(ERROR,
3165 errmsg("cannot collect transition tuples from child foreign tables")));
3166 }
3167
3168 if ((trigdesc && trigdesc->trig_update_after_row) ||
3169 (transition_capture &&
3170 (transition_capture->tcs_update_old_table ||
3171 transition_capture->tcs_update_new_table)))
3172 {
3173 /*
3174 * Note: if the UPDATE is converted into a DELETE+INSERT as part of
3175 * update-partition-key operation, then this function is also called
3176 * separately for DELETE and INSERT to capture transition table rows.
3177 * In such case, either old tuple or new tuple can be NULL.
3178 */
3181
3184
3187
3189 GetTupleForTrigger(estate,
3190 NULL,
3191 tupsrc,
3192 tupleid,
3194 oldslot,
3195 false,
3196 NULL,
3197 NULL,
3198 NULL);
3199 else if (fdw_trigtuple != NULL)
3201 else
3203
3207 true,
3210 transition_capture,
3212 }
3213}
Bitmapset * ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition execUtils.c:1423
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 2683 of file trigger.c.

2685{
2686 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2687
2688 if (trigdesc && trigdesc->trig_delete_after_statement)
2691 false, NULL, NULL, NIL, NULL, transition_capture,
2692 false);
2693}
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 2454 of file trigger.c.

2456{
2457 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2458
2459 if (trigdesc && trigdesc->trig_insert_after_statement)
2462 false, NULL, NULL, NIL, NULL, transition_capture,
2463 false);
2464}
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 3329 of file trigger.c.

3330{
3331 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3332
3333 if (trigdesc && trigdesc->trig_truncate_after_statement)
3335 NULL, NULL,
3337 false, NULL, NULL, NIL, NULL, NULL,
3338 false);
3339}
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 2955 of file trigger.c.

2957{
2958 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2959
2960 /* statement-level triggers operate on the parent table */
2961 Assert(relinfo->ri_RootResultRelInfo == NULL);
2962
2963 if (trigdesc && trigdesc->trig_update_after_statement)
2966 false, NULL, NULL, NIL,
2968 transition_capture,
2969 false);
2970}
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 2703 of file trigger.c.

2711{
2713 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2714 bool result = true;
2717 bool should_free = false;
2718 int i;
2719
2721 if (fdw_trigtuple == NULL)
2722 {
2724
2725 /*
2726 * Get a copy of the on-disk tuple we are planning to delete. In
2727 * general, if the tuple has been concurrently updated, we should
2728 * recheck it using EPQ. However, if this is a MERGE DELETE action,
2729 * we skip this EPQ recheck and leave it to the caller (it must do
2730 * additional rechecking, and might end up executing a different
2731 * action entirely).
2732 */
2733 if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
2735 &epqslot_candidate, tmresult, tmfd))
2736 return false;
2737
2738 /*
2739 * If the tuple was concurrently updated and the caller of this
2740 * function requested for the updated tuple, skip the trigger
2741 * execution.
2742 */
2743 if (epqslot_candidate != NULL && epqslot != NULL)
2744 {
2746 return false;
2747 }
2748
2750 }
2751 else
2752 {
2754 ExecForceStoreHeapTuple(trigtuple, slot, false);
2755 }
2756
2761 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2762 for (i = 0; i < trigdesc->numtriggers; i++)
2763 {
2764 HeapTuple newtuple;
2765 Trigger *trigger = &trigdesc->triggers[i];
2766
2767 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2771 continue;
2772 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2773 NULL, slot, NULL))
2774 continue;
2775
2776 LocTriggerData.tg_trigslot = slot;
2777 LocTriggerData.tg_trigtuple = trigtuple;
2778 LocTriggerData.tg_trigger = trigger;
2780 i,
2781 relinfo->ri_TrigFunctions,
2782 relinfo->ri_TrigInstrument,
2783 GetPerTupleMemoryContext(estate));
2784 if (newtuple == NULL)
2785 {
2786 result = false; /* tell caller to suppress delete */
2787 break;
2788 }
2789 if (newtuple != trigtuple)
2790 heap_freetuple(newtuple);
2791 }
2792 if (should_free)
2794
2795 return result;
2796}
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
#define GetPerTupleMemoryContext(estate)
Definition executor.h:665
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo, Trigger *trigger, TriggerEvent event, Bitmapset *modifiedCols, TupleTableSlot *oldslot, TupleTableSlot *newslot)
Definition trigger.c:3484
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, int tgindx, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context)
Definition trigger.c:2311
#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, 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 2467 of file trigger.c.

2469{
2470 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2471 HeapTuple newtuple = NULL;
2472 bool should_free;
2474 int i;
2475
2480 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2481 for (i = 0; i < trigdesc->numtriggers; i++)
2482 {
2483 Trigger *trigger = &trigdesc->triggers[i];
2484 HeapTuple oldtuple;
2485
2486 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2490 continue;
2491 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2492 NULL, NULL, slot))
2493 continue;
2494
2495 if (!newtuple)
2496 newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2497
2498 LocTriggerData.tg_trigslot = slot;
2499 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2500 LocTriggerData.tg_trigger = trigger;
2502 i,
2503 relinfo->ri_TrigFunctions,
2504 relinfo->ri_TrigInstrument,
2505 GetPerTupleMemoryContext(estate));
2506 if (newtuple == NULL)
2507 {
2508 if (should_free)
2509 heap_freetuple(oldtuple);
2510 return false; /* "do nothing" */
2511 }
2512 else if (newtuple != oldtuple)
2513 {
2514 newtuple = check_modified_virtual_generated(RelationGetDescr(relinfo->ri_RelationDesc), newtuple);
2515
2516 ExecForceStoreHeapTuple(newtuple, slot, false);
2517
2518 /*
2519 * After a tuple in a partition goes through a trigger, the user
2520 * could have changed the partition key enough that the tuple no
2521 * longer fits the partition. Verify that.
2522 */
2523 if (trigger->tgisclone &&
2524 !ExecPartitionCheck(relinfo, slot, estate, false))
2525 ereport(ERROR,
2527 errmsg("moving row to another partition during a BEFORE FOR EACH ROW trigger is not supported"),
2528 errdetail("Before executing trigger \"%s\", the row was to be in partition \"%s.%s\".",
2529 trigger->tgname,
2531 RelationGetRelationName(relinfo->ri_RelationDesc))));
2532
2533 if (should_free)
2534 heap_freetuple(oldtuple);
2535
2536 /* signal tuple should be re-fetched if used */
2537 newtuple = NULL;
2538 }
2539 }
2540
2541 return true;
2542}
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition execMain.c:1875
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3588
NodeTag type
Definition trigger.h:33
static HeapTuple check_modified_virtual_generated(TupleDesc tupdesc, HeapTuple tuple)
Definition trigger.c:6736

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

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

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

2633{
2634 TriggerDesc *trigdesc;
2635 int i;
2637
2638 trigdesc = relinfo->ri_TrigDesc;
2639
2640 if (trigdesc == NULL)
2641 return;
2642 if (!trigdesc->trig_delete_before_statement)
2643 return;
2644
2645 /* no-op if we already fired BS triggers in this context */
2647 CMD_DELETE))
2648 return;
2649
2653 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2654 for (i = 0; i < trigdesc->numtriggers; i++)
2655 {
2656 Trigger *trigger = &trigdesc->triggers[i];
2657 HeapTuple newtuple;
2658
2659 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2663 continue;
2664 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2665 NULL, NULL, NULL))
2666 continue;
2667
2668 LocTriggerData.tg_trigger = trigger;
2670 i,
2671 relinfo->ri_TrigFunctions,
2672 relinfo->ri_TrigInstrument,
2673 GetPerTupleMemoryContext(estate));
2674
2675 if (newtuple)
2676 ereport(ERROR,
2678 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2679 }
2680}
@ CMD_DELETE
Definition nodes.h:278
bool trig_delete_before_statement
Definition reltrigger.h:69
static bool before_stmt_triggers_fired(Oid relid, CmdType cmdType)
Definition trigger.c:6585

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

2404{
2405 TriggerDesc *trigdesc;
2406 int i;
2408
2409 trigdesc = relinfo->ri_TrigDesc;
2410
2411 if (trigdesc == NULL)
2412 return;
2413 if (!trigdesc->trig_insert_before_statement)
2414 return;
2415
2416 /* no-op if we already fired BS triggers in this context */
2418 CMD_INSERT))
2419 return;
2420
2424 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2425 for (i = 0; i < trigdesc->numtriggers; i++)
2426 {
2427 Trigger *trigger = &trigdesc->triggers[i];
2428 HeapTuple newtuple;
2429
2430 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2434 continue;
2435 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2436 NULL, NULL, NULL))
2437 continue;
2438
2439 LocTriggerData.tg_trigger = trigger;
2441 i,
2442 relinfo->ri_TrigFunctions,
2443 relinfo->ri_TrigInstrument,
2444 GetPerTupleMemoryContext(estate));
2445
2446 if (newtuple)
2447 ereport(ERROR,
2449 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2450 }
2451}
@ CMD_INSERT
Definition nodes.h:277
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 3282 of file trigger.c.

3283{
3284 TriggerDesc *trigdesc;
3285 int i;
3287
3288 trigdesc = relinfo->ri_TrigDesc;
3289
3290 if (trigdesc == NULL)
3291 return;
3292 if (!trigdesc->trig_truncate_before_statement)
3293 return;
3294
3298 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3299
3300 for (i = 0; i < trigdesc->numtriggers; i++)
3301 {
3302 Trigger *trigger = &trigdesc->triggers[i];
3303 HeapTuple newtuple;
3304
3305 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3309 continue;
3310 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3311 NULL, NULL, NULL))
3312 continue;
3313
3314 LocTriggerData.tg_trigger = trigger;
3316 i,
3317 relinfo->ri_TrigFunctions,
3318 relinfo->ri_TrigInstrument,
3319 GetPerTupleMemoryContext(estate));
3320
3321 if (newtuple)
3322 ereport(ERROR,
3324 errmsg("BEFORE STATEMENT trigger cannot return a value")));
3325 }
3326}
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 2897 of file trigger.c.

2898{
2899 TriggerDesc *trigdesc;
2900 int i;
2902 Bitmapset *updatedCols;
2903
2904 trigdesc = relinfo->ri_TrigDesc;
2905
2906 if (trigdesc == NULL)
2907 return;
2908 if (!trigdesc->trig_update_before_statement)
2909 return;
2910
2911 /* no-op if we already fired BS triggers in this context */
2913 CMD_UPDATE))
2914 return;
2915
2916 /* statement-level triggers operate on the parent table */
2917 Assert(relinfo->ri_RootResultRelInfo == NULL);
2918
2919 updatedCols = ExecGetAllUpdatedCols(relinfo, estate);
2920
2924 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2925 LocTriggerData.tg_updatedcols = updatedCols;
2926 for (i = 0; i < trigdesc->numtriggers; i++)
2927 {
2928 Trigger *trigger = &trigdesc->triggers[i];
2929 HeapTuple newtuple;
2930
2931 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2935 continue;
2936 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2937 updatedCols, NULL, NULL))
2938 continue;
2939
2940 LocTriggerData.tg_trigger = trigger;
2942 i,
2943 relinfo->ri_TrigFunctions,
2944 relinfo->ri_TrigInstrument,
2945 GetPerTupleMemoryContext(estate));
2946
2947 if (newtuple)
2948 ereport(ERROR,
2950 errmsg("BEFORE STATEMENT trigger cannot return a value")));
2951 }
2952}
@ CMD_UPDATE
Definition nodes.h:276
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 2850 of file trigger.c.

2852{
2853 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2856 int i;
2857
2862 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2863
2864 ExecForceStoreHeapTuple(trigtuple, slot, false);
2865
2866 for (i = 0; i < trigdesc->numtriggers; i++)
2867 {
2869 Trigger *trigger = &trigdesc->triggers[i];
2870
2871 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2875 continue;
2876 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2877 NULL, slot, NULL))
2878 continue;
2879
2880 LocTriggerData.tg_trigslot = slot;
2881 LocTriggerData.tg_trigtuple = trigtuple;
2882 LocTriggerData.tg_trigger = trigger;
2884 i,
2885 relinfo->ri_TrigFunctions,
2886 relinfo->ri_TrigInstrument,
2887 GetPerTupleMemoryContext(estate));
2888 if (rettuple == NULL)
2889 return false; /* Delete was suppressed */
2890 if (rettuple != trigtuple)
2892 }
2893 return true;
2894}
#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 2571 of file trigger.c.

2573{
2574 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
2575 HeapTuple newtuple = NULL;
2576 bool should_free;
2578 int i;
2579
2584 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2585 for (i = 0; i < trigdesc->numtriggers; i++)
2586 {
2587 Trigger *trigger = &trigdesc->triggers[i];
2588 HeapTuple oldtuple;
2589
2590 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
2594 continue;
2595 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
2596 NULL, NULL, slot))
2597 continue;
2598
2599 if (!newtuple)
2600 newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
2601
2602 LocTriggerData.tg_trigslot = slot;
2603 LocTriggerData.tg_trigtuple = oldtuple = newtuple;
2604 LocTriggerData.tg_trigger = trigger;
2606 i,
2607 relinfo->ri_TrigFunctions,
2608 relinfo->ri_TrigInstrument,
2609 GetPerTupleMemoryContext(estate));
2610 if (newtuple == NULL)
2611 {
2612 if (should_free)
2613 heap_freetuple(oldtuple);
2614 return false; /* "do nothing" */
2615 }
2616 else if (newtuple != oldtuple)
2617 {
2618 ExecForceStoreHeapTuple(newtuple, slot, false);
2619
2620 if (should_free)
2621 heap_freetuple(oldtuple);
2622
2623 /* signal tuple should be re-fetched if used */
2624 newtuple = NULL;
2625 }
2626 }
2627
2628 return true;
2629}

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

3218{
3219 TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
3221 HeapTuple newtuple = NULL;
3222 bool should_free;
3224 int i;
3225
3230 LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
3231
3233
3234 for (i = 0; i < trigdesc->numtriggers; i++)
3235 {
3236 Trigger *trigger = &trigdesc->triggers[i];
3237 HeapTuple oldtuple;
3238
3239 if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
3243 continue;
3244 if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
3245 NULL, oldslot, newslot))
3246 continue;
3247
3248 if (!newtuple)
3249 newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free);
3250
3251 LocTriggerData.tg_trigslot = oldslot;
3252 LocTriggerData.tg_trigtuple = trigtuple;
3253 LocTriggerData.tg_newslot = newslot;
3254 LocTriggerData.tg_newtuple = oldtuple = newtuple;
3255
3256 LocTriggerData.tg_trigger = trigger;
3258 i,
3259 relinfo->ri_TrigFunctions,
3260 relinfo->ri_TrigInstrument,
3261 GetPerTupleMemoryContext(estate));
3262 if (newtuple == NULL)
3263 {
3264 return false; /* "do nothing" */
3265 }
3266 else if (newtuple != oldtuple)
3267 {
3268 ExecForceStoreHeapTuple(newtuple, newslot, false);
3269
3270 if (should_free)
3271 heap_freetuple(oldtuple);
3272
3273 /* signal tuple should be re-fetched if used */
3274 newtuple = NULL;
3275 }
3276 }
3277
3278 return true;
3279}

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

2280{
2281 if (trigdesc != NULL)
2282 {
2283 int i;
2284
2285 for (i = 0; i < trigdesc->numtriggers; ++i)
2286 {
2287 Trigger *trigger = &trigdesc->triggers[i];
2288
2289 if (!TRIGGER_FOR_ROW(trigger->tgtype))
2290 continue;
2291 if (trigger->tgoldtable != NULL || trigger->tgnewtable != NULL)
2292 return trigger->tgname;
2293 }
2294 }
2295
2296 return NULL;
2297}
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 2147 of file trigger.c.

2148{
2150 int i;
2151
2152 if (trigdesc == NULL)
2153 return;
2154
2155 trigger = trigdesc->triggers;
2156 for (i = 0; i < trigdesc->numtriggers; i++)
2157 {
2158 pfree(trigger->tgname);
2159 if (trigger->tgnattr > 0)
2160 pfree(trigger->tgattr);
2161 if (trigger->tgnargs > 0)
2162 {
2163 while (--(trigger->tgnargs) >= 0)
2164 pfree(trigger->tgargs[trigger->tgnargs]);
2165 pfree(trigger->tgargs);
2166 }
2167 if (trigger->tgqual)
2168 pfree(trigger->tgqual);
2169 if (trigger->tgoldtable)
2170 pfree(trigger->tgoldtable);
2171 if (trigger->tgnewtable)
2172 pfree(trigger->tgnewtable);
2173 trigger++;
2174 }
2175 pfree(trigdesc->triggers);
2176 pfree(trigdesc);
2177}

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

1373{
1375 ScanKeyData skey[2];
1377 HeapTuple tup;
1378 Oid oid;
1379
1380 /*
1381 * Find the trigger, verify permissions, set up object address
1382 */
1384
1385 ScanKeyInit(&skey[0],
1388 ObjectIdGetDatum(relid));
1389 ScanKeyInit(&skey[1],
1392 CStringGetDatum(trigname));
1393
1395 NULL, 2, skey);
1396
1398
1399 if (!HeapTupleIsValid(tup))
1400 {
1401 if (!missing_ok)
1402 ereport(ERROR,
1404 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1405 trigname, get_rel_name(relid))));
1406 oid = InvalidOid;
1407 }
1408 else
1409 {
1410 oid = ((Form_pg_trigger) GETSTRUCT(tup))->oid;
1411 }
1412
1415 return oid;
1416}

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

4960{
4962 bool need_old_upd,
4971
4972 if (trigdesc == NULL)
4973 return NULL;
4974
4975 /* Detect which table(s) we need. */
4976 switch (cmdType)
4977 {
4978 case CMD_INSERT:
4981 break;
4982 case CMD_UPDATE:
4985 need_old_del = need_new_ins = false;
4986 break;
4987 case CMD_DELETE:
4990 break;
4991 case CMD_MERGE:
4996 break;
4997 default:
4998 elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
4999 /* keep compiler quiet */
5001 break;
5002 }
5004 return NULL;
5005
5006 /* Check state, like AfterTriggerSaveEvent. */
5007 if (afterTriggers.query_depth < 0)
5008 elog(ERROR, "MakeTransitionCaptureState() called outside of query");
5009
5010 /* Be sure we have enough space to record events at this query depth. */
5013
5014 /*
5015 * Find or create AfterTriggersTableData struct(s) to hold the
5016 * tuplestore(s). If there's a matching struct but it's marked closed,
5017 * ignore it; we need a newer one.
5018 *
5019 * Note: MERGE must use the same AfterTriggersTableData structs as INSERT,
5020 * UPDATE, and DELETE, so that any MERGE'd tuples are added to the same
5021 * tuplestores as tuples from any INSERT, UPDATE, or DELETE commands
5022 * running in the same top-level command (e.g., in a writable CTE).
5023 *
5024 * Note: the AfterTriggersTableData list, as well as the tuplestores, are
5025 * allocated in the current (sub)transaction's CurTransactionContext, and
5026 * the tuplestores are managed by the (sub)transaction's resource owner.
5027 * This is sufficient lifespan because we do not allow triggers using
5028 * transition tables to be deferrable; they will be fired during
5029 * AfterTriggerEndQuery, after which it's okay to delete the data.
5030 */
5031 if (need_new_ins)
5033 else
5034 ins_table = NULL;
5035
5038 else
5039 upd_table = NULL;
5040
5041 if (need_old_del)
5043 else
5044 del_table = NULL;
5045
5046 /* Now create required tuplestore(s), if we don't have them already. */
5050
5051 if (need_old_upd && upd_table->old_tuplestore == NULL)
5052 upd_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5053 if (need_new_upd && upd_table->new_tuplestore == NULL)
5054 upd_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5055 if (need_old_del && del_table->old_tuplestore == NULL)
5056 del_table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5057 if (need_new_ins && ins_table->new_tuplestore == NULL)
5058 ins_table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
5059
5062
5063 /* Now build the TransitionCaptureState struct, in caller's context */
5065 state->tcs_delete_old_table = need_old_del;
5066 state->tcs_update_old_table = need_old_upd;
5067 state->tcs_update_new_table = need_new_upd;
5068 state->tcs_insert_new_table = need_new_ins;
5069 state->tcs_insert_private = ins_table;
5070 state->tcs_update_private = upd_table;
5071 state->tcs_delete_private = del_table;
5072
5073 return state;
5074}
#define palloc0_object(type)
Definition fe_memutils.h:75
int work_mem
Definition globals.c:131
MemoryContext CurTransactionContext
Definition mcxt.c:172
@ CMD_MERGE
Definition nodes.h:279
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:4868
static void AfterTriggerEnlargeQueryState(void)
Definition trigger.c:5646
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().

◆ RelationBuildTriggers()

void RelationBuildTriggers ( Relation  relation)
extern

Definition at line 1863 of file trigger.c.

1864{
1865 TriggerDesc *trigdesc;
1866 int numtrigs;
1867 int maxtrigs;
1868 Trigger *triggers;
1872 HeapTuple htup;
1874 int i;
1875
1876 /*
1877 * Allocate a working array to hold the triggers (the array is extended if
1878 * necessary)
1879 */
1880 maxtrigs = 16;
1881 triggers = (Trigger *) palloc(maxtrigs * sizeof(Trigger));
1882 numtrigs = 0;
1883
1884 /*
1885 * Note: since we scan the triggers using TriggerRelidNameIndexId, we will
1886 * be reading the triggers in name order, except possibly during
1887 * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn
1888 * ensures that triggers will be fired in name order.
1889 */
1894
1897 NULL, 1, &skey);
1898
1900 {
1902 Trigger *build;
1903 Datum datum;
1904 bool isnull;
1905
1906 if (numtrigs >= maxtrigs)
1907 {
1908 maxtrigs *= 2;
1909 triggers = (Trigger *) repalloc(triggers, maxtrigs * sizeof(Trigger));
1910 }
1911 build = &(triggers[numtrigs]);
1912
1913 build->tgoid = pg_trigger->oid;
1915 NameGetDatum(&pg_trigger->tgname)));
1916 build->tgfoid = pg_trigger->tgfoid;
1917 build->tgtype = pg_trigger->tgtype;
1918 build->tgenabled = pg_trigger->tgenabled;
1919 build->tgisinternal = pg_trigger->tgisinternal;
1920 build->tgisclone = OidIsValid(pg_trigger->tgparentid);
1921 build->tgconstrrelid = pg_trigger->tgconstrrelid;
1922 build->tgconstrindid = pg_trigger->tgconstrindid;
1923 build->tgconstraint = pg_trigger->tgconstraint;
1924 build->tgdeferrable = pg_trigger->tgdeferrable;
1925 build->tginitdeferred = pg_trigger->tginitdeferred;
1926 build->tgnargs = pg_trigger->tgnargs;
1927 /* tgattr is first var-width field, so OK to access directly */
1928 build->tgnattr = pg_trigger->tgattr.dim1;
1929 if (build->tgnattr > 0)
1930 {
1931 build->tgattr = (int16 *) palloc(build->tgnattr * sizeof(int16));
1932 memcpy(build->tgattr, &(pg_trigger->tgattr.values),
1933 build->tgnattr * sizeof(int16));
1934 }
1935 else
1936 build->tgattr = NULL;
1937 if (build->tgnargs > 0)
1938 {
1939 bytea *val;
1940 char *p;
1941
1944 tgrel->rd_att, &isnull));
1945 if (isnull)
1946 elog(ERROR, "tgargs is null in trigger for relation \"%s\"",
1947 RelationGetRelationName(relation));
1948 p = (char *) VARDATA_ANY(val);
1949 build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
1950 for (i = 0; i < build->tgnargs; i++)
1951 {
1952 build->tgargs[i] = pstrdup(p);
1953 p += strlen(p) + 1;
1954 }
1955 }
1956 else
1957 build->tgargs = NULL;
1958
1960 tgrel->rd_att, &isnull);
1961 if (!isnull)
1962 build->tgoldtable =
1964 else
1965 build->tgoldtable = NULL;
1966
1968 tgrel->rd_att, &isnull);
1969 if (!isnull)
1970 build->tgnewtable =
1972 else
1973 build->tgnewtable = NULL;
1974
1975 datum = fastgetattr(htup, Anum_pg_trigger_tgqual,
1976 tgrel->rd_att, &isnull);
1977 if (!isnull)
1978 build->tgqual = TextDatumGetCString(datum);
1979 else
1980 build->tgqual = NULL;
1981
1982 numtrigs++;
1983 }
1984
1987
1988 /* There might not be any triggers */
1989 if (numtrigs == 0)
1990 {
1991 pfree(triggers);
1992 return;
1993 }
1994
1995 /* Build trigdesc */
1996 trigdesc = palloc0_object(TriggerDesc);
1997 trigdesc->triggers = triggers;
1998 trigdesc->numtriggers = numtrigs;
1999 for (i = 0; i < numtrigs; i++)
2000 SetTriggerFlags(trigdesc, &(triggers[i]));
2001
2002 /* Copy completed trigdesc into cache storage */
2004 relation->trigdesc = CopyTriggerDesc(trigdesc);
2006
2007 /* Release working memory */
2008 FreeTriggerDesc(trigdesc);
2009}
#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:169
Datum nameout(PG_FUNCTION_ARGS)
Definition name.c:71
static char * DatumGetCString(Datum X)
Definition postgres.h:355
static Datum NameGetDatum(const NameData *X)
Definition postgres.h:393
TriggerDesc * trigdesc
Definition rel.h:117
Oid tgoid
Definition reltrigger.h:25
Definition c.h:778
void FreeTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2147
static void SetTriggerFlags(TriggerDesc *trigdesc, Trigger *trigger)
Definition trigger.c:2015
TriggerDesc * CopyTriggerDesc(TriggerDesc *trigdesc)
Definition trigger.c:2092
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, 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 1293 of file trigger.c.

1294{
1297 ScanKeyData skey[1];
1298 HeapTuple tup;
1299 Oid relid;
1300 Relation rel;
1301
1303
1304 /*
1305 * Find the trigger to delete.
1306 */
1307 ScanKeyInit(&skey[0],
1310 ObjectIdGetDatum(trigOid));
1311
1313 NULL, 1, skey);
1314
1316 if (!HeapTupleIsValid(tup))
1317 elog(ERROR, "could not find tuple for trigger %u", trigOid);
1318
1319 /*
1320 * Open and exclusive-lock the relation the trigger belongs to.
1321 */
1322 relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
1323
1324 rel = table_open(relid, AccessExclusiveLock);
1325
1326 if (rel->rd_rel->relkind != RELKIND_RELATION &&
1327 rel->rd_rel->relkind != RELKIND_VIEW &&
1328 rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
1329 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1330 ereport(ERROR,
1332 errmsg("relation \"%s\" cannot have triggers",
1334 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
1335
1337 ereport(ERROR,
1339 errmsg("permission denied: \"%s\" is a system catalog",
1341
1342 /*
1343 * Delete the pg_trigger tuple.
1344 */
1345 CatalogTupleDelete(tgrel, &tup->t_self);
1346
1349
1350 /*
1351 * We do not bother to try to determine whether any other triggers remain,
1352 * which would be needed in order to decide whether it's safe to clear the
1353 * relation's relhastriggers. (In any case, there might be a concurrent
1354 * process adding new triggers.) Instead, just force a relcache inval to
1355 * make other backends (and this one too!) rebuild their relcache entries.
1356 * There's no great harm in leaving relhastriggers true even if there are
1357 * no triggers left.
1358 */
1360
1361 /* Keep lock on trigger's rel until end of xact */
1362 table_close(rel, NoLock);
1363}
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 1469 of file trigger.c.

1470{
1471 Oid tgoid;
1474 HeapTuple tuple;
1476 ScanKeyData key[2];
1477 Oid relid;
1478 ObjectAddress address;
1479
1480 /*
1481 * Look up name, check permissions, and acquire lock (which we will NOT
1482 * release until end of transaction).
1483 */
1485 0,
1487 NULL);
1488
1489 /* Have lock already, so just need to build relcache entry. */
1490 targetrel = relation_open(relid, NoLock);
1491
1492 /*
1493 * On partitioned tables, this operation recurses to partitions. Lock all
1494 * tables upfront.
1495 */
1496 if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1498
1500
1501 /*
1502 * Search for the trigger to modify.
1503 */
1504 ScanKeyInit(&key[0],
1507 ObjectIdGetDatum(relid));
1508 ScanKeyInit(&key[1],
1511 PointerGetDatum(stmt->subname));
1513 NULL, 2, key);
1515 {
1517
1519 tgoid = trigform->oid;
1520
1521 /*
1522 * If the trigger descends from a trigger on a parent partitioned
1523 * table, reject the rename. We don't allow a trigger in a partition
1524 * to differ in name from that of its parent: that would lead to an
1525 * inconsistency that pg_dump would not reproduce.
1526 */
1527 if (OidIsValid(trigform->tgparentid))
1528 ereport(ERROR,
1530 errmsg("cannot rename trigger \"%s\" on table \"%s\"",
1532 errhint("Rename the trigger on the partitioned table \"%s\" instead.",
1533 get_rel_name(get_partition_parent(relid, false))));
1534
1535
1536 /* Rename the trigger on this relation ... */
1537 renametrig_internal(tgrel, targetrel, tuple, stmt->newname,
1538 stmt->subname);
1539
1540 /* ... and if it is partitioned, recurse to its partitions */
1541 if (targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1542 {
1544
1545 for (int i = 0; i < partdesc->nparts; i++)
1546 {
1547 Oid partitionId = partdesc->oids[i];
1548
1550 stmt->newname, stmt->subname);
1551 }
1552 }
1553 }
1554 else
1555 {
1556 ereport(ERROR,
1558 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1560 }
1561
1562 ObjectAddressSet(address, TriggerRelationId, tgoid);
1563
1565
1567
1568 /*
1569 * Close rel, but keep exclusive lock!
1570 */
1572
1573 return address;
1574}
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:205
static void renametrig_internal(Relation tgrel, Relation targetrel, HeapTuple trigtup, const char *newname, const char *expected_name)
Definition trigger.c:1584
static void RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition trigger.c:1422
static void renametrig_partition(Relation tgrel, Oid partitionId, Oid parentTriggerOid, const char *newname, const char *expected_name)
Definition trigger.c:1655

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 1419 of file ri_triggers.c.

1421{
1423 int ri_nullcheck;
1424
1425 /*
1426 * AfterTriggerSaveEvent() handles things such that this function is never
1427 * called for partitioned tables.
1428 */
1429 Assert(fk_rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE);
1430
1432
1434
1435 /*
1436 * If all new key values are NULL, the row satisfies the constraint, so no
1437 * check is needed.
1438 */
1440 return false;
1441
1442 /*
1443 * If some new key values are NULL, the behavior depends on the match
1444 * type.
1445 */
1446 else if (ri_nullcheck == RI_KEYS_SOME_NULL)
1447 {
1448 switch (riinfo->confmatchtype)
1449 {
1451
1452 /*
1453 * If any new key value is NULL, the row must satisfy the
1454 * constraint, so no check is needed.
1455 */
1456 return false;
1457
1459
1460 /*
1461 * Don't know, must run full check.
1462 */
1463 break;
1464
1466
1467 /*
1468 * If some new key values are NULL, the row fails the
1469 * constraint. We must not throw error here, because the row
1470 * might get invalidated before the constraint is to be
1471 * checked, but we should queue the event to apply the check
1472 * later.
1473 */
1474 return true;
1475 }
1476 }
1477
1478 /*
1479 * Continues here for no new key values are NULL, or we couldn't decide
1480 * yet.
1481 */
1482
1483 /*
1484 * If the original row was inserted by our own transaction, we must fire
1485 * the trigger whether or not the keys are equal. This is because our
1486 * UPDATE will invalidate the INSERT so that the INSERT RI trigger will
1487 * not do anything; so we had better do the UPDATE check. (We could skip
1488 * this if we knew the INSERT trigger already fired, but there is no easy
1489 * way to know that.)
1490 */
1492 return true;
1493
1494 /* If all old and new key values are equal, no check is needed */
1495 if (ri_KeysEqual(fk_rel, oldslot, newslot, riinfo, false))
1496 return false;
1497
1498 /* Else we need to fire the trigger. */
1499 return true;
1500}
#define FKCONSTR_MATCH_SIMPLE
#define FKCONSTR_MATCH_PARTIAL
#define FKCONSTR_MATCH_FULL
#define RI_KEYS_SOME_NULL
Definition ri_triggers.c:64
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:63
static const 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 1387 of file ri_triggers.c.

1389{
1391
1393
1394 /*
1395 * If any old key value is NULL, the row could not have been referenced by
1396 * an FK row, so no check is needed.
1397 */
1399 return false;
1400
1401 /* If all old and new key values are equal, no check is needed */
1403 return false;
1404
1405 /* Else we need to fire the trigger. */
1406 return true;
1407}
#define RI_KEYS_NONE_NULL
Definition ri_triggers.c:65

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 3212 of file ri_triggers.c.

3213{
3214 switch (tgfoid)
3215 {
3226 return RI_TRIGGER_PK;
3227
3230 return RI_TRIGGER_FK;
3231 }
3232
3233 return RI_TRIGGER_NONE;
3234}
#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 1520 of file ri_triggers.c.

1521{
1531 List *rtes = NIL;
1532 List *perminfos = NIL;
1533 const char *sep;
1534 const char *fk_only;
1535 const char *pk_only;
1536 int save_nestlevel;
1537 char workmembuf[32];
1538 int spi_result;
1540
1542
1543 /*
1544 * Check to make sure current user has enough permissions to do the test
1545 * query. (If not, caller can fall back to the trigger method, which
1546 * works because it changes user IDs on the fly.)
1547 *
1548 * XXX are there any other show-stopper conditions to check?
1549 */
1552 pk_perminfo->requiredPerms = ACL_SELECT;
1555 rte->rtekind = RTE_RELATION;
1556 rte->relid = RelationGetRelid(pk_rel);
1557 rte->relkind = pk_rel->rd_rel->relkind;
1558 rte->rellockmode = AccessShareLock;
1559 rte->perminfoindex = list_length(perminfos);
1560 rtes = lappend(rtes, rte);
1561
1564 fk_perminfo->requiredPerms = ACL_SELECT;
1567 rte->rtekind = RTE_RELATION;
1568 rte->relid = RelationGetRelid(fk_rel);
1569 rte->relkind = fk_rel->rd_rel->relkind;
1570 rte->rellockmode = AccessShareLock;
1571 rte->perminfoindex = list_length(perminfos);
1572 rtes = lappend(rtes, rte);
1573
1574 for (int i = 0; i < riinfo->nkeys; i++)
1575 {
1576 int attno;
1577
1578 attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
1579 pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno);
1580
1581 attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
1582 fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno);
1583 }
1584
1585 if (!ExecCheckPermissions(rtes, perminfos, false))
1586 return false;
1587
1588 /*
1589 * Also punt if RLS is enabled on either table unless this role has the
1590 * bypassrls right or is the table owner of the table(s) involved which
1591 * have RLS enabled.
1592 */
1594 ((pk_rel->rd_rel->relrowsecurity &&
1596 GetUserId())) ||
1597 (fk_rel->rd_rel->relrowsecurity &&
1599 GetUserId()))))
1600 return false;
1601
1602 /*----------
1603 * The query string built is:
1604 * SELECT fk.keycols FROM [ONLY] relname fk
1605 * LEFT OUTER JOIN [ONLY] pkrelname pk
1606 * ON (pk.pkkeycol1=fk.keycol1 [AND ...])
1607 * WHERE pk.pkkeycol1 IS NULL AND
1608 * For MATCH SIMPLE:
1609 * (fk.keycol1 IS NOT NULL [AND ...])
1610 * For MATCH FULL:
1611 * (fk.keycol1 IS NOT NULL [OR ...])
1612 *
1613 * We attach COLLATE clauses to the operators when comparing columns
1614 * that have different collations.
1615 *----------
1616 */
1618 appendStringInfoString(&querybuf, "SELECT ");
1619 sep = "";
1620 for (int i = 0; i < riinfo->nkeys; i++)
1621 {
1623 RIAttName(fk_rel, riinfo->fk_attnums[i]));
1624 appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
1625 sep = ", ";
1626 }
1627
1630 fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
1631 "" : "ONLY ";
1632 pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
1633 "" : "ONLY ";
1635 " FROM %s%s fk LEFT OUTER JOIN %s%s pk ON",
1637
1638 strcpy(pkattname, "pk.");
1639 strcpy(fkattname, "fk.");
1640 sep = "(";
1641 for (int i = 0; i < riinfo->nkeys; i++)
1642 {
1643 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
1644 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
1645 Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
1646 Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
1647
1649 RIAttName(pk_rel, riinfo->pk_attnums[i]));
1651 RIAttName(fk_rel, riinfo->fk_attnums[i]));
1654 riinfo->pf_eq_oprs[i],
1656 if (pk_coll != fk_coll)
1658 sep = "AND";
1659 }
1660
1661 /*
1662 * It's sufficient to test any one pk attribute for null to detect a join
1663 * failure.
1664 */
1665 quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0]));
1666 appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
1667
1668 sep = "";
1669 for (int i = 0; i < riinfo->nkeys; i++)
1670 {
1671 quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
1673 "%sfk.%s IS NOT NULL",
1674 sep, fkattname);
1675 switch (riinfo->confmatchtype)
1676 {
1678 sep = " AND ";
1679 break;
1681 sep = " OR ";
1682 break;
1683 }
1684 }
1686
1687 /*
1688 * Temporarily increase work_mem so that the check query can be executed
1689 * more efficiently. It seems okay to do this because the query is simple
1690 * enough to not use a multiple of work_mem, and one typically would not
1691 * have many large foreign-key validations happening concurrently. So
1692 * this seems to meet the criteria for being considered a "maintenance"
1693 * operation, and accordingly we use maintenance_work_mem. However, we
1694 * must also set hash_mem_multiplier to 1, since it is surely not okay to
1695 * let that get applied to the maintenance_work_mem value.
1696 *
1697 * We use the equivalent of a function SET option to allow the setting to
1698 * persist for exactly the duration of the check query. guc.c also takes
1699 * care of undoing the setting on error.
1700 */
1701 save_nestlevel = NewGUCNestLevel();
1702
1704 (void) set_config_option("work_mem", workmembuf,
1706 GUC_ACTION_SAVE, true, 0, false);
1707 (void) set_config_option("hash_mem_multiplier", "1",
1709 GUC_ACTION_SAVE, true, 0, false);
1710
1711 SPI_connect();
1712
1713 /*
1714 * Generate the plan. We don't need to cache it, and there are no
1715 * arguments to the plan.
1716 */
1717 qplan = SPI_prepare(querybuf.data, 0, NULL);
1718
1719 if (qplan == NULL)
1720 elog(ERROR, "SPI_prepare returned %s for %s",
1722
1723 /*
1724 * Run the plan. For safety we force a current snapshot to be used. (In
1725 * transaction-snapshot mode, this arguably violates transaction isolation
1726 * rules, but we really haven't got much choice.) We don't need to
1727 * register the snapshot, because SPI_execute_snapshot will see to it. We
1728 * need at most one tuple returned, so pass limit = 1.
1729 */
1731 NULL, NULL,
1734 true, false, 1);
1735
1736 /* Check result */
1738 elog(ERROR, "SPI_execute_snapshot returned %s", SPI_result_code_string(spi_result));
1739
1740 /* Did we find a tuple violating the constraint? */
1741 if (SPI_processed > 0)
1742 {
1743 TupleTableSlot *slot;
1744 HeapTuple tuple = SPI_tuptable->vals[0];
1745 TupleDesc tupdesc = SPI_tuptable->tupdesc;
1747
1748 slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
1749
1750 heap_deform_tuple(tuple, tupdesc,
1751 slot->tts_values, slot->tts_isnull);
1753
1754 /*
1755 * The columns to look at in the result tuple are 1..N, not whatever
1756 * they are in the fk_rel. Hack up riinfo so that the subroutines
1757 * called here will behave properly.
1758 *
1759 * In addition to this, we have to pass the correct tupdesc to
1760 * ri_ReportViolation, overriding its normal habit of using the pk_rel
1761 * or fk_rel's tupdesc.
1762 */
1764 for (int i = 0; i < fake_riinfo.nkeys; i++)
1765 fake_riinfo.fk_attnums[i] = i + 1;
1766
1767 /*
1768 * If it's MATCH FULL, and there are any nulls in the FK keys,
1769 * complain about that rather than the lack of a match. MATCH FULL
1770 * disallows partially-null FK rows.
1771 */
1772 if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
1773 ri_NullCheck(tupdesc, slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
1774 ereport(ERROR,
1776 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
1778 NameStr(fake_riinfo.conname)),
1779 errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
1781 NameStr(fake_riinfo.conname))));
1782
1783 /*
1784 * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
1785 * query, which isn't true, but will cause it to use
1786 * fake_riinfo.fk_attnums as we need.
1787 */
1789 pk_rel, fk_rel,
1790 slot, tupdesc,
1791 RI_PLAN_CHECK_LOOKUPPK, false, false);
1792
1794 }
1795
1796 if (SPI_finish() != SPI_OK_FINISH)
1797 elog(ERROR, "SPI_finish failed");
1798
1799 /*
1800 * Restore work_mem and hash_mem_multiplier.
1801 */
1802 AtEOXact_GUC(true, save_nestlevel);
1803
1804 return true;
1805}
bool has_bypassrls_privilege(Oid roleid)
Definition aclchk.c:4231
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4133
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
bool ExecCheckPermissions(List *rangeTable, List *rteperminfos, bool ereport_on_violation)
Definition execMain.c:584
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:133
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:1266
List * lappend(List *list, void *datum)
Definition list.c:339
#define makeNode(_type_)
Definition nodes.h:161
@ RTE_RELATION
#define ACL_SELECT
Definition parsenodes.h:77
int errtableconstraint(Relation rel, const char *conname)
Definition relcache.c:6116
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:87
static void ri_GenerateQualCollation(StringInfo buf, Oid collation)
#define MAX_QUOTED_REL_NAME_LEN
Definition ri_triggers.c:84
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:88
#define RI_PLAN_CHECK_LOOKUPPK
Definition ri_triggers.c:69
#define MAX_QUOTED_NAME_LEN
Definition ri_triggers.c:83
#define RIAttName(rel, attnum)
Definition ri_triggers.c:86
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, 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, NameStr, NewGUCNestLevel(), NIL, object_ownercheck(), PGC_S_SESSION, PGC_USERSET, quoteOneName(), quoteRelationName(), 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 1814 of file ri_triggers.c.

1815{
1818 char *constraintDef;
1823 const char *sep;
1824 const char *fk_only;
1825 int save_nestlevel;
1826 char workmembuf[32];
1827 int spi_result;
1829 int i;
1830
1832
1833 /*
1834 * We don't check permissions before displaying the error message, on the
1835 * assumption that the user detaching the partition must have enough
1836 * privileges to examine the table contents anyhow.
1837 */
1838
1839 /*----------
1840 * The query string built is:
1841 * SELECT fk.keycols FROM [ONLY] relname fk
1842 * JOIN pkrelname pk
1843 * ON (pk.pkkeycol1=fk.keycol1 [AND ...])
1844 * WHERE (<partition constraint>) AND
1845 * For MATCH SIMPLE:
1846 * (fk.keycol1 IS NOT NULL [AND ...])
1847 * For MATCH FULL:
1848 * (fk.keycol1 IS NOT NULL [OR ...])
1849 *
1850 * We attach COLLATE clauses to the operators when comparing columns
1851 * that have different collations.
1852 *----------
1853 */
1855 appendStringInfoString(&querybuf, "SELECT ");
1856 sep = "";
1857 for (i = 0; i < riinfo->nkeys; i++)
1858 {
1860 RIAttName(fk_rel, riinfo->fk_attnums[i]));
1861 appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
1862 sep = ", ";
1863 }
1864
1867 fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
1868 "" : "ONLY ";
1870 " FROM %s%s fk JOIN %s pk ON",
1872 strcpy(pkattname, "pk.");
1873 strcpy(fkattname, "fk.");
1874 sep = "(";
1875 for (i = 0; i < riinfo->nkeys; i++)
1876 {
1877 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
1878 Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
1879 Oid pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
1880 Oid fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
1881
1883 RIAttName(pk_rel, riinfo->pk_attnums[i]));
1885 RIAttName(fk_rel, riinfo->fk_attnums[i]));
1888 riinfo->pf_eq_oprs[i],
1890 if (pk_coll != fk_coll)
1892 sep = "AND";
1893 }
1894
1895 /*
1896 * Start the WHERE clause with the partition constraint (except if this is
1897 * the default partition and there's no other partition, because the
1898 * partition constraint is the empty string in that case.)
1899 */
1901 if (constraintDef && constraintDef[0] != '\0')
1902 appendStringInfo(&querybuf, ") WHERE %s AND (",
1904 else
1905 appendStringInfoString(&querybuf, ") WHERE (");
1906
1907 sep = "";
1908 for (i = 0; i < riinfo->nkeys; i++)
1909 {
1910 quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
1912 "%sfk.%s IS NOT NULL",
1913 sep, fkattname);
1914 switch (riinfo->confmatchtype)
1915 {
1917 sep = " AND ";
1918 break;
1920 sep = " OR ";
1921 break;
1922 }
1923 }
1925
1926 /*
1927 * Temporarily increase work_mem so that the check query can be executed
1928 * more efficiently. It seems okay to do this because the query is simple
1929 * enough to not use a multiple of work_mem, and one typically would not
1930 * have many large foreign-key validations happening concurrently. So
1931 * this seems to meet the criteria for being considered a "maintenance"
1932 * operation, and accordingly we use maintenance_work_mem. However, we
1933 * must also set hash_mem_multiplier to 1, since it is surely not okay to
1934 * let that get applied to the maintenance_work_mem value.
1935 *
1936 * We use the equivalent of a function SET option to allow the setting to
1937 * persist for exactly the duration of the check query. guc.c also takes
1938 * care of undoing the setting on error.
1939 */
1940 save_nestlevel = NewGUCNestLevel();
1941
1943 (void) set_config_option("work_mem", workmembuf,
1945 GUC_ACTION_SAVE, true, 0, false);
1946 (void) set_config_option("hash_mem_multiplier", "1",
1948 GUC_ACTION_SAVE, true, 0, false);
1949
1950 SPI_connect();
1951
1952 /*
1953 * Generate the plan. We don't need to cache it, and there are no
1954 * arguments to the plan.
1955 */
1956 qplan = SPI_prepare(querybuf.data, 0, NULL);
1957
1958 if (qplan == NULL)
1959 elog(ERROR, "SPI_prepare returned %s for %s",
1961
1962 /*
1963 * Run the plan. For safety we force a current snapshot to be used. (In
1964 * transaction-snapshot mode, this arguably violates transaction isolation
1965 * rules, but we really haven't got much choice.) We don't need to
1966 * register the snapshot, because SPI_execute_snapshot will see to it. We
1967 * need at most one tuple returned, so pass limit = 1.
1968 */
1970 NULL, NULL,
1973 true, false, 1);
1974
1975 /* Check result */
1977 elog(ERROR, "SPI_execute_snapshot returned %s", SPI_result_code_string(spi_result));
1978
1979 /* Did we find a tuple that would violate the constraint? */
1980 if (SPI_processed > 0)
1981 {
1982 TupleTableSlot *slot;
1983 HeapTuple tuple = SPI_tuptable->vals[0];
1984 TupleDesc tupdesc = SPI_tuptable->tupdesc;
1986
1987 slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
1988
1989 heap_deform_tuple(tuple, tupdesc,
1990 slot->tts_values, slot->tts_isnull);
1992
1993 /*
1994 * The columns to look at in the result tuple are 1..N, not whatever
1995 * they are in the fk_rel. Hack up riinfo so that ri_ReportViolation
1996 * will behave properly.
1997 *
1998 * In addition to this, we have to pass the correct tupdesc to
1999 * ri_ReportViolation, overriding its normal habit of using the pk_rel
2000 * or fk_rel's tupdesc.
2001 */
2003 for (i = 0; i < fake_riinfo.nkeys; i++)
2004 fake_riinfo.pk_attnums[i] = i + 1;
2005
2007 slot, tupdesc, 0, false, true);
2008 }
2009
2010 if (SPI_finish() != SPI_OK_FINISH)
2011 elog(ERROR, "SPI_finish failed");
2012
2013 /*
2014 * Restore work_mem and hash_mem_multiplier.
2015 */
2016 AtEOXact_GUC(true, save_nestlevel);
2017}
char * pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
Definition ruleutils.c:2484

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

1226{
1228 ScanKeyData skey[1];
1230 HeapTuple tuple,
1231 newtup;
1234
1235 /*
1236 * Find the trigger to delete.
1237 */
1238 ScanKeyInit(&skey[0],
1242
1244 NULL, 1, skey);
1245
1246 tuple = systable_getnext(tgscan);
1247 if (!HeapTupleIsValid(tuple))
1248 elog(ERROR, "could not find tuple for trigger %u", childTrigId);
1249 newtup = heap_copytuple(tuple);
1252 {
1253 /* don't allow setting parent for a constraint that already has one */
1254 if (OidIsValid(trigForm->tgparentid))
1255 elog(ERROR, "trigger %u already has a parent trigger",
1256 childTrigId);
1257
1258 trigForm->tgparentid = parentTrigId;
1259
1261
1263
1266
1269 }
1270 else
1271 {
1272 trigForm->tgparentid = InvalidOid;
1273
1275
1282 }
1283
1286}
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:353

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