Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * constraint.c
4 : * PostgreSQL CONSTRAINT support code.
5 : *
6 : * Portions Copyright (c) 1996-2017, 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 "catalog/index.h"
17 : #include "commands/trigger.h"
18 : #include "executor/executor.h"
19 : #include "utils/builtins.h"
20 : #include "utils/rel.h"
21 : #include "utils/tqual.h"
22 :
23 :
24 : /*
25 : * unique_key_recheck - trigger function to do a deferred uniqueness check.
26 : *
27 : * This now also does deferred exclusion-constraint checks, so the name is
28 : * somewhat historical.
29 : *
30 : * This is invoked as an AFTER ROW trigger for both INSERT and UPDATE,
31 : * for any rows recorded as potentially violating a deferrable unique
32 : * or exclusion constraint.
33 : *
34 : * This may be an end-of-statement check, a commit-time check, or a
35 : * check triggered by a SET CONSTRAINTS command.
36 : */
37 : Datum
38 18 : unique_key_recheck(PG_FUNCTION_ARGS)
39 : {
40 18 : TriggerData *trigdata = castNode(TriggerData, fcinfo->context);
41 18 : 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;
49 : Datum values[INDEX_MAX_KEYS];
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 18 : if (!CALLED_AS_TRIGGER(fcinfo))
58 0 : 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 36 : if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
64 18 : !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
65 0 : 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 18 : if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
74 12 : new_row = trigdata->tg_trigtuple;
75 6 : else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
76 6 : new_row = trigdata->tg_newtuple;
77 : else
78 : {
79 0 : 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 18 : tmptid = new_row->t_self;
105 18 : 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 0 : 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 18 : indexRel = index_open(trigdata->tg_trigger->tgconstrindid,
119 : RowExclusiveLock);
120 18 : indexInfo = BuildIndexInfo(indexRel);
121 :
122 : /*
123 : * The heap tuple must be put into a slot for FormIndexDatum.
124 : */
125 18 : slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation));
126 :
127 18 : 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 36 : if (indexInfo->ii_Expressions != NIL ||
135 18 : indexInfo->ii_ExclusionOps != NULL)
136 : {
137 4 : estate = CreateExecutorState();
138 4 : econtext = GetPerTupleExprContext(estate);
139 4 : econtext->ecxt_scantuple = slot;
140 : }
141 : else
142 14 : 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 18 : FormIndexDatum(indexInfo, slot, estate, values, isnull);
155 :
156 : /*
157 : * Now do the appropriate check.
158 : */
159 18 : 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 14 : index_insert(indexRel, values, isnull, &(new_row->t_self),
168 : trigdata->tg_relation, UNIQUE_CHECK_EXISTING,
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 4 : 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 10 : if (estate != NULL)
189 1 : FreeExecutorState(estate);
190 :
191 10 : ExecDropSingleTupleTableSlot(slot);
192 :
193 10 : index_close(indexRel, RowExclusiveLock);
194 :
195 10 : return PointerGetDatum(NULL);
196 : }
|