Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * execIndexing.c
4 : : * routines for inserting index tuples and enforcing unique and
5 : : * exclusion constraints.
6 : : *
7 : : * ExecInsertIndexTuples() is the main entry point. It's called after
8 : : * inserting a tuple to the heap, and it inserts corresponding index tuples
9 : : * into all indexes. At the same time, it enforces any unique and
10 : : * exclusion constraints:
11 : : *
12 : : * Unique Indexes
13 : : * --------------
14 : : *
15 : : * Enforcing a unique constraint is straightforward. When the index AM
16 : : * inserts the tuple to the index, it also checks that there are no
17 : : * conflicting tuples in the index already. It does so atomically, so that
18 : : * even if two backends try to insert the same key concurrently, only one
19 : : * of them will succeed. All the logic to ensure atomicity, and to wait
20 : : * for in-progress transactions to finish, is handled by the index AM.
21 : : *
22 : : * If a unique constraint is deferred, we request the index AM to not
23 : : * throw an error if a conflict is found. Instead, we make note that there
24 : : * was a conflict and return the list of indexes with conflicts to the
25 : : * caller. The caller must re-check them later, by calling index_insert()
26 : : * with the UNIQUE_CHECK_EXISTING option.
27 : : *
28 : : * Exclusion Constraints
29 : : * ---------------------
30 : : *
31 : : * Exclusion constraints are different from unique indexes in that when the
32 : : * tuple is inserted to the index, the index AM does not check for
33 : : * duplicate keys at the same time. After the insertion, we perform a
34 : : * separate scan on the index to check for conflicting tuples, and if one
35 : : * is found, we throw an error and the transaction is aborted. If the
36 : : * conflicting tuple's inserter or deleter is in-progress, we wait for it
37 : : * to finish first.
38 : : *
39 : : * There is a chance of deadlock, if two backends insert a tuple at the
40 : : * same time, and then perform the scan to check for conflicts. They will
41 : : * find each other's tuple, and both try to wait for each other. The
42 : : * deadlock detector will detect that, and abort one of the transactions.
43 : : * That's fairly harmless, as one of them was bound to abort with a
44 : : * "duplicate key error" anyway, although you get a different error
45 : : * message.
46 : : *
47 : : * If an exclusion constraint is deferred, we still perform the conflict
48 : : * checking scan immediately after inserting the index tuple. But instead
49 : : * of throwing an error if a conflict is found, we return that information
50 : : * to the caller. The caller must re-check them later by calling
51 : : * check_exclusion_constraint().
52 : : *
53 : : * Speculative insertion
54 : : * ---------------------
55 : : *
56 : : * Speculative insertion is a two-phase mechanism used to implement
57 : : * INSERT ... ON CONFLICT DO UPDATE/NOTHING. The tuple is first inserted
58 : : * to the heap and update the indexes as usual, but if a constraint is
59 : : * violated, we can still back out the insertion without aborting the whole
60 : : * transaction. In an INSERT ... ON CONFLICT statement, if a conflict is
61 : : * detected, the inserted tuple is backed out and the ON CONFLICT action is
62 : : * executed instead.
63 : : *
64 : : * Insertion to a unique index works as usual: the index AM checks for
65 : : * duplicate keys atomically with the insertion. But instead of throwing
66 : : * an error on a conflict, the speculatively inserted heap tuple is backed
67 : : * out.
68 : : *
69 : : * Exclusion constraints are slightly more complicated. As mentioned
70 : : * earlier, there is a risk of deadlock when two backends insert the same
71 : : * key concurrently. That was not a problem for regular insertions, when
72 : : * one of the transactions has to be aborted anyway, but with a speculative
73 : : * insertion we cannot let a deadlock happen, because we only want to back
74 : : * out the speculatively inserted tuple on conflict, not abort the whole
75 : : * transaction.
76 : : *
77 : : * When a backend detects that the speculative insertion conflicts with
78 : : * another in-progress tuple, it has two options:
79 : : *
80 : : * 1. back out the speculatively inserted tuple, then wait for the other
81 : : * transaction, and retry. Or,
82 : : * 2. wait for the other transaction, with the speculatively inserted tuple
83 : : * still in place.
84 : : *
85 : : * If two backends insert at the same time, and both try to wait for each
86 : : * other, they will deadlock. So option 2 is not acceptable. Option 1
87 : : * avoids the deadlock, but it is prone to a livelock instead. Both
88 : : * transactions will wake up immediately as the other transaction backs
89 : : * out. Then they both retry, and conflict with each other again, lather,
90 : : * rinse, repeat.
91 : : *
92 : : * To avoid the livelock, one of the backends must back out first, and then
93 : : * wait, while the other one waits without backing out. It doesn't matter
94 : : * which one backs out, so we employ an arbitrary rule that the transaction
95 : : * with the higher XID backs out.
96 : : *
97 : : *
98 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
99 : : * Portions Copyright (c) 1994, Regents of the University of California
100 : : *
101 : : *
102 : : * IDENTIFICATION
103 : : * src/backend/executor/execIndexing.c
104 : : *
105 : : *-------------------------------------------------------------------------
106 : : */
107 : : #include "postgres.h"
108 : :
109 : : #include "access/genam.h"
110 : : #include "access/relscan.h"
111 : : #include "access/tableam.h"
112 : : #include "access/xact.h"
113 : : #include "catalog/index.h"
114 : : #include "executor/executor.h"
115 : : #include "nodes/nodeFuncs.h"
116 : : #include "storage/lmgr.h"
117 : : #include "utils/multirangetypes.h"
118 : : #include "utils/rangetypes.h"
119 : : #include "utils/snapmgr.h"
120 : :
121 : : /* waitMode argument to check_exclusion_or_unique_constraint() */
122 : : typedef enum
123 : : {
124 : : CEOUC_WAIT,
125 : : CEOUC_NOWAIT,
126 : : CEOUC_LIVELOCK_PREVENTING_WAIT,
127 : : } CEOUC_WAIT_MODE;
128 : :
129 : : static bool check_exclusion_or_unique_constraint(Relation heap, Relation index,
130 : : IndexInfo *indexInfo,
131 : : ItemPointer tupleid,
132 : : const Datum *values, const bool *isnull,
133 : : EState *estate, bool newIndex,
134 : : CEOUC_WAIT_MODE waitMode,
135 : : bool violationOK,
136 : : ItemPointer conflictTid);
137 : :
138 : : static bool index_recheck_constraint(Relation index, const Oid *constr_procs,
139 : : const Datum *existing_values, const bool *existing_isnull,
140 : : const Datum *new_values);
141 : : static bool index_unchanged_by_update(ResultRelInfo *resultRelInfo,
142 : : EState *estate, IndexInfo *indexInfo,
143 : : Relation indexRelation);
144 : : static bool index_expression_changed_walker(Node *node,
145 : : Bitmapset *allUpdatedCols);
146 : : static void ExecWithoutOverlapsNotEmpty(Relation rel, NameData attname, Datum attval,
147 : : char typtype, Oid atttypid);
148 : :
149 : : /* ----------------------------------------------------------------
150 : : * ExecOpenIndices
151 : : *
152 : : * Find the indices associated with a result relation, open them,
153 : : * and save information about them in the result ResultRelInfo.
154 : : *
155 : : * At entry, caller has already opened and locked
156 : : * resultRelInfo->ri_RelationDesc.
157 : : * ----------------------------------------------------------------
158 : : */
159 : : void
3774 andres@anarazel.de 160 :CBC 937706 : ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
161 : : {
3788 heikki.linnakangas@i 162 : 937706 : Relation resultRelation = resultRelInfo->ri_RelationDesc;
163 : : List *indexoidlist;
164 : : ListCell *l;
165 : : int len,
166 : : i;
167 : : RelationPtr relationDescs;
168 : : IndexInfo **indexInfoArray;
169 : :
170 : 937706 : resultRelInfo->ri_NumIndices = 0;
171 : :
172 : : /* fast path if no indexes */
173 [ + + ]: 937706 : if (!RelationGetForm(resultRelation)->relhasindex)
174 : 40496 : return;
175 : :
176 : : /*
177 : : * Get cached list of index OIDs
178 : : */
179 : 897210 : indexoidlist = RelationGetIndexList(resultRelation);
180 : 897210 : len = list_length(indexoidlist);
181 [ + + ]: 897210 : if (len == 0)
182 : 20295 : return;
183 : :
184 : : /* This Assert will fail if ExecOpenIndices is called twice */
199 tgl@sss.pgh.pa.us 185 [ - + ]: 876915 : Assert(resultRelInfo->ri_IndexRelationDescs == NULL);
186 : :
187 : : /*
188 : : * allocate space for result arrays
189 : : */
3788 heikki.linnakangas@i 190 : 876915 : relationDescs = (RelationPtr) palloc(len * sizeof(Relation));
191 : 876915 : indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *));
192 : :
193 : 876915 : resultRelInfo->ri_NumIndices = len;
194 : 876915 : resultRelInfo->ri_IndexRelationDescs = relationDescs;
195 : 876915 : resultRelInfo->ri_IndexRelationInfo = indexInfoArray;
196 : :
197 : : /*
198 : : * For each index, open the index relation and save pg_index info. We
199 : : * acquire RowExclusiveLock, signifying we will update the index.
200 : : *
201 : : * Note: we do this even if the index is not indisready; it's not worth
202 : : * the trouble to optimize for the case where it isn't.
203 : : */
204 : 876915 : i = 0;
205 [ + - + + : 2633041 : foreach(l, indexoidlist)
+ + ]
206 : : {
207 : 1756126 : Oid indexOid = lfirst_oid(l);
208 : : Relation indexDesc;
209 : : IndexInfo *ii;
210 : :
211 : 1756126 : indexDesc = index_open(indexOid, RowExclusiveLock);
212 : :
213 : : /* extract index key information from the index's pg_index info */
214 : 1756126 : ii = BuildIndexInfo(indexDesc);
215 : :
216 : : /*
217 : : * If the indexes are to be used for speculative insertion, add extra
218 : : * information required by unique index entries.
219 : : */
354 peter@eisentraut.org 220 [ + + + + : 1756126 : if (speculative && ii->ii_Unique && !indexDesc->rd_index->indisexclusion)
+ + ]
3774 andres@anarazel.de 221 : 614 : BuildSpeculativeIndexInfo(indexDesc, ii);
222 : :
3788 heikki.linnakangas@i 223 : 1756126 : relationDescs[i] = indexDesc;
224 : 1756126 : indexInfoArray[i] = ii;
225 : 1756126 : i++;
226 : : }
227 : :
228 : 876915 : list_free(indexoidlist);
229 : : }
230 : :
231 : : /* ----------------------------------------------------------------
232 : : * ExecCloseIndices
233 : : *
234 : : * Close the index relations stored in resultRelInfo
235 : : * ----------------------------------------------------------------
236 : : */
237 : : void
238 : 976021 : ExecCloseIndices(ResultRelInfo *resultRelInfo)
239 : : {
240 : : int i;
241 : : int numIndices;
242 : : RelationPtr indexDescs;
243 : : IndexInfo **indexInfos;
244 : :
245 : 976021 : numIndices = resultRelInfo->ri_NumIndices;
246 : 976021 : indexDescs = resultRelInfo->ri_IndexRelationDescs;
651 tomas.vondra@postgre 247 : 976021 : indexInfos = resultRelInfo->ri_IndexRelationInfo;
248 : :
3788 heikki.linnakangas@i 249 [ + + ]: 2731160 : for (i = 0; i < numIndices; i++)
250 : : {
251 : : /* This Assert will fail if ExecCloseIndices is called twice */
199 tgl@sss.pgh.pa.us 252 [ - + ]: 1755139 : Assert(indexDescs[i] != NULL);
253 : :
254 : : /* Give the index a chance to do some post-insert cleanup */
651 tomas.vondra@postgre 255 : 1755139 : index_insert_cleanup(indexDescs[i], indexInfos[i]);
256 : :
257 : : /* Drop lock acquired by ExecOpenIndices */
3788 heikki.linnakangas@i 258 : 1755139 : index_close(indexDescs[i], RowExclusiveLock);
259 : :
260 : : /* Mark the index as closed */
199 tgl@sss.pgh.pa.us 261 : 1755139 : indexDescs[i] = NULL;
262 : : }
263 : :
264 : : /*
265 : : * We don't attempt to free the IndexInfo data structures or the arrays,
266 : : * instead assuming that such stuff will be cleaned up automatically in
267 : : * FreeExecutorState.
268 : : */
3788 heikki.linnakangas@i 269 : 976021 : }
270 : :
271 : : /* ----------------------------------------------------------------
272 : : * ExecInsertIndexTuples
273 : : *
274 : : * This routine takes care of inserting index tuples
275 : : * into all the relations indexing the result relation
276 : : * when a heap tuple is inserted into the result relation.
277 : : *
278 : : * When 'update' is true and 'onlySummarizing' is false,
279 : : * executor is performing an UPDATE that could not use an
280 : : * optimization like heapam's HOT (in more general terms a
281 : : * call to table_tuple_update() took place and set
282 : : * 'update_indexes' to TU_All). Receiving this hint makes
283 : : * us consider if we should pass down the 'indexUnchanged'
284 : : * hint in turn. That's something that we figure out for
285 : : * each index_insert() call iff 'update' is true.
286 : : * (When 'update' is false we already know not to pass the
287 : : * hint to any index.)
288 : : *
289 : : * If onlySummarizing is set, an equivalent optimization to
290 : : * HOT has been applied and any updated columns are indexed
291 : : * only by summarizing indexes (or in more general terms a
292 : : * call to table_tuple_update() took place and set
293 : : * 'update_indexes' to TU_Summarizing). We can (and must)
294 : : * therefore only update the indexes that have
295 : : * 'amsummarizing' = true.
296 : : *
297 : : * Unique and exclusion constraints are enforced at the same
298 : : * time. This returns a list of index OIDs for any unique or
299 : : * exclusion constraints that are deferred and that had
300 : : * potential (unconfirmed) conflicts. (if noDupErr == true,
301 : : * the same is done for non-deferred constraints, but report
302 : : * if conflict was speculative or deferred conflict to caller)
303 : : *
304 : : * If 'arbiterIndexes' is nonempty, noDupErr applies only to
305 : : * those indexes. NIL means noDupErr applies to all indexes.
306 : : * ----------------------------------------------------------------
307 : : */
308 : : List *
1788 309 : 1821781 : ExecInsertIndexTuples(ResultRelInfo *resultRelInfo,
310 : : TupleTableSlot *slot,
311 : : EState *estate,
312 : : bool update,
313 : : bool noDupErr,
314 : : bool *specConflict,
315 : : List *arbiterIndexes,
316 : : bool onlySummarizing)
317 : : {
2359 andres@anarazel.de 318 : 1821781 : ItemPointer tupleid = &slot->tts_tid;
3788 heikki.linnakangas@i 319 : 1821781 : List *result = NIL;
320 : : int i;
321 : : int numIndices;
322 : : RelationPtr relationDescs;
323 : : Relation heapRelation;
324 : : IndexInfo **indexInfoArray;
325 : : ExprContext *econtext;
326 : : Datum values[INDEX_MAX_KEYS];
327 : : bool isnull[INDEX_MAX_KEYS];
328 : :
2359 andres@anarazel.de 329 [ - + ]: 1821781 : Assert(ItemPointerIsValid(tupleid));
330 : :
331 : : /*
332 : : * Get information from the result relation info structure.
333 : : */
3788 heikki.linnakangas@i 334 : 1821781 : numIndices = resultRelInfo->ri_NumIndices;
335 : 1821781 : relationDescs = resultRelInfo->ri_IndexRelationDescs;
336 : 1821781 : indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
337 : 1821781 : heapRelation = resultRelInfo->ri_RelationDesc;
338 : :
339 : : /* Sanity check: slot must belong to the same rel as the resultRelInfo. */
2285 drowley@postgresql.o 340 [ - + ]: 1821781 : Assert(slot->tts_tableOid == RelationGetRelid(heapRelation));
341 : :
342 : : /*
343 : : * We will use the EState's per-tuple context for evaluating predicates
344 : : * and index expressions (creating it if it's not already there).
345 : : */
3788 heikki.linnakangas@i 346 [ + + ]: 1821781 : econtext = GetPerTupleExprContext(estate);
347 : :
348 : : /* Arrange for econtext's scan tuple to be the tuple under test */
349 : 1821781 : econtext->ecxt_scantuple = slot;
350 : :
351 : : /*
352 : : * for each index, form and insert the index tuple
353 : : */
354 [ + + ]: 3826786 : for (i = 0; i < numIndices; i++)
355 : : {
356 : 2005350 : Relation indexRelation = relationDescs[i];
357 : : IndexInfo *indexInfo;
358 : : bool applyNoDupErr;
359 : : IndexUniqueCheck checkUnique;
360 : : bool indexUnchanged;
361 : : bool satisfiesConstraint;
362 : :
363 [ - + ]: 2005350 : if (indexRelation == NULL)
3788 heikki.linnakangas@i 364 :UBC 0 : continue;
365 : :
3788 heikki.linnakangas@i 366 :CBC 2005350 : indexInfo = indexInfoArray[i];
367 : :
368 : : /* If the index is marked as read-only, ignore it */
369 [ + + ]: 2005350 : if (!indexInfo->ii_ReadyForInserts)
370 : 99 : continue;
371 : :
372 : : /*
373 : : * Skip processing of non-summarizing indexes if we only update
374 : : * summarizing indexes
375 : : */
901 tomas.vondra@postgre 376 [ + + + + ]: 2005251 : if (onlySummarizing && !indexInfo->ii_Summarizing)
377 : 3 : continue;
378 : :
379 : : /* Check for partial index */
3788 heikki.linnakangas@i 380 [ + + ]: 2005248 : if (indexInfo->ii_Predicate != NIL)
381 : : {
382 : : ExprState *predicate;
383 : :
384 : : /*
385 : : * If predicate state not set up yet, create it (in the estate's
386 : : * per-query context)
387 : : */
388 : 200545 : predicate = indexInfo->ii_PredicateState;
3098 andres@anarazel.de 389 [ + + ]: 200545 : if (predicate == NULL)
390 : : {
391 : 130 : predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
3788 heikki.linnakangas@i 392 : 130 : indexInfo->ii_PredicateState = predicate;
393 : : }
394 : :
395 : : /* Skip this index-update if the predicate isn't satisfied */
3098 andres@anarazel.de 396 [ + + ]: 200545 : if (!ExecQual(predicate, econtext))
3788 heikki.linnakangas@i 397 : 200274 : continue;
398 : : }
399 : :
400 : : /*
401 : : * FormIndexDatum fills in its values and isnull parameters with the
402 : : * appropriate values for the column(s) of the index.
403 : : */
404 : 1804974 : FormIndexDatum(indexInfo,
405 : : slot,
406 : : estate,
407 : : values,
408 : : isnull);
409 : :
410 : : /* Check whether to apply noDupErr to this index */
3351 tgl@sss.pgh.pa.us 411 [ + + + + ]: 1882726 : applyNoDupErr = noDupErr &&
412 [ + + ]: 77752 : (arbiterIndexes == NIL ||
413 : 77752 : list_member_oid(arbiterIndexes,
414 : 77752 : indexRelation->rd_index->indexrelid));
415 : :
416 : : /*
417 : : * The index AM does the actual insertion, plus uniqueness checking.
418 : : *
419 : : * For an immediate-mode unique index, we just tell the index AM to
420 : : * throw error if not unique.
421 : : *
422 : : * For a deferrable unique index, we tell the index AM to just detect
423 : : * possible non-uniqueness, and we add the index OID to the result
424 : : * list if further checking is needed.
425 : : *
426 : : * For a speculative insertion (used by INSERT ... ON CONFLICT), do
427 : : * the same as for a deferrable unique index.
428 : : */
3788 heikki.linnakangas@i 429 [ + + ]: 1804974 : if (!indexRelation->rd_index->indisunique)
430 : 956127 : checkUnique = UNIQUE_CHECK_NO;
3351 tgl@sss.pgh.pa.us 431 [ + + ]: 848847 : else if (applyNoDupErr)
3774 andres@anarazel.de 432 : 77790 : checkUnique = UNIQUE_CHECK_PARTIAL;
3788 heikki.linnakangas@i 433 [ + + ]: 771057 : else if (indexRelation->rd_index->indimmediate)
434 : 770982 : checkUnique = UNIQUE_CHECK_YES;
435 : : else
436 : 75 : checkUnique = UNIQUE_CHECK_PARTIAL;
437 : :
438 : : /*
439 : : * There's definitely going to be an index_insert() call for this
440 : : * index. If we're being called as part of an UPDATE statement,
441 : : * consider if the 'indexUnchanged' = true hint should be passed.
442 : : */
1697 pg@bowt.ie 443 [ + + + + ]: 1804974 : indexUnchanged = update && index_unchanged_by_update(resultRelInfo,
444 : : estate,
445 : : indexInfo,
446 : : indexRelation);
447 : :
448 : : satisfiesConstraint =
3788 heikki.linnakangas@i 449 : 1804974 : index_insert(indexRelation, /* index relation */
450 : : values, /* array of index Datums */
451 : : isnull, /* null flags */
452 : : tupleid, /* tid of heap tuple */
453 : : heapRelation, /* heap relation */
454 : : checkUnique, /* type of uniqueness check to do */
455 : : indexUnchanged, /* UPDATE without logical change? */
456 : : indexInfo); /* index AM may need this */
457 : :
458 : : /*
459 : : * If the index has an associated exclusion constraint, check that.
460 : : * This is simpler than the process for uniqueness checks since we
461 : : * always insert first and then check. If the constraint is deferred,
462 : : * we check now anyway, but don't throw error on violation or wait for
463 : : * a conclusive outcome from a concurrent insertion; instead we'll
464 : : * queue a recheck event. Similarly, noDupErr callers (speculative
465 : : * inserters) will recheck later, and wait for a conclusive outcome
466 : : * then.
467 : : *
468 : : * An index for an exclusion constraint can't also be UNIQUE (not an
469 : : * essential property, we just don't allow it in the grammar), so no
470 : : * need to preserve the prior state of satisfiesConstraint.
471 : : */
472 [ + + ]: 1804716 : if (indexInfo->ii_ExclusionOps != NULL)
473 : : {
474 : : bool violationOK;
475 : : CEOUC_WAIT_MODE waitMode;
476 : :
3351 tgl@sss.pgh.pa.us 477 [ + + ]: 967 : if (applyNoDupErr)
478 : : {
3774 andres@anarazel.de 479 : 72 : violationOK = true;
480 : 72 : waitMode = CEOUC_LIVELOCK_PREVENTING_WAIT;
481 : : }
482 [ + + ]: 895 : else if (!indexRelation->rd_index->indimmediate)
483 : : {
484 : 21 : violationOK = true;
485 : 21 : waitMode = CEOUC_NOWAIT;
486 : : }
487 : : else
488 : : {
489 : 874 : violationOK = false;
490 : 874 : waitMode = CEOUC_WAIT;
491 : : }
492 : :
493 : : satisfiesConstraint =
494 : 967 : check_exclusion_or_unique_constraint(heapRelation,
495 : : indexRelation, indexInfo,
496 : : tupleid, values, isnull,
497 : : estate, false,
498 : : waitMode, violationOK, NULL);
499 : : }
500 : :
3788 heikki.linnakangas@i 501 [ + + ]: 1804629 : if ((checkUnique == UNIQUE_CHECK_PARTIAL ||
502 [ + + ]: 1726764 : indexInfo->ii_ExclusionOps != NULL) &&
503 [ + + ]: 78673 : !satisfiesConstraint)
504 : : {
505 : : /*
506 : : * The tuple potentially violates the uniqueness or exclusion
507 : : * constraint, so make a note of the index so that we can re-check
508 : : * it later. Speculative inserters are told if there was a
509 : : * speculative conflict, since that always requires a restart.
510 : : */
511 : 112 : result = lappend_oid(result, RelationGetRelid(indexRelation));
3774 andres@anarazel.de 512 [ + + + - ]: 112 : if (indexRelation->rd_index->indimmediate && specConflict)
513 : 51 : *specConflict = true;
514 : : }
515 : : }
516 : :
3788 heikki.linnakangas@i 517 : 1821436 : return result;
518 : : }
519 : :
520 : : /* ----------------------------------------------------------------
521 : : * ExecCheckIndexConstraints
522 : : *
523 : : * This routine checks if a tuple violates any unique or
524 : : * exclusion constraints. Returns true if there is no conflict.
525 : : * Otherwise returns false, and the TID of the conflicting
526 : : * tuple is returned in *conflictTid.
527 : : *
528 : : * If 'arbiterIndexes' is given, only those indexes are checked.
529 : : * NIL means all indexes.
530 : : *
531 : : * Note that this doesn't lock the values in any way, so it's
532 : : * possible that a conflicting tuple is inserted immediately
533 : : * after this returns. This can be used for either a pre-check
534 : : * before insertion or a re-check after finding a conflict.
535 : : *
536 : : * 'tupleid' should be the TID of the tuple that has been recently
537 : : * inserted (or can be invalid if we haven't inserted a new tuple yet).
538 : : * This tuple will be excluded from conflict checking.
539 : : * ----------------------------------------------------------------
540 : : */
541 : : bool
1788 542 : 4827 : ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
543 : : EState *estate, ItemPointer conflictTid,
544 : : ItemPointer tupleid, List *arbiterIndexes)
545 : : {
546 : : int i;
547 : : int numIndices;
548 : : RelationPtr relationDescs;
549 : : Relation heapRelation;
550 : : IndexInfo **indexInfoArray;
551 : : ExprContext *econtext;
552 : : Datum values[INDEX_MAX_KEYS];
553 : : bool isnull[INDEX_MAX_KEYS];
554 : : ItemPointerData invalidItemPtr;
3774 andres@anarazel.de 555 : 4827 : bool checkedIndex = false;
556 : :
557 : 4827 : ItemPointerSetInvalid(conflictTid);
558 : 4827 : ItemPointerSetInvalid(&invalidItemPtr);
559 : :
560 : : /*
561 : : * Get information from the result relation info structure.
562 : : */
563 : 4827 : numIndices = resultRelInfo->ri_NumIndices;
564 : 4827 : relationDescs = resultRelInfo->ri_IndexRelationDescs;
565 : 4827 : indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
566 : 4827 : heapRelation = resultRelInfo->ri_RelationDesc;
567 : :
568 : : /*
569 : : * We will use the EState's per-tuple context for evaluating predicates
570 : : * and index expressions (creating it if it's not already there).
571 : : */
572 [ + + ]: 4827 : econtext = GetPerTupleExprContext(estate);
573 : :
574 : : /* Arrange for econtext's scan tuple to be the tuple under test */
575 : 4827 : econtext->ecxt_scantuple = slot;
576 : :
577 : : /*
578 : : * For each index, form index tuple and check if it satisfies the
579 : : * constraint.
580 : : */
581 [ + + ]: 6965 : for (i = 0; i < numIndices; i++)
582 : : {
583 : 4894 : Relation indexRelation = relationDescs[i];
584 : : IndexInfo *indexInfo;
585 : : bool satisfiesConstraint;
586 : :
587 [ - + ]: 4894 : if (indexRelation == NULL)
3774 andres@anarazel.de 588 :UBC 0 : continue;
589 : :
3774 andres@anarazel.de 590 :CBC 4894 : indexInfo = indexInfoArray[i];
591 : :
592 [ + + + + ]: 4894 : if (!indexInfo->ii_Unique && !indexInfo->ii_ExclusionOps)
593 : 2 : continue;
594 : :
595 : : /* If the index is marked as read-only, ignore it */
596 [ - + ]: 4892 : if (!indexInfo->ii_ReadyForInserts)
3774 andres@anarazel.de 597 :UBC 0 : continue;
598 : :
599 : : /* When specific arbiter indexes requested, only examine them */
3774 andres@anarazel.de 600 [ + + ]:CBC 4892 : if (arbiterIndexes != NIL &&
601 [ + + ]: 4760 : !list_member_oid(arbiterIndexes,
602 : 4760 : indexRelation->rd_index->indexrelid))
603 : 62 : continue;
604 : :
605 [ + + ]: 4830 : if (!indexRelation->rd_index->indimmediate)
606 [ + - ]: 3 : ereport(ERROR,
607 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
608 : : errmsg("ON CONFLICT does not support deferrable unique constraints/exclusion constraints as arbiters"),
609 : : errtableconstraint(heapRelation,
610 : : RelationGetRelationName(indexRelation))));
611 : :
612 : 4827 : checkedIndex = true;
613 : :
614 : : /* Check for partial index */
615 [ + + ]: 4827 : if (indexInfo->ii_Predicate != NIL)
616 : : {
617 : : ExprState *predicate;
618 : :
619 : : /*
620 : : * If predicate state not set up yet, create it (in the estate's
621 : : * per-query context)
622 : : */
623 : 18 : predicate = indexInfo->ii_PredicateState;
3098 624 [ + - ]: 18 : if (predicate == NULL)
625 : : {
626 : 18 : predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
3774 627 : 18 : indexInfo->ii_PredicateState = predicate;
628 : : }
629 : :
630 : : /* Skip this index-update if the predicate isn't satisfied */
3098 631 [ - + ]: 18 : if (!ExecQual(predicate, econtext))
3774 andres@anarazel.de 632 :UBC 0 : continue;
633 : : }
634 : :
635 : : /*
636 : : * FormIndexDatum fills in its values and isnull parameters with the
637 : : * appropriate values for the column(s) of the index.
638 : : */
3774 andres@anarazel.de 639 :CBC 4827 : FormIndexDatum(indexInfo,
640 : : slot,
641 : : estate,
642 : : values,
643 : : isnull);
644 : :
645 : : satisfiesConstraint =
646 : 4827 : check_exclusion_or_unique_constraint(heapRelation, indexRelation,
647 : : indexInfo, tupleid,
648 : : values, isnull, estate, false,
649 : : CEOUC_WAIT, true,
650 : : conflictTid);
651 [ + + ]: 4826 : if (!satisfiesConstraint)
652 : 2752 : return false;
653 : : }
654 : :
655 [ + + - + ]: 2071 : if (arbiterIndexes != NIL && !checkedIndex)
3774 andres@anarazel.de 656 [ # # ]:UBC 0 : elog(ERROR, "unexpected failure to find arbiter index");
657 : :
3774 andres@anarazel.de 658 :CBC 2071 : return true;
659 : : }
660 : :
661 : : /*
662 : : * Check for violation of an exclusion or unique constraint
663 : : *
664 : : * heap: the table containing the new tuple
665 : : * index: the index supporting the constraint
666 : : * indexInfo: info about the index, including the exclusion properties
667 : : * tupleid: heap TID of the new tuple we have just inserted (invalid if we
668 : : * haven't inserted a new tuple yet)
669 : : * values, isnull: the *index* column values computed for the new tuple
670 : : * estate: an EState we can do evaluation in
671 : : * newIndex: if true, we are trying to build a new index (this affects
672 : : * only the wording of error messages)
673 : : * waitMode: whether to wait for concurrent inserters/deleters
674 : : * violationOK: if true, don't throw error for violation
675 : : * conflictTid: if not-NULL, the TID of the conflicting tuple is returned here
676 : : *
677 : : * Returns true if OK, false if actual or potential violation
678 : : *
679 : : * 'waitMode' determines what happens if a conflict is detected with a tuple
680 : : * that was inserted or deleted by a transaction that's still running.
681 : : * CEOUC_WAIT means that we wait for the transaction to commit, before
682 : : * throwing an error or returning. CEOUC_NOWAIT means that we report the
683 : : * violation immediately; so the violation is only potential, and the caller
684 : : * must recheck sometime later. This behavior is convenient for deferred
685 : : * exclusion checks; we need not bother queuing a deferred event if there is
686 : : * definitely no conflict at insertion time.
687 : : *
688 : : * CEOUC_LIVELOCK_PREVENTING_WAIT is like CEOUC_NOWAIT, but we will sometimes
689 : : * wait anyway, to prevent livelocking if two transactions try inserting at
690 : : * the same time. This is used with speculative insertions, for INSERT ON
691 : : * CONFLICT statements. (See notes in file header)
692 : : *
693 : : * If violationOK is true, we just report the potential or actual violation to
694 : : * the caller by returning 'false'. Otherwise we throw a descriptive error
695 : : * message here. When violationOK is false, a false result is impossible.
696 : : *
697 : : * Note: The indexam is normally responsible for checking unique constraints,
698 : : * so this normally only needs to be used for exclusion constraints. But this
699 : : * function is also called when doing a "pre-check" for conflicts on a unique
700 : : * constraint, when doing speculative insertion. Caller may use the returned
701 : : * conflict TID to take further steps.
702 : : */
703 : : static bool
704 : 6033 : check_exclusion_or_unique_constraint(Relation heap, Relation index,
705 : : IndexInfo *indexInfo,
706 : : ItemPointer tupleid,
707 : : const Datum *values, const bool *isnull,
708 : : EState *estate, bool newIndex,
709 : : CEOUC_WAIT_MODE waitMode,
710 : : bool violationOK,
711 : : ItemPointer conflictTid)
712 : : {
713 : : Oid *constr_procs;
714 : : uint16 *constr_strats;
3788 heikki.linnakangas@i 715 : 6033 : Oid *index_collations = index->rd_indcollation;
2709 teodor@sigaev.ru 716 : 6033 : int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
717 : : IndexScanDesc index_scan;
718 : : ScanKeyData scankeys[INDEX_MAX_KEYS];
719 : : SnapshotData DirtySnapshot;
720 : : int i;
721 : : bool conflict;
722 : : bool found_self;
723 : : ExprContext *econtext;
724 : : TupleTableSlot *existing_slot;
725 : : TupleTableSlot *save_scantuple;
726 : :
3774 andres@anarazel.de 727 [ + + ]: 6033 : if (indexInfo->ii_ExclusionOps)
728 : : {
729 : 1284 : constr_procs = indexInfo->ii_ExclusionProcs;
730 : 1284 : constr_strats = indexInfo->ii_ExclusionStrats;
731 : : }
732 : : else
733 : : {
734 : 4749 : constr_procs = indexInfo->ii_UniqueProcs;
735 : 4749 : constr_strats = indexInfo->ii_UniqueStrats;
736 : : }
737 : :
738 : : /*
739 : : * If this is a WITHOUT OVERLAPS constraint, we must also forbid empty
740 : : * ranges/multiranges. This must happen before we look for NULLs below, or
741 : : * a UNIQUE constraint could insert an empty range along with a NULL
742 : : * scalar part.
743 : : */
354 peter@eisentraut.org 744 [ + + ]: 6033 : if (indexInfo->ii_WithoutOverlaps)
745 : : {
746 : : /*
747 : : * Look up the type from the heap tuple, but check the Datum from the
748 : : * index tuple.
749 : : */
750 : 1081 : AttrNumber attno = indexInfo->ii_IndexAttrNumbers[indnkeyatts - 1];
751 : :
752 [ + + ]: 1081 : if (!isnull[indnkeyatts - 1])
753 : : {
754 : 1051 : TupleDesc tupdesc = RelationGetDescr(heap);
755 : 1051 : Form_pg_attribute att = TupleDescAttr(tupdesc, attno - 1);
756 : 1051 : TypeCacheEntry *typcache = lookup_type_cache(att->atttypid, 0);
757 : :
758 : 1051 : ExecWithoutOverlapsNotEmpty(heap, att->attname,
759 : 1051 : values[indnkeyatts - 1],
760 : 1051 : typcache->typtype, att->atttypid);
761 : : }
762 : : }
763 : :
764 : : /*
765 : : * If any of the input values are NULL, and the index uses the default
766 : : * nulls-are-distinct mode, the constraint check is assumed to pass (i.e.,
767 : : * we assume the operators are strict). Otherwise, we interpret the
768 : : * constraint as specifying IS NULL for each column whose input value is
769 : : * NULL.
770 : : */
1129 tgl@sss.pgh.pa.us 771 [ + + ]: 5991 : if (!indexInfo->ii_NullsNotDistinct)
772 : : {
773 [ + + ]: 13036 : for (i = 0; i < indnkeyatts; i++)
774 : : {
775 [ + + ]: 7108 : if (isnull[i])
776 : 60 : return true;
777 : : }
778 : : }
779 : :
780 : : /*
781 : : * Search the tuples that are in the index for any violations, including
782 : : * tuples that aren't visible yet.
783 : : */
3788 heikki.linnakangas@i 784 : 5931 : InitDirtySnapshot(DirtySnapshot);
785 : :
2709 teodor@sigaev.ru 786 [ + + ]: 12952 : for (i = 0; i < indnkeyatts; i++)
787 : : {
3788 heikki.linnakangas@i 788 : 7021 : ScanKeyEntryInitialize(&scankeys[i],
1129 tgl@sss.pgh.pa.us 789 : 7021 : isnull[i] ? SK_ISNULL | SK_SEARCHNULL : 0,
3788 heikki.linnakangas@i 790 : 7021 : i + 1,
791 : 7021 : constr_strats[i],
792 : : InvalidOid,
793 : 7021 : index_collations[i],
794 : 7021 : constr_procs[i],
795 [ + + ]: 7021 : values[i]);
796 : : }
797 : :
798 : : /*
799 : : * Need a TupleTableSlot to put existing tuples in.
800 : : *
801 : : * To use FormIndexDatum, we have to make the econtext's scantuple point
802 : : * to this slot. Be sure to save and restore caller's value for
803 : : * scantuple.
804 : : */
2371 andres@anarazel.de 805 : 5931 : existing_slot = table_slot_create(heap, NULL);
806 : :
3788 heikki.linnakangas@i 807 [ + - ]: 5931 : econtext = GetPerTupleExprContext(estate);
808 : 5931 : save_scantuple = econtext->ecxt_scantuple;
809 : 5931 : econtext->ecxt_scantuple = existing_slot;
810 : :
811 : : /*
812 : : * May have to restart scan from this point if a potential conflict is
813 : : * found.
814 : : */
815 : 5967 : retry:
816 : 5967 : conflict = false;
817 : 5967 : found_self = false;
179 pg@bowt.ie 818 : 5967 : index_scan = index_beginscan(heap, index, &DirtySnapshot, NULL, indnkeyatts, 0);
2709 teodor@sigaev.ru 819 : 5967 : index_rescan(index_scan, scankeys, indnkeyatts, NULL, 0);
820 : :
2371 andres@anarazel.de 821 [ + + ]: 7012 : while (index_getnext_slot(index_scan, ForwardScanDirection, existing_slot))
822 : : {
823 : : TransactionId xwait;
824 : : XLTW_Oper reason_wait;
825 : : Datum existing_values[INDEX_MAX_KEYS];
826 : : bool existing_isnull[INDEX_MAX_KEYS];
827 : : char *error_new;
828 : : char *error_existing;
829 : :
830 : : /*
831 : : * Ignore the entry for the tuple we're trying to check.
832 : : */
3774 833 [ + + + + ]: 5117 : if (ItemPointerIsValid(tupleid) &&
2371 834 : 1187 : ItemPointerEquals(tupleid, &existing_slot->tts_tid))
835 : : {
3788 heikki.linnakangas@i 836 [ - + ]: 1018 : if (found_self) /* should not happen */
3788 heikki.linnakangas@i 837 [ # # ]:UBC 0 : elog(ERROR, "found self tuple multiple times in index \"%s\"",
838 : : RelationGetRelationName(index));
3788 heikki.linnakangas@i 839 :CBC 1018 : found_self = true;
840 : 1045 : continue;
841 : : }
842 : :
843 : : /*
844 : : * Extract the index column values and isnull flags from the existing
845 : : * tuple.
846 : : */
847 : 2912 : FormIndexDatum(indexInfo, existing_slot, estate,
848 : : existing_values, existing_isnull);
849 : :
850 : : /* If lossy indexscan, must recheck the condition */
851 [ + + ]: 2912 : if (index_scan->xs_recheck)
852 : : {
853 [ + + ]: 69 : if (!index_recheck_constraint(index,
854 : : constr_procs,
855 : : existing_values,
856 : : existing_isnull,
857 : : values))
858 : 27 : continue; /* tuple doesn't actually match, so no
859 : : * conflict */
860 : : }
861 : :
862 : : /*
863 : : * At this point we have either a conflict or a potential conflict.
864 : : *
865 : : * If an in-progress transaction is affecting the visibility of this
866 : : * tuple, we need to wait for it to complete and then recheck (unless
867 : : * the caller requested not to). For simplicity we do rechecking by
868 : : * just restarting the whole scan --- this case probably doesn't
869 : : * happen often enough to be worth trying harder, and anyway we don't
870 : : * want to hold any index internal locks while waiting.
871 : : */
872 : 5770 : xwait = TransactionIdIsValid(DirtySnapshot.xmin) ?
873 [ + + ]: 2885 : DirtySnapshot.xmin : DirtySnapshot.xmax;
874 : :
3774 andres@anarazel.de 875 [ + + - + ]: 2885 : if (TransactionIdIsValid(xwait) &&
3774 andres@anarazel.de 876 [ # # ]:UBC 0 : (waitMode == CEOUC_WAIT ||
877 : 0 : (waitMode == CEOUC_LIVELOCK_PREVENTING_WAIT &&
878 [ # # # # ]: 0 : DirtySnapshot.speculativeToken &&
879 : 0 : TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
880 : : {
3462 sfrost@snowman.net 881 :CBC 74 : reason_wait = indexInfo->ii_ExclusionOps ?
882 [ - + ]: 37 : XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
3788 heikki.linnakangas@i 883 : 37 : index_endscan(index_scan);
3774 andres@anarazel.de 884 [ + + ]: 37 : if (DirtySnapshot.speculativeToken)
885 : 1 : SpeculativeInsertionWait(DirtySnapshot.xmin,
886 : : DirtySnapshot.speculativeToken);
887 : : else
2371 888 : 36 : XactLockTableWait(xwait, heap,
2371 andres@anarazel.de 889 :GIC 36 : &existing_slot->tts_tid, reason_wait);
3788 heikki.linnakangas@i 890 :CBC 36 : goto retry;
891 : : }
892 : :
893 : : /*
894 : : * We have a definite conflict (or a potential one, but the caller
895 : : * didn't want to wait). Return it to caller, or report it.
896 : : */
3774 andres@anarazel.de 897 [ + + ]: 2848 : if (violationOK)
898 : : {
899 : 2764 : conflict = true;
900 [ + + ]: 2764 : if (conflictTid)
2371 901 : 2752 : *conflictTid = existing_slot->tts_tid;
3774 902 : 2764 : break;
903 : : }
904 : :
3788 heikki.linnakangas@i 905 : 84 : error_new = BuildIndexValueDescription(index, values, isnull);
906 : 84 : error_existing = BuildIndexValueDescription(index, existing_values,
907 : : existing_isnull);
908 [ + + ]: 84 : if (newIndex)
909 [ + - + - : 18 : ereport(ERROR,
+ - ]
910 : : (errcode(ERRCODE_EXCLUSION_VIOLATION),
911 : : errmsg("could not create exclusion constraint \"%s\"",
912 : : RelationGetRelationName(index)),
913 : : error_new && error_existing ?
914 : : errdetail("Key %s conflicts with key %s.",
915 : : error_new, error_existing) :
916 : : errdetail("Key conflicts exist."),
917 : : errtableconstraint(heap,
918 : : RelationGetRelationName(index))));
919 : : else
920 [ + - + - : 66 : ereport(ERROR,
+ - ]
921 : : (errcode(ERRCODE_EXCLUSION_VIOLATION),
922 : : errmsg("conflicting key value violates exclusion constraint \"%s\"",
923 : : RelationGetRelationName(index)),
924 : : error_new && error_existing ?
925 : : errdetail("Key %s conflicts with existing key %s.",
926 : : error_new, error_existing) :
927 : : errdetail("Key conflicts with existing key."),
928 : : errtableconstraint(heap,
929 : : RelationGetRelationName(index))));
930 : : }
931 : :
932 : 5846 : index_endscan(index_scan);
933 : :
934 : : /*
935 : : * Ordinarily, at this point the search should have found the originally
936 : : * inserted tuple (if any), unless we exited the loop early because of
937 : : * conflict. However, it is possible to define exclusion constraints for
938 : : * which that wouldn't be true --- for instance, if the operator is <>. So
939 : : * we no longer complain if found_self is still false.
940 : : */
941 : :
942 : 5846 : econtext->ecxt_scantuple = save_scantuple;
943 : :
944 : 5846 : ExecDropSingleTupleTableSlot(existing_slot);
945 : :
946 : 5846 : return !conflict;
947 : : }
948 : :
949 : : /*
950 : : * Check for violation of an exclusion constraint
951 : : *
952 : : * This is a dumbed down version of check_exclusion_or_unique_constraint
953 : : * for external callers. They don't need all the special modes.
954 : : */
955 : : void
3774 andres@anarazel.de 956 : 239 : check_exclusion_constraint(Relation heap, Relation index,
957 : : IndexInfo *indexInfo,
958 : : ItemPointer tupleid,
959 : : const Datum *values, const bool *isnull,
960 : : EState *estate, bool newIndex)
961 : : {
962 : 239 : (void) check_exclusion_or_unique_constraint(heap, index, indexInfo, tupleid,
963 : : values, isnull,
964 : : estate, newIndex,
965 : : CEOUC_WAIT, false, NULL);
966 : 200 : }
967 : :
968 : : /*
969 : : * Check existing tuple's index values to see if it really matches the
970 : : * exclusion condition against the new_values. Returns true if conflict.
971 : : */
972 : : static bool
697 peter@eisentraut.org 973 : 69 : index_recheck_constraint(Relation index, const Oid *constr_procs,
974 : : const Datum *existing_values, const bool *existing_isnull,
975 : : const Datum *new_values)
976 : : {
2709 teodor@sigaev.ru 977 : 69 : int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
978 : : int i;
979 : :
980 [ + + ]: 171 : for (i = 0; i < indnkeyatts; i++)
981 : : {
982 : : /* Assume the exclusion operators are strict */
3788 heikki.linnakangas@i 983 [ - + ]: 129 : if (existing_isnull[i])
3788 heikki.linnakangas@i 984 :UBC 0 : return false;
985 : :
3788 heikki.linnakangas@i 986 [ + + ]:CBC 129 : if (!DatumGetBool(OidFunctionCall2Coll(constr_procs[i],
987 : 129 : index->rd_indcollation[i],
988 : 129 : existing_values[i],
989 : 129 : new_values[i])))
990 : 27 : return false;
991 : : }
992 : :
993 : 42 : return true;
994 : : }
995 : :
996 : : /*
997 : : * Check if ExecInsertIndexTuples() should pass indexUnchanged hint.
998 : : *
999 : : * When the executor performs an UPDATE that requires a new round of index
1000 : : * tuples, determine if we should pass 'indexUnchanged' = true hint for one
1001 : : * single index.
1002 : : */
1003 : : static bool
1697 pg@bowt.ie 1004 : 176096 : index_unchanged_by_update(ResultRelInfo *resultRelInfo, EState *estate,
1005 : : IndexInfo *indexInfo, Relation indexRelation)
1006 : : {
1007 : : Bitmapset *updatedCols;
1008 : : Bitmapset *extraUpdatedCols;
1009 : : Bitmapset *allUpdatedCols;
1010 : 176096 : bool hasexpression = false;
1011 : : List *idxExprs;
1012 : :
1013 : : /*
1014 : : * Check cache first
1015 : : */
1333 1016 [ + + ]: 176096 : if (indexInfo->ii_CheckedUnchanged)
1017 : 154126 : return indexInfo->ii_IndexUnchanged;
1018 : 21970 : indexInfo->ii_CheckedUnchanged = true;
1019 : :
1020 : : /*
1021 : : * Check for indexed attribute overlap with updated columns.
1022 : : *
1023 : : * Only do this for key columns. A change to a non-key column within an
1024 : : * INCLUDE index should not be counted here. Non-key column values are
1025 : : * opaque payload state to the index AM, a little like an extra table TID.
1026 : : *
1027 : : * Note that row-level BEFORE triggers won't affect our behavior, since
1028 : : * they don't affect the updatedCols bitmaps generally. It doesn't seem
1029 : : * worth the trouble of checking which attributes were changed directly.
1030 : : */
1031 : 21970 : updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
1032 : 21970 : extraUpdatedCols = ExecGetExtraUpdatedCols(resultRelInfo, estate);
1697 1033 [ + + ]: 23751 : for (int attr = 0; attr < indexInfo->ii_NumIndexKeyAttrs; attr++)
1034 : : {
1035 : 22735 : int keycol = indexInfo->ii_IndexAttrNumbers[attr];
1036 : :
1037 [ + + ]: 22735 : if (keycol <= 0)
1038 : : {
1039 : : /*
1040 : : * Skip expressions for now, but remember to deal with them later
1041 : : * on
1042 : : */
1043 : 15 : hasexpression = true;
1044 : 15 : continue;
1045 : : }
1046 : :
1047 [ + + ]: 22720 : if (bms_is_member(keycol - FirstLowInvalidHeapAttributeNumber,
1048 [ - + ]: 1766 : updatedCols) ||
1049 : 1766 : bms_is_member(keycol - FirstLowInvalidHeapAttributeNumber,
1050 : : extraUpdatedCols))
1051 : : {
1052 : : /* Changed key column -- don't hint for this index */
1333 1053 : 20954 : indexInfo->ii_IndexUnchanged = false;
1697 1054 : 20954 : return false;
1055 : : }
1056 : : }
1057 : :
1058 : : /*
1059 : : * When we get this far and index has no expressions, return true so that
1060 : : * index_insert() call will go on to pass 'indexUnchanged' = true hint.
1061 : : *
1062 : : * The _absence_ of an indexed key attribute that overlaps with updated
1063 : : * attributes (in addition to the total absence of indexed expressions)
1064 : : * shows that the index as a whole is logically unchanged by UPDATE.
1065 : : */
1066 [ + + ]: 1016 : if (!hasexpression)
1067 : : {
1333 1068 : 1004 : indexInfo->ii_IndexUnchanged = true;
1697 1069 : 1004 : return true;
1070 : : }
1071 : :
1072 : : /*
1073 : : * Need to pass only one bms to expression_tree_walker helper function.
1074 : : * Avoid allocating memory in common case where there are no extra cols.
1075 : : */
1076 [ + - ]: 12 : if (!extraUpdatedCols)
1077 : 12 : allUpdatedCols = updatedCols;
1078 : : else
1697 pg@bowt.ie 1079 :UBC 0 : allUpdatedCols = bms_union(updatedCols, extraUpdatedCols);
1080 : :
1081 : : /*
1082 : : * We have to work slightly harder in the event of indexed expressions,
1083 : : * but the principle is the same as before: try to find columns (Vars,
1084 : : * actually) that overlap with known-updated columns.
1085 : : *
1086 : : * If we find any matching Vars, don't pass hint for index. Otherwise
1087 : : * pass hint.
1088 : : */
1697 pg@bowt.ie 1089 :CBC 12 : idxExprs = RelationGetIndexExpressions(indexRelation);
1090 : 12 : hasexpression = index_expression_changed_walker((Node *) idxExprs,
1091 : : allUpdatedCols);
1092 : 12 : list_free(idxExprs);
1093 [ - + ]: 12 : if (extraUpdatedCols)
1697 pg@bowt.ie 1094 :UBC 0 : bms_free(allUpdatedCols);
1095 : :
1697 pg@bowt.ie 1096 [ + + ]:CBC 12 : if (hasexpression)
1097 : : {
1333 1098 : 9 : indexInfo->ii_IndexUnchanged = false;
1697 1099 : 9 : return false;
1100 : : }
1101 : :
1102 : : /*
1103 : : * Deliberately don't consider index predicates. We should even give the
1104 : : * hint when result rel's "updated tuple" has no corresponding index
1105 : : * tuple, which is possible with a partial index (provided the usual
1106 : : * conditions are met).
1107 : : */
1333 1108 : 3 : indexInfo->ii_IndexUnchanged = true;
1697 1109 : 3 : return true;
1110 : : }
1111 : :
1112 : : /*
1113 : : * Indexed expression helper for index_unchanged_by_update().
1114 : : *
1115 : : * Returns true when Var that appears within allUpdatedCols located.
1116 : : */
1117 : : static bool
1118 : 38 : index_expression_changed_walker(Node *node, Bitmapset *allUpdatedCols)
1119 : : {
1120 [ - + ]: 38 : if (node == NULL)
1697 pg@bowt.ie 1121 :UBC 0 : return false;
1122 : :
1697 pg@bowt.ie 1123 [ + + ]:CBC 38 : if (IsA(node, Var))
1124 : : {
1125 : 12 : Var *var = (Var *) node;
1126 : :
1127 [ + + ]: 12 : if (bms_is_member(var->varattno - FirstLowInvalidHeapAttributeNumber,
1128 : : allUpdatedCols))
1129 : : {
1130 : : /* Var was updated -- indicates that we should not hint */
1131 : 9 : return true;
1132 : : }
1133 : :
1134 : : /* Still haven't found a reason to not pass the hint */
1135 : 3 : return false;
1136 : : }
1137 : :
1138 : 26 : return expression_tree_walker(node, index_expression_changed_walker,
1139 : : allUpdatedCols);
1140 : : }
1141 : :
1142 : : /*
1143 : : * ExecWithoutOverlapsNotEmpty - raise an error if the tuple has an empty
1144 : : * range or multirange in the given attribute.
1145 : : */
1146 : : static void
354 peter@eisentraut.org 1147 : 1051 : ExecWithoutOverlapsNotEmpty(Relation rel, NameData attname, Datum attval, char typtype, Oid atttypid)
1148 : : {
1149 : : bool isempty;
1150 : : RangeType *r;
1151 : : MultirangeType *mr;
1152 : :
1153 [ + + - ]: 1051 : switch (typtype)
1154 : : {
1155 : 589 : case TYPTYPE_RANGE:
1156 : 589 : r = DatumGetRangeTypeP(attval);
1157 : 589 : isempty = RangeIsEmpty(r);
1158 : 589 : break;
1159 : 462 : case TYPTYPE_MULTIRANGE:
1160 : 462 : mr = DatumGetMultirangeTypeP(attval);
1161 : 462 : isempty = MultirangeIsEmpty(mr);
1162 : 462 : break;
354 peter@eisentraut.org 1163 :UBC 0 : default:
1164 [ # # ]: 0 : elog(ERROR, "WITHOUT OVERLAPS column \"%s\" is not a range or multirange",
1165 : : NameStr(attname));
1166 : : }
1167 : :
1168 : : /* Report a CHECK_VIOLATION */
354 peter@eisentraut.org 1169 [ + + ]:CBC 1051 : if (isempty)
1170 [ + - ]: 42 : ereport(ERROR,
1171 : : (errcode(ERRCODE_CHECK_VIOLATION),
1172 : : errmsg("empty WITHOUT OVERLAPS value found in column \"%s\" in relation \"%s\"",
1173 : : NameStr(attname), RelationGetRelationName(rel))));
1174 : 1009 : }
|