PostgreSQL Source Code  git master
constraint.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/tableam.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "utils/fmgrprotos.h"
#include "utils/snapmgr.h"
Include dependency graph for constraint.c:

Go to the source code of this file.

Functions

Datum unique_key_recheck (PG_FUNCTION_ARGS)
 

Function Documentation

◆ unique_key_recheck()

Datum unique_key_recheck ( PG_FUNCTION_ARGS  )

Definition at line 39 of file constraint.c.

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))
59  ereport(ERROR,
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) ||
65  !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
66  ereport(ERROR,
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  {
80  ereport(ERROR,
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  {
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  */
120  table_index_fetch_end(scan);
121  return PointerGetDatum(NULL);
122  }
123  table_index_fetch_end(scan);
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,
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
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:1341
EState * CreateExecutorState(void)
Definition: execUtils.c:88
void FreeExecutorState(EState *estate)
Definition: execUtils.c:191
#define GetPerTupleExprContext(estate)
Definition: executor.h:561
@ UNIQUE_CHECK_EXISTING
Definition: genam.h:120
#define funcname
Definition: indent_codes.h:69
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition: index.c:2726
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2425
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:322
uintptr_t Datum
Definition: postgres.h:64
#define SnapshotSelf
Definition: snapmgr.h:32
Relation tg_relation
Definition: trigger.h:35
TriggerEvent tg_event
Definition: trigger.h:34
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
TupleTableSlot * tg_newslot
Definition: trigger.h:40
Trigger * tg_trigger
Definition: trigger.h:38
Oid tgconstrindid
Definition: reltrigger.h:34
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:1201
static void table_index_fetch_end(struct IndexFetchTableData *scan)
Definition: tableam.h:1220
static bool table_index_fetch_tuple(struct IndexFetchTableData *scan, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, bool *call_again, bool *all_dead)
Definition: tableam.h:1250
#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

References CALLED_AS_TRIGGER, ereport, errcode(), errmsg(), ERROR, funcname, if(), INDEX_MAX_KEYS, ItemPointerSetInvalid(), TriggerData::tg_event, TriggerData::tg_newslot, TriggerData::tg_trigslot, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, TupleTableSlot::tts_tid, and values.