PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
constraint.c File Reference
#include "postgres.h"
#include "catalog/index.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/tqual.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

Datum unique_key_recheck ( PG_FUNCTION_ARGS  )

Definition at line 38 of file constraint.c.

References BuildIndexInfo(), CALLED_AS_TRIGGER, castNode, check_exclusion_constraint(), CreateExecutorState(), ExprContext::ecxt_scantuple, ereport, errcode(), errmsg(), ERROR, ExecDropSingleTupleTableSlot(), ExecStoreTuple(), FormIndexDatum(), FreeExecutorState(), GetPerTupleExprContext, heap_hot_search(), IndexInfo::ii_ExclusionOps, IndexInfo::ii_Expressions, index_close(), index_insert(), INDEX_MAX_KEYS, index_open(), InvalidBuffer, MakeSingleTupleTableSlot(), NIL, NULL, PointerGetDatum, RelationGetDescr, RowExclusiveLock, SnapshotSelf, HeapTupleData::t_self, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgconstrindid, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, UNIQUE_CHECK_EXISTING, and values.

39 {
40  TriggerData *trigdata = castNode(TriggerData, fcinfo->context);
41  const char *funcname = "unique_key_recheck";
42  HeapTuple new_row;
43  ItemPointerData tmptid;
44  Relation indexRel;
45  IndexInfo *indexInfo;
46  EState *estate;
47  ExprContext *econtext;
48  TupleTableSlot *slot;
50  bool isnull[INDEX_MAX_KEYS];
51 
52  /*
53  * Make sure this is being called as an AFTER ROW trigger. Note:
54  * translatable error strings are shared with ri_triggers.c, so resist the
55  * temptation to fold the function name into them.
56  */
57  if (!CALLED_AS_TRIGGER(fcinfo))
58  ereport(ERROR,
59  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
60  errmsg("function \"%s\" was not called by trigger manager",
61  funcname)));
62 
63  if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
64  !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
65  ereport(ERROR,
66  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
67  errmsg("function \"%s\" must be fired AFTER ROW",
68  funcname)));
69 
70  /*
71  * Get the new data that was inserted/updated.
72  */
73  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
74  new_row = trigdata->tg_trigtuple;
75  else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
76  new_row = trigdata->tg_newtuple;
77  else
78  {
79  ereport(ERROR,
80  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
81  errmsg("function \"%s\" must be fired for INSERT or UPDATE",
82  funcname)));
83  new_row = NULL; /* keep compiler quiet */
84  }
85 
86  /*
87  * If the new_row is now dead (ie, inserted and then deleted within our
88  * transaction), we can skip the check. However, we have to be careful,
89  * because this trigger gets queued only in response to index insertions;
90  * which means it does not get queued for HOT updates. The row we are
91  * called for might now be dead, but have a live HOT child, in which case
92  * we still need to make the check --- effectively, we're applying the
93  * check against the live child row, although we can use the values from
94  * this row since by definition all columns of interest to us are the
95  * same.
96  *
97  * This might look like just an optimization, because the index AM will
98  * make this identical test before throwing an error. But it's actually
99  * needed for correctness, because the index AM will also throw an error
100  * if it doesn't find the index entry for the row. If the row's dead then
101  * it's possible the index entry has also been marked dead, and even
102  * removed.
103  */
104  tmptid = new_row->t_self;
105  if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
106  {
107  /*
108  * All rows in the HOT chain are dead, so skip the check.
109  */
110  return PointerGetDatum(NULL);
111  }
112 
113  /*
114  * Open the index, acquiring a RowExclusiveLock, just as if we were going
115  * to update it. (This protects against possible changes of the index
116  * schema, not against concurrent updates.)
117  */
118  indexRel = index_open(trigdata->tg_trigger->tgconstrindid,
120  indexInfo = BuildIndexInfo(indexRel);
121 
122  /*
123  * The heap tuple must be put into a slot for FormIndexDatum.
124  */
126 
127  ExecStoreTuple(new_row, slot, InvalidBuffer, false);
128 
129  /*
130  * Typically the index won't have expressions, but if it does we need an
131  * EState to evaluate them. We need it for exclusion constraints too,
132  * even if they are just on simple columns.
133  */
134  if (indexInfo->ii_Expressions != NIL ||
135  indexInfo->ii_ExclusionOps != NULL)
136  {
137  estate = CreateExecutorState();
138  econtext = GetPerTupleExprContext(estate);
139  econtext->ecxt_scantuple = slot;
140  }
141  else
142  estate = NULL;
143 
144  /*
145  * Form the index values and isnull flags for the index entry that we need
146  * to check.
147  *
148  * Note: if the index uses functions that are not as immutable as they are
149  * supposed to be, this could produce an index tuple different from the
150  * original. The index AM can catch such errors by verifying that it
151  * finds a matching index entry with the tuple's TID. For exclusion
152  * constraints we check this in check_exclusion_constraint().
153  */
154  FormIndexDatum(indexInfo, slot, estate, values, isnull);
155 
156  /*
157  * Now do the appropriate check.
158  */
159  if (indexInfo->ii_ExclusionOps == NULL)
160  {
161  /*
162  * Note: this is not a real insert; it is a check that the index entry
163  * that has already been inserted is unique. Passing t_self is
164  * correct even if t_self is now dead, because that is the TID the
165  * index will know about.
166  */
167  index_insert(indexRel, values, isnull, &(new_row->t_self),
169  indexInfo);
170  }
171  else
172  {
173  /*
174  * For exclusion constraints we just do the normal check, but now it's
175  * okay to throw error. In the HOT-update case, we must use the live
176  * HOT child's TID here, else check_exclusion_constraint will think
177  * the child is a conflict.
178  */
179  check_exclusion_constraint(trigdata->tg_relation, indexRel, indexInfo,
180  &tmptid, values, isnull,
181  estate, false);
182  }
183 
184  /*
185  * If that worked, then this index entry is unique or non-excluded, and we
186  * are done.
187  */
188  if (estate != NULL)
189  FreeExecutorState(estate);
190 
192 
193  index_close(indexRel, RowExclusiveLock);
194 
195  return PointerGetDatum(NULL);
196 }
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition: index.c:1765
#define NIL
Definition: pg_list.h:69
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:320
#define RelationGetDescr(relation)
Definition: rel.h:429
#define castNode(_type_, nodeptr)
Definition: nodes.h:578
#define PointerGetDatum(X)
Definition: postgres.h:562
#define InvalidBuffer
Definition: buf.h:25
int errcode(int sqlerrcode)
Definition: elog.c:575
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:1640
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:91
HeapTuple tg_trigtuple
Definition: trigger.h:35
void FreeExecutorState(EState *estate)
Definition: execUtils.c:178
#define GetPerTupleExprContext(estate)
Definition: executor.h:456
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:216
#define RowExclusiveLock
Definition: lockdefs.h:38
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc)
Definition: execTuples.c:199
#define SnapshotSelf
Definition: tqual.h:27
#define ereport(elevel, rest)
Definition: elog.h:122
EState * CreateExecutorState(void)
Definition: execUtils.c:80
uintptr_t Datum
Definition: postgres.h:372
Trigger * tg_trigger
Definition: trigger.h:37
HeapTuple tg_newtuple
Definition: trigger.h:36
#define NULL
Definition: c.h:229
List * ii_Expressions
Definition: execnodes.h:136
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:25
TriggerEvent tg_event
Definition: trigger.h:33
Oid tgconstrindid
Definition: reltrigger.h:33
#define INDEX_MAX_KEYS
bool heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot, bool *all_dead)
Definition: heapam.c:2139
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:197
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:176
static Datum values[MAXATTR]
Definition: bootstrap.c:163
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:70
Oid * ii_ExclusionOps
Definition: execnodes.h:140
int errmsg(const char *fmt,...)
Definition: elog.c:797
void check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo, ItemPointer tupleid, Datum *values, bool *isnull, EState *estate, bool newIndex)
Definition: execIndexing.c:863
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:82
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:76
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:151
bool index_insert(Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_t_ctid, Relation heapRelation, IndexUniqueCheck checkUnique, IndexInfo *indexInfo)
Definition: indexam.c:194
Relation tg_relation
Definition: trigger.h:34