PostgreSQL Source Code git master
constraint.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * constraint.c
4 * PostgreSQL CONSTRAINT support code.
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * IDENTIFICATION
10 * src/backend/commands/constraint.c
11 *
12 *-------------------------------------------------------------------------
13 */
14#include "postgres.h"
15
16#include "access/genam.h"
17#include "access/tableam.h"
18#include "catalog/index.h"
19#include "commands/trigger.h"
20#include "executor/executor.h"
21#include "utils/fmgrprotos.h"
22#include "utils/snapmgr.h"
23
24
25/*
26 * unique_key_recheck - trigger function to do a deferred uniqueness check.
27 *
28 * This now also does deferred exclusion-constraint checks, so the name is
29 * somewhat historical.
30 *
31 * This is invoked as an AFTER ROW trigger for both INSERT and UPDATE,
32 * for any rows recorded as potentially violating a deferrable unique
33 * or exclusion constraint.
34 *
35 * This may be an end-of-statement check, a commit-time check, or a
36 * check triggered by a SET CONSTRAINTS command.
37 */
40{
41 TriggerData *trigdata = (TriggerData *) fcinfo->context;
42 const char *funcname = "unique_key_recheck";
43 ItemPointerData checktid;
44 ItemPointerData tmptid;
45 Relation indexRel;
46 IndexInfo *indexInfo;
47 EState *estate;
48 ExprContext *econtext;
49 TupleTableSlot *slot;
51 bool isnull[INDEX_MAX_KEYS];
52
53 /*
54 * Make sure this is being called as an AFTER ROW trigger. Note:
55 * translatable error strings are shared with ri_triggers.c, so resist the
56 * temptation to fold the function name into them.
57 */
58 if (!CALLED_AS_TRIGGER(fcinfo))
60 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
61 errmsg("function \"%s\" was not called by trigger manager",
62 funcname)));
63
64 if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
67 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
68 errmsg("function \"%s\" must be fired AFTER ROW",
69 funcname)));
70
71 /*
72 * Get the new data that was inserted/updated.
73 */
74 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
75 checktid = trigdata->tg_trigslot->tts_tid;
76 else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
77 checktid = trigdata->tg_newslot->tts_tid;
78 else
79 {
81 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
82 errmsg("function \"%s\" must be fired for INSERT or UPDATE",
83 funcname)));
84 ItemPointerSetInvalid(&checktid); /* keep compiler quiet */
85 }
86
87 slot = table_slot_create(trigdata->tg_relation, NULL);
88
89 /*
90 * If the row pointed at by checktid is now dead (ie, inserted and then
91 * deleted within our transaction), we can skip the check. However, we
92 * have to be careful, because this trigger gets queued only in response
93 * to index insertions; which means it does not get queued e.g. for HOT
94 * updates. The row we are called for might now be dead, but have a live
95 * HOT child, in which case we still need to make the check ---
96 * effectively, we're applying the check against the live child row,
97 * although we can use the values from this row since by definition all
98 * columns of interest to us are the same.
99 *
100 * This might look like just an optimization, because the index AM will
101 * make this identical test before throwing an error. But it's actually
102 * needed for correctness, because the index AM will also throw an error
103 * if it doesn't find the index entry for the row. If the row's dead then
104 * it's possible the index entry has also been marked dead, and even
105 * removed.
106 */
107 tmptid = checktid;
108 {
109 IndexFetchTableData *scan = table_index_fetch_begin(trigdata->tg_relation);
110 bool call_again = false;
111
112 if (!table_index_fetch_tuple(scan, &tmptid, SnapshotSelf, slot,
113 &call_again, NULL))
114 {
115 /*
116 * All rows referenced by the index entry are dead, so skip the
117 * check.
118 */
121 return PointerGetDatum(NULL);
122 }
124 }
125
126 /*
127 * Open the index, acquiring a RowExclusiveLock, just as if we were going
128 * to update it. (This protects against possible changes of the index
129 * schema, not against concurrent updates.)
130 */
131 indexRel = index_open(trigdata->tg_trigger->tgconstrindid,
133 indexInfo = BuildIndexInfo(indexRel);
134
135 /*
136 * Typically the index won't have expressions, but if it does we need an
137 * EState to evaluate them. We need it for exclusion constraints too,
138 * even if they are just on simple columns.
139 */
140 if (indexInfo->ii_Expressions != NIL ||
141 indexInfo->ii_ExclusionOps != NULL)
142 {
143 estate = CreateExecutorState();
144 econtext = GetPerTupleExprContext(estate);
145 econtext->ecxt_scantuple = slot;
146 }
147 else
148 estate = NULL;
149
150 /*
151 * Form the index values and isnull flags for the index entry that we need
152 * to check.
153 *
154 * Note: if the index uses functions that are not as immutable as they are
155 * supposed to be, this could produce an index tuple different from the
156 * original. The index AM can catch such errors by verifying that it
157 * finds a matching index entry with the tuple's TID. For exclusion
158 * constraints we check this in check_exclusion_constraint().
159 */
160 FormIndexDatum(indexInfo, slot, estate, values, isnull);
161
162 /*
163 * Now do the appropriate check.
164 */
165 if (indexInfo->ii_ExclusionOps == NULL)
166 {
167 /*
168 * Note: this is not a real insert; it is a check that the index entry
169 * that has already been inserted is unique. Passing the tuple's tid
170 * (i.e. unmodified by table_index_fetch_tuple()) is correct even if
171 * the row is now dead, because that is the TID the index will know
172 * about.
173 */
174 index_insert(indexRel, values, isnull, &checktid,
175 trigdata->tg_relation, UNIQUE_CHECK_EXISTING,
176 false, indexInfo);
177
178 /* Cleanup cache possibly initialized by index_insert. */
179 index_insert_cleanup(indexRel, indexInfo);
180 }
181 else
182 {
183 /*
184 * For exclusion constraints we just do the normal check, but now it's
185 * okay to throw error. In the HOT-update case, we must use the live
186 * HOT child's TID here, else check_exclusion_constraint will think
187 * the child is a conflict.
188 */
189 check_exclusion_constraint(trigdata->tg_relation, indexRel, indexInfo,
190 &tmptid, values, isnull,
191 estate, false);
192 }
193
194 /*
195 * If that worked, then this index entry is unique or non-excluded, and we
196 * are done.
197 */
198 if (estate != NULL)
199 FreeExecutorState(estate);
200
202
203 index_close(indexRel, RowExclusiveLock);
204
205 return PointerGetDatum(NULL);
206}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
Datum unique_key_recheck(PG_FUNCTION_ARGS)
Definition: constraint.c:39
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
void check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo, ItemPointer tupleid, const Datum *values, const bool *isnull, EState *estate, bool newIndex)
Definition: execIndexing.c:950
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1441
void FreeExecutorState(EState *estate)
Definition: execUtils.c:192
EState * CreateExecutorState(void)
Definition: execUtils.c:88
#define GetPerTupleExprContext(estate)
Definition: executor.h:563
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
@ UNIQUE_CHECK_EXISTING
Definition: genam.h:122
#define funcname
Definition: indent_codes.h:69
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2427
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition: index.c:2730
bool index_insert(Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_t_ctid, Relation heapRelation, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition: indexam.c:213
void index_insert_cleanup(Relation indexRelation, IndexInfo *indexInfo)
Definition: indexam.c:241
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
#define RowExclusiveLock
Definition: lockdefs.h:38
#define INDEX_MAX_KEYS
#define NIL
Definition: pg_list.h:68
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
uintptr_t Datum
Definition: postgres.h:69
#define SnapshotSelf
Definition: snapmgr.h:32
TriggerEvent tg_event
Definition: trigger.h:34
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
TupleTableSlot * tg_newslot
Definition: trigger.h:40
ItemPointerData tts_tid
Definition: tuptable.h:129
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
static IndexFetchTableData * table_index_fetch_begin(Relation rel)
Definition: tableam.h:1198
static void table_index_fetch_end(struct IndexFetchTableData *scan)
Definition: tableam.h:1217
static bool table_index_fetch_tuple(struct IndexFetchTableData *scan, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, bool *call_again, bool *all_dead)
Definition: tableam.h:1247
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:26
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:122
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:131
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:110
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:116