Age Owner 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-2023, 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/snapmgr.h"
118 :
119 : /* waitMode argument to check_exclusion_or_unique_constraint() */
120 : typedef enum
121 : {
122 : CEOUC_WAIT,
123 : CEOUC_NOWAIT,
124 : CEOUC_LIVELOCK_PREVENTING_WAIT
125 : } CEOUC_WAIT_MODE;
126 :
127 : static bool check_exclusion_or_unique_constraint(Relation heap, Relation index,
128 : IndexInfo *indexInfo,
129 : ItemPointer tupleid,
130 : Datum *values, bool *isnull,
131 : EState *estate, bool newIndex,
132 : CEOUC_WAIT_MODE waitMode,
133 : bool violationOK,
134 : ItemPointer conflictTid);
135 :
136 : static bool index_recheck_constraint(Relation index, Oid *constr_procs,
137 : Datum *existing_values, bool *existing_isnull,
138 : Datum *new_values);
139 : static bool index_unchanged_by_update(ResultRelInfo *resultRelInfo,
140 : EState *estate, IndexInfo *indexInfo,
141 : Relation indexRelation);
142 : static bool index_expression_changed_walker(Node *node,
143 : Bitmapset *allUpdatedCols);
144 :
145 : /* ----------------------------------------------------------------
146 : * ExecOpenIndices
147 : *
148 : * Find the indices associated with a result relation, open them,
149 : * and save information about them in the result ResultRelInfo.
150 : *
151 : * At entry, caller has already opened and locked
152 : * resultRelInfo->ri_RelationDesc.
153 : * ----------------------------------------------------------------
154 : */
155 : void
2893 andres 156 CBC 2371282 : ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
157 : {
2907 heikki.linnakangas 158 2371282 : Relation resultRelation = resultRelInfo->ri_RelationDesc;
159 : List *indexoidlist;
160 : ListCell *l;
161 : int len,
162 : i;
163 : RelationPtr relationDescs;
164 : IndexInfo **indexInfoArray;
165 :
166 2371282 : resultRelInfo->ri_NumIndices = 0;
167 :
168 : /* fast path if no indexes */
169 2371282 : if (!RelationGetForm(resultRelation)->relhasindex)
170 242770 : return;
171 :
172 : /*
173 : * Get cached list of index OIDs
174 : */
175 2128512 : indexoidlist = RelationGetIndexList(resultRelation);
176 2128512 : len = list_length(indexoidlist);
177 2128512 : if (len == 0)
178 20325 : return;
179 :
180 : /*
181 : * allocate space for result arrays
182 : */
183 2108187 : relationDescs = (RelationPtr) palloc(len * sizeof(Relation));
184 2108187 : indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *));
185 :
186 2108187 : resultRelInfo->ri_NumIndices = len;
187 2108187 : resultRelInfo->ri_IndexRelationDescs = relationDescs;
188 2108187 : resultRelInfo->ri_IndexRelationInfo = indexInfoArray;
189 :
190 : /*
191 : * For each index, open the index relation and save pg_index info. We
192 : * acquire RowExclusiveLock, signifying we will update the index.
193 : *
194 : * Note: we do this even if the index is not indisready; it's not worth
195 : * the trouble to optimize for the case where it isn't.
196 : */
197 2108187 : i = 0;
198 6210559 : foreach(l, indexoidlist)
199 : {
200 4102372 : Oid indexOid = lfirst_oid(l);
201 : Relation indexDesc;
202 : IndexInfo *ii;
203 :
204 4102372 : indexDesc = index_open(indexOid, RowExclusiveLock);
205 :
206 : /* extract index key information from the index's pg_index info */
207 4102372 : ii = BuildIndexInfo(indexDesc);
208 :
209 : /*
210 : * If the indexes are to be used for speculative insertion, add extra
211 : * information required by unique index entries.
212 : */
2893 andres 213 4102372 : if (speculative && ii->ii_Unique)
214 603 : BuildSpeculativeIndexInfo(indexDesc, ii);
215 :
2907 heikki.linnakangas 216 4102372 : relationDescs[i] = indexDesc;
217 4102372 : indexInfoArray[i] = ii;
218 4102372 : i++;
219 : }
220 :
221 2108187 : list_free(indexoidlist);
222 : }
223 :
224 : /* ----------------------------------------------------------------
225 : * ExecCloseIndices
226 : *
227 : * Close the index relations stored in resultRelInfo
228 : * ----------------------------------------------------------------
229 : */
230 : void
231 2421886 : ExecCloseIndices(ResultRelInfo *resultRelInfo)
232 : {
233 : int i;
234 : int numIndices;
235 : RelationPtr indexDescs;
236 :
237 2421886 : numIndices = resultRelInfo->ri_NumIndices;
238 2421886 : indexDescs = resultRelInfo->ri_IndexRelationDescs;
239 :
240 6523506 : for (i = 0; i < numIndices; i++)
241 : {
242 4101620 : if (indexDescs[i] == NULL)
2907 heikki.linnakangas 243 UBC 0 : continue; /* shouldn't happen? */
244 :
245 : /* Drop lock acquired by ExecOpenIndices */
2907 heikki.linnakangas 246 CBC 4101620 : index_close(indexDescs[i], RowExclusiveLock);
247 : }
248 :
249 : /*
250 : * XXX should free indexInfo array here too? Currently we assume that
251 : * such stuff will be cleaned up automatically in FreeExecutorState.
252 : */
253 2421886 : }
254 :
255 : /* ----------------------------------------------------------------
256 : * ExecInsertIndexTuples
257 : *
258 : * This routine takes care of inserting index tuples
259 : * into all the relations indexing the result relation
260 : * when a heap tuple is inserted into the result relation.
261 : *
262 : * When 'update' is true and 'onlySummarizing' is false,
263 : * executor is performing an UPDATE that could not use an
264 : * optimization like heapam's HOT (in more general terms a
265 : * call to table_tuple_update() took place and set
266 : * 'update_indexes' to TUUI_All). Receiving this hint makes
267 : * us consider if we should pass down the 'indexUnchanged'
268 : * hint in turn. That's something that we figure out for
269 : * each index_insert() call iff 'update' is true.
270 : * (When 'update' is false we already know not to pass the
271 : * hint to any index.)
272 : *
273 : * If onlySummarizing is set, an equivalent optimization to
274 : * HOT has been applied and any updated columns are indexed
275 : * only by summarizing indexes (or in more general terms a
276 : * call to table_tuple_update() took place and set
277 : * 'update_indexes' to TUUI_Summarizing). We can (and must)
278 : * therefore only update the indexes that have
279 : * 'amsummarizing' = true.
280 : *
281 : * Unique and exclusion constraints are enforced at the same
282 : * time. This returns a list of index OIDs for any unique or
283 : * exclusion constraints that are deferred and that had
284 : * potential (unconfirmed) conflicts. (if noDupErr == true,
285 : * the same is done for non-deferred constraints, but report
286 : * if conflict was speculative or deferred conflict to caller)
287 : *
288 : * If 'arbiterIndexes' is nonempty, noDupErr applies only to
289 : * those indexes. NIL means noDupErr applies to all indexes.
290 : * ----------------------------------------------------------------
291 : */
292 : List *
907 heikki.linnakangas 293 GIC 2009170 : ExecInsertIndexTuples(ResultRelInfo *resultRelInfo,
294 : TupleTableSlot *slot,
295 : EState *estate,
296 : bool update,
297 : bool noDupErr,
298 : bool *specConflict,
299 : List *arbiterIndexes,
300 : bool onlySummarizing)
301 : {
1478 andres 302 2009170 : ItemPointer tupleid = &slot->tts_tid;
2907 heikki.linnakangas 303 CBC 2009170 : List *result = NIL;
304 : int i;
305 : int numIndices;
306 : RelationPtr relationDescs;
307 : Relation heapRelation;
308 : IndexInfo **indexInfoArray;
309 : ExprContext *econtext;
310 : Datum values[INDEX_MAX_KEYS];
311 : bool isnull[INDEX_MAX_KEYS];
2907 heikki.linnakangas 312 ECB :
1478 andres 313 CBC 2009170 : Assert(ItemPointerIsValid(tupleid));
314 :
315 : /*
316 : * Get information from the result relation info structure.
317 : */
2907 heikki.linnakangas 318 GIC 2009170 : numIndices = resultRelInfo->ri_NumIndices;
319 2009170 : relationDescs = resultRelInfo->ri_IndexRelationDescs;
320 2009170 : indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
321 2009170 : heapRelation = resultRelInfo->ri_RelationDesc;
322 :
1404 drowley 323 ECB : /* Sanity check: slot must belong to the same rel as the resultRelInfo. */
1404 drowley 324 GIC 2009170 : Assert(slot->tts_tableOid == RelationGetRelid(heapRelation));
325 :
326 : /*
327 : * We will use the EState's per-tuple context for evaluating predicates
2907 heikki.linnakangas 328 ECB : * and index expressions (creating it if it's not already there).
329 : */
2907 heikki.linnakangas 330 CBC 2009170 : econtext = GetPerTupleExprContext(estate);
2907 heikki.linnakangas 331 ECB :
332 : /* Arrange for econtext's scan tuple to be the tuple under test */
2907 heikki.linnakangas 333 GIC 2009170 : econtext->ecxt_scantuple = slot;
2907 heikki.linnakangas 334 ECB :
335 : /*
336 : * for each index, form and insert the index tuple
337 : */
2907 heikki.linnakangas 338 GIC 4251376 : for (i = 0; i < numIndices; i++)
339 : {
2907 heikki.linnakangas 340 CBC 2242491 : Relation indexRelation = relationDescs[i];
341 : IndexInfo *indexInfo;
342 : bool applyNoDupErr;
2907 heikki.linnakangas 343 ECB : IndexUniqueCheck checkUnique;
344 : bool indexUnchanged;
345 : bool satisfiesConstraint;
346 :
2907 heikki.linnakangas 347 GIC 2242491 : if (indexRelation == NULL)
2907 heikki.linnakangas 348 LBC 0 : continue;
349 :
2907 heikki.linnakangas 350 CBC 2242491 : indexInfo = indexInfoArray[i];
351 :
352 : /* If the index is marked as read-only, ignore it */
2907 heikki.linnakangas 353 GIC 2242491 : if (!indexInfo->ii_ReadyForInserts)
354 106 : continue;
355 :
356 : /*
357 : * Skip processing of non-summarizing indexes if we only
358 : * update summarizing indexes
359 : */
20 tomas.vondra 360 GNC 2242385 : if (onlySummarizing && !indexInfo->ii_Summarizing)
361 3 : continue;
362 :
363 : /* Check for partial index */
2907 heikki.linnakangas 364 CBC 2242382 : if (indexInfo->ii_Predicate != NIL)
2907 heikki.linnakangas 365 EUB : {
366 : ExprState *predicate;
2907 heikki.linnakangas 367 ECB :
368 : /*
369 : * If predicate state not set up yet, create it (in the estate's
370 : * per-query context)
371 : */
2907 heikki.linnakangas 372 GIC 200545 : predicate = indexInfo->ii_PredicateState;
2217 andres 373 200545 : if (predicate == NULL)
374 : {
375 130 : predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
2907 heikki.linnakangas 376 130 : indexInfo->ii_PredicateState = predicate;
2907 heikki.linnakangas 377 ECB : }
378 :
379 : /* Skip this index-update if the predicate isn't satisfied */
2217 andres 380 GIC 200545 : if (!ExecQual(predicate, econtext))
2907 heikki.linnakangas 381 CBC 200274 : continue;
382 : }
383 :
384 : /*
385 : * FormIndexDatum fills in its values and isnull parameters with the
386 : * appropriate values for the column(s) of the index.
387 : */
2907 heikki.linnakangas 388 GIC 2042108 : FormIndexDatum(indexInfo,
2907 heikki.linnakangas 389 ECB : slot,
390 : estate,
391 : values,
392 : isnull);
393 :
394 : /* Check whether to apply noDupErr to this index */
2470 tgl 395 GIC 2044103 : applyNoDupErr = noDupErr &&
396 1995 : (arbiterIndexes == NIL ||
2470 tgl 397 CBC 1995 : list_member_oid(arbiterIndexes,
398 1995 : indexRelation->rd_index->indexrelid));
399 :
400 : /*
401 : * The index AM does the actual insertion, plus uniqueness checking.
402 : *
403 : * For an immediate-mode unique index, we just tell the index AM to
404 : * throw error if not unique.
2907 heikki.linnakangas 405 ECB : *
406 : * For a deferrable unique index, we tell the index AM to just detect
407 : * possible non-uniqueness, and we add the index OID to the result
408 : * list if further checking is needed.
409 : *
410 : * For a speculative insertion (used by INSERT ... ON CONFLICT), do
411 : * the same as for a deferrable unique index.
412 : */
2907 heikki.linnakangas 413 CBC 2042108 : if (!indexRelation->rd_index->indisunique)
414 930743 : checkUnique = UNIQUE_CHECK_NO;
2470 tgl 415 1111365 : else if (applyNoDupErr)
2893 andres 416 GIC 2016 : checkUnique = UNIQUE_CHECK_PARTIAL;
2907 heikki.linnakangas 417 1109349 : else if (indexRelation->rd_index->indimmediate)
418 1109274 : checkUnique = UNIQUE_CHECK_YES;
419 : else
420 75 : checkUnique = UNIQUE_CHECK_PARTIAL;
421 :
422 : /*
423 : * There's definitely going to be an index_insert() call for this
424 : * index. If we're being called as part of an UPDATE statement,
425 : * consider if the 'indexUnchanged' = true hint should be passed.
426 : */
816 pg 427 2042108 : indexUnchanged = update && index_unchanged_by_update(resultRelInfo,
428 : estate,
429 : indexInfo,
816 pg 430 ECB : indexRelation);
431 :
2907 heikki.linnakangas 432 : satisfiesConstraint =
2907 heikki.linnakangas 433 CBC 2042108 : index_insert(indexRelation, /* index relation */
2907 heikki.linnakangas 434 ECB : values, /* array of index Datums */
435 : isnull, /* null flags */
436 : tupleid, /* tid of heap tuple */
437 : heapRelation, /* heap relation */
438 : checkUnique, /* type of uniqueness check to do */
439 : indexUnchanged, /* UPDATE without logical change? */
440 : indexInfo); /* index AM may need this */
441 :
442 : /*
443 : * If the index has an associated exclusion constraint, check that.
444 : * This is simpler than the process for uniqueness checks since we
445 : * always insert first and then check. If the constraint is deferred,
446 : * we check now anyway, but don't throw error on violation or wait for
447 : * a conclusive outcome from a concurrent insertion; instead we'll
448 : * queue a recheck event. Similarly, noDupErr callers (speculative
449 : * inserters) will recheck later, and wait for a conclusive outcome
2893 andres 450 : * then.
451 : *
452 : * An index for an exclusion constraint can't also be UNIQUE (not an
453 : * essential property, we just don't allow it in the grammar), so no
454 : * need to preserve the prior state of satisfiesConstraint.
455 : */
2907 heikki.linnakangas 456 GIC 2041848 : if (indexInfo->ii_ExclusionOps != NULL)
457 : {
458 : bool violationOK;
459 : CEOUC_WAIT_MODE waitMode;
460 :
2470 tgl 461 153 : if (applyNoDupErr)
462 : {
2893 andres 463 UIC 0 : violationOK = true;
464 0 : waitMode = CEOUC_LIVELOCK_PREVENTING_WAIT;
465 : }
2893 andres 466 GIC 153 : else if (!indexRelation->rd_index->indimmediate)
467 : {
468 21 : violationOK = true;
469 21 : waitMode = CEOUC_NOWAIT;
470 : }
471 : else
472 : {
2893 andres 473 CBC 132 : violationOK = false;
2893 andres 474 GIC 132 : waitMode = CEOUC_WAIT;
475 : }
476 :
477 : satisfiesConstraint =
2893 andres 478 CBC 153 : check_exclusion_or_unique_constraint(heapRelation,
479 : indexRelation, indexInfo,
2893 andres 480 EUB : tupleid, values, isnull,
481 : estate, false,
482 : waitMode, violationOK, NULL);
2907 heikki.linnakangas 483 ECB : }
484 :
2907 heikki.linnakangas 485 CBC 2041823 : if ((checkUnique == UNIQUE_CHECK_PARTIAL ||
486 2039732 : indexInfo->ii_ExclusionOps != NULL) &&
2907 heikki.linnakangas 487 GIC 2219 : !satisfiesConstraint)
488 : {
489 : /*
2907 heikki.linnakangas 490 ECB : * The tuple potentially violates the uniqueness or exclusion
491 : * constraint, so make a note of the index so that we can re-check
492 : * it later. Speculative inserters are told if there was a
493 : * speculative conflict, since that always requires a restart.
494 : */
2907 heikki.linnakangas 495 CBC 66 : result = lappend_oid(result, RelationGetRelid(indexRelation));
2893 andres 496 GIC 66 : if (indexRelation->rd_index->indimmediate && specConflict)
497 5 : *specConflict = true;
498 : }
499 : }
500 :
2907 heikki.linnakangas 501 2008885 : return result;
2907 heikki.linnakangas 502 ECB : }
503 :
2893 andres 504 : /* ----------------------------------------------------------------
505 : * ExecCheckIndexConstraints
506 : *
507 : * This routine checks if a tuple violates any unique or
508 : * exclusion constraints. Returns true if there is no conflict.
509 : * Otherwise returns false, and the TID of the conflicting
510 : * tuple is returned in *conflictTid.
511 : *
512 : * If 'arbiterIndexes' is given, only those indexes are checked.
513 : * NIL means all indexes.
514 : *
515 : * Note that this doesn't lock the values in any way, so it's
516 : * possible that a conflicting tuple is inserted immediately
517 : * after this returns. But this can be used for a pre-check
518 : * before insertion.
519 : * ----------------------------------------------------------------
520 : */
521 : bool
907 heikki.linnakangas 522 GIC 4695 : ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
523 : EState *estate, ItemPointer conflictTid,
524 : List *arbiterIndexes)
525 : {
526 : int i;
527 : int numIndices;
528 : RelationPtr relationDescs;
529 : Relation heapRelation;
530 : IndexInfo **indexInfoArray;
531 : ExprContext *econtext;
532 : Datum values[INDEX_MAX_KEYS];
533 : bool isnull[INDEX_MAX_KEYS];
534 : ItemPointerData invalidItemPtr;
2893 andres 535 4695 : bool checkedIndex = false;
536 :
537 4695 : ItemPointerSetInvalid(conflictTid);
538 4695 : ItemPointerSetInvalid(&invalidItemPtr);
2893 andres 539 ECB :
540 : /*
541 : * Get information from the result relation info structure.
542 : */
2893 andres 543 GIC 4695 : numIndices = resultRelInfo->ri_NumIndices;
544 4695 : relationDescs = resultRelInfo->ri_IndexRelationDescs;
545 4695 : indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
546 4695 : heapRelation = resultRelInfo->ri_RelationDesc;
547 :
548 : /*
549 : * We will use the EState's per-tuple context for evaluating predicates
550 : * and index expressions (creating it if it's not already there).
551 : */
2893 andres 552 CBC 4695 : econtext = GetPerTupleExprContext(estate);
553 :
2893 andres 554 ECB : /* Arrange for econtext's scan tuple to be the tuple under test */
2893 andres 555 CBC 4695 : econtext->ecxt_scantuple = slot;
556 :
557 : /*
558 : * For each index, form index tuple and check if it satisfies the
559 : * constraint.
2893 andres 560 ECB : */
2893 andres 561 CBC 6752 : for (i = 0; i < numIndices; i++)
2893 andres 562 ECB : {
2893 andres 563 CBC 4739 : Relation indexRelation = relationDescs[i];
564 : IndexInfo *indexInfo;
565 : bool satisfiesConstraint;
566 :
2893 andres 567 GIC 4739 : if (indexRelation == NULL)
2893 andres 568 UIC 0 : continue;
2893 andres 569 ECB :
2893 andres 570 GIC 4739 : indexInfo = indexInfoArray[i];
571 :
2893 andres 572 CBC 4739 : if (!indexInfo->ii_Unique && !indexInfo->ii_ExclusionOps)
2893 andres 573 GIC 2 : continue;
574 :
575 : /* If the index is marked as read-only, ignore it */
576 4737 : if (!indexInfo->ii_ReadyForInserts)
2893 andres 577 UIC 0 : continue;
2893 andres 578 ECB :
579 : /* When specific arbiter indexes requested, only examine them */
2893 andres 580 CBC 4737 : if (arbiterIndexes != NIL &&
2893 andres 581 GIC 4647 : !list_member_oid(arbiterIndexes,
582 4647 : indexRelation->rd_index->indexrelid))
583 39 : continue;
2893 andres 584 ECB :
2893 andres 585 GBC 4698 : if (!indexRelation->rd_index->indimmediate)
2893 andres 586 GIC 3 : ereport(ERROR,
2735 rhaas 587 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
588 : errmsg("ON CONFLICT does not support deferrable unique constraints/exclusion constraints as arbiters"),
2893 andres 589 : errtableconstraint(heapRelation,
2118 tgl 590 : RelationGetRelationName(indexRelation))));
591 :
2893 andres 592 GIC 4695 : checkedIndex = true;
2893 andres 593 ECB :
2893 andres 594 EUB : /* Check for partial index */
2893 andres 595 GIC 4695 : if (indexInfo->ii_Predicate != NIL)
596 : {
2217 andres 597 ECB : ExprState *predicate;
2893 598 :
599 : /*
600 : * If predicate state not set up yet, create it (in the estate's
601 : * per-query context)
602 : */
2893 andres 603 CBC 15 : predicate = indexInfo->ii_PredicateState;
2217 andres 604 GIC 15 : if (predicate == NULL)
605 : {
606 15 : predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
2893 607 15 : indexInfo->ii_PredicateState = predicate;
608 : }
2893 andres 609 ECB :
610 : /* Skip this index-update if the predicate isn't satisfied */
2217 andres 611 GIC 15 : if (!ExecQual(predicate, econtext))
2893 andres 612 LBC 0 : continue;
613 : }
614 :
615 : /*
616 : * FormIndexDatum fills in its values and isnull parameters with the
617 : * appropriate values for the column(s) of the index.
618 : */
2893 andres 619 GIC 4695 : FormIndexDatum(indexInfo,
2893 andres 620 ECB : slot,
621 : estate,
622 : values,
623 : isnull);
624 :
625 : satisfiesConstraint =
2893 andres 626 GIC 4695 : check_exclusion_or_unique_constraint(heapRelation, indexRelation,
627 : indexInfo, &invalidItemPtr,
2118 tgl 628 ECB : values, isnull, estate, false,
2893 andres 629 EUB : CEOUC_WAIT, true,
630 : conflictTid);
2893 andres 631 GIC 4695 : if (!satisfiesConstraint)
632 2679 : return false;
633 : }
634 :
635 2013 : if (arbiterIndexes != NIL && !checkedIndex)
2893 andres 636 LBC 0 : elog(ERROR, "unexpected failure to find arbiter index");
637 :
2893 andres 638 GIC 2013 : return true;
639 : }
640 :
641 : /*
642 : * Check for violation of an exclusion or unique constraint
2907 heikki.linnakangas 643 ECB : *
644 : * heap: the table containing the new tuple
645 : * index: the index supporting the constraint
646 : * indexInfo: info about the index, including the exclusion properties
647 : * tupleid: heap TID of the new tuple we have just inserted (invalid if we
2893 andres 648 : * haven't inserted a new tuple yet)
2907 heikki.linnakangas 649 : * values, isnull: the *index* column values computed for the new tuple
650 : * estate: an EState we can do evaluation in
651 : * newIndex: if true, we are trying to build a new index (this affects
652 : * only the wording of error messages)
2893 andres 653 EUB : * waitMode: whether to wait for concurrent inserters/deleters
654 : * violationOK: if true, don't throw error for violation
2893 andres 655 ECB : * conflictTid: if not-NULL, the TID of the conflicting tuple is returned here
656 : *
657 : * Returns true if OK, false if actual or potential violation
658 : *
659 : * 'waitMode' determines what happens if a conflict is detected with a tuple
660 : * that was inserted or deleted by a transaction that's still running.
661 : * CEOUC_WAIT means that we wait for the transaction to commit, before
662 : * throwing an error or returning. CEOUC_NOWAIT means that we report the
663 : * violation immediately; so the violation is only potential, and the caller
664 : * must recheck sometime later. This behavior is convenient for deferred
665 : * exclusion checks; we need not bother queuing a deferred event if there is
666 : * definitely no conflict at insertion time.
667 : *
668 : * CEOUC_LIVELOCK_PREVENTING_WAIT is like CEOUC_NOWAIT, but we will sometimes
669 : * wait anyway, to prevent livelocking if two transactions try inserting at
670 : * the same time. This is used with speculative insertions, for INSERT ON
671 : * CONFLICT statements. (See notes in file header)
672 : *
673 : * If violationOK is true, we just report the potential or actual violation to
674 : * the caller by returning 'false'. Otherwise we throw a descriptive error
675 : * message here. When violationOK is false, a false result is impossible.
676 : *
677 : * Note: The indexam is normally responsible for checking unique constraints,
678 : * so this normally only needs to be used for exclusion constraints. But this
679 : * function is also called when doing a "pre-check" for conflicts on a unique
680 : * constraint, when doing speculative insertion. Caller may use the returned
681 : * conflict TID to take further steps.
682 : */
683 : static bool
2893 andres 684 GIC 4883 : check_exclusion_or_unique_constraint(Relation heap, Relation index,
685 : IndexInfo *indexInfo,
686 : ItemPointer tupleid,
687 : Datum *values, bool *isnull,
688 : EState *estate, bool newIndex,
689 : CEOUC_WAIT_MODE waitMode,
690 : bool violationOK,
691 : ItemPointer conflictTid)
692 : {
693 : Oid *constr_procs;
694 : uint16 *constr_strats;
2907 heikki.linnakangas 695 4883 : Oid *index_collations = index->rd_indcollation;
1828 teodor 696 4883 : int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
697 : IndexScanDesc index_scan;
698 : ScanKeyData scankeys[INDEX_MAX_KEYS];
699 : SnapshotData DirtySnapshot;
700 : int i;
2907 heikki.linnakangas 701 ECB : bool conflict;
702 : bool found_self;
703 : ExprContext *econtext;
704 : TupleTableSlot *existing_slot;
705 : TupleTableSlot *save_scantuple;
706 :
2893 andres 707 GIC 4883 : if (indexInfo->ii_ExclusionOps)
708 : {
709 194 : constr_procs = indexInfo->ii_ExclusionProcs;
710 194 : constr_strats = indexInfo->ii_ExclusionStrats;
711 : }
2893 andres 712 ECB : else
713 : {
2893 andres 714 GIC 4689 : constr_procs = indexInfo->ii_UniqueProcs;
715 4689 : constr_strats = indexInfo->ii_UniqueStrats;
716 : }
717 :
718 : /*
719 : * If any of the input values are NULL, and the index uses the default
720 : * nulls-are-distinct mode, the constraint check is assumed to pass (i.e.,
721 : * we assume the operators are strict). Otherwise, we interpret the
722 : * constraint as specifying IS NULL for each column whose input value is
723 : * NULL.
2907 heikki.linnakangas 724 ECB : */
248 tgl 725 GIC 4883 : if (!indexInfo->ii_NullsNotDistinct)
2907 heikki.linnakangas 726 ECB : {
248 tgl 727 CBC 9848 : for (i = 0; i < indnkeyatts; i++)
728 : {
248 tgl 729 GIC 4968 : if (isnull[i])
248 tgl 730 UIC 0 : return true;
248 tgl 731 ECB : }
2907 heikki.linnakangas 732 : }
733 :
734 : /*
735 : * Search the tuples that are in the index for any violations, including
736 : * tuples that aren't visible yet.
737 : */
2907 heikki.linnakangas 738 GIC 4883 : InitDirtySnapshot(DirtySnapshot);
739 :
1828 teodor 740 9854 : for (i = 0; i < indnkeyatts; i++)
741 : {
2907 heikki.linnakangas 742 CBC 4971 : ScanKeyEntryInitialize(&scankeys[i],
248 tgl 743 GIC 4971 : isnull[i] ? SK_ISNULL | SK_SEARCHNULL : 0,
2907 heikki.linnakangas 744 CBC 4971 : i + 1,
2907 heikki.linnakangas 745 GIC 4971 : constr_strats[i],
2907 heikki.linnakangas 746 ECB : InvalidOid,
2907 heikki.linnakangas 747 GBC 4971 : index_collations[i],
2907 heikki.linnakangas 748 GIC 4971 : constr_procs[i],
749 4971 : values[i]);
750 : }
751 :
752 : /*
753 : * Need a TupleTableSlot to put existing tuples in.
754 : *
2907 heikki.linnakangas 755 ECB : * To use FormIndexDatum, we have to make the econtext's scantuple point
756 : * to this slot. Be sure to save and restore caller's value for
757 : * scantuple.
758 : */
1490 andres 759 CBC 4883 : existing_slot = table_slot_create(heap, NULL);
2907 heikki.linnakangas 760 ECB :
2907 heikki.linnakangas 761 CBC 4883 : econtext = GetPerTupleExprContext(estate);
762 4883 : save_scantuple = econtext->ecxt_scantuple;
2907 heikki.linnakangas 763 GIC 4883 : econtext->ecxt_scantuple = existing_slot;
2907 heikki.linnakangas 764 ECB :
765 : /*
766 : * May have to restart scan from this point if a potential conflict is
767 : * found.
768 : */
2907 heikki.linnakangas 769 GIC 4918 : retry:
770 4918 : conflict = false;
771 4918 : found_self = false;
1828 teodor 772 4918 : index_scan = index_beginscan(heap, index, &DirtySnapshot, indnkeyatts, 0);
773 4918 : index_rescan(index_scan, scankeys, indnkeyatts, NULL, 0);
774 :
1490 andres 775 5084 : while (index_getnext_slot(index_scan, ForwardScanDirection, existing_slot))
2907 heikki.linnakangas 776 ECB : {
777 : TransactionId xwait;
2495 rhaas 778 : XLTW_Oper reason_wait;
2907 heikki.linnakangas 779 : Datum existing_values[INDEX_MAX_KEYS];
780 : bool existing_isnull[INDEX_MAX_KEYS];
781 : char *error_new;
782 : char *error_existing;
783 :
784 : /*
785 : * Ignore the entry for the tuple we're trying to check.
786 : */
2893 andres 787 CBC 3150 : if (ItemPointerIsValid(tupleid) &&
1490 788 218 : ItemPointerEquals(tupleid, &existing_slot->tts_tid))
2907 heikki.linnakangas 789 ECB : {
2907 heikki.linnakangas 790 CBC 139 : if (found_self) /* should not happen */
2907 heikki.linnakangas 791 UIC 0 : elog(ERROR, "found self tuple multiple times in index \"%s\"",
2907 heikki.linnakangas 792 ECB : RelationGetRelationName(index));
2907 heikki.linnakangas 793 GIC 139 : found_self = true;
794 166 : continue;
795 : }
796 :
797 : /*
798 : * Extract the index column values and isnull flags from the existing
799 : * tuple.
800 : */
801 2793 : FormIndexDatum(indexInfo, existing_slot, estate,
802 : existing_values, existing_isnull);
803 :
2907 heikki.linnakangas 804 ECB : /* If lossy indexscan, must recheck the condition */
2907 heikki.linnakangas 805 CBC 2793 : if (index_scan->xs_recheck)
806 : {
807 36 : if (!index_recheck_constraint(index,
2907 heikki.linnakangas 808 EUB : constr_procs,
809 : existing_values,
2907 heikki.linnakangas 810 ECB : existing_isnull,
811 : values))
2907 heikki.linnakangas 812 GIC 27 : continue; /* tuple doesn't actually match, so no
813 : * conflict */
814 : }
815 :
816 : /*
817 : * At this point we have either a conflict or a potential conflict.
2893 andres 818 ECB : *
819 : * If an in-progress transaction is affecting the visibility of this
820 : * tuple, we need to wait for it to complete and then recheck (unless
821 : * the caller requested not to). For simplicity we do rechecking by
822 : * just restarting the whole scan --- this case probably doesn't
823 : * happen often enough to be worth trying harder, and anyway we don't
824 : * want to hold any index internal locks while waiting.
825 : */
2907 heikki.linnakangas 826 GIC 5532 : xwait = TransactionIdIsValid(DirtySnapshot.xmin) ?
827 2766 : DirtySnapshot.xmin : DirtySnapshot.xmax;
828 :
2893 andres 829 CBC 2766 : if (TransactionIdIsValid(xwait) &&
2893 andres 830 UIC 0 : (waitMode == CEOUC_WAIT ||
831 0 : (waitMode == CEOUC_LIVELOCK_PREVENTING_WAIT &&
832 0 : DirtySnapshot.speculativeToken &&
833 0 : TransactionIdPrecedes(GetCurrentTransactionId(), xwait))))
834 : {
2581 sfrost 835 GIC 70 : reason_wait = indexInfo->ii_ExclusionOps ?
836 35 : XLTW_RecheckExclusionConstr : XLTW_InsertIndex;
2907 heikki.linnakangas 837 35 : index_endscan(index_scan);
2893 andres 838 35 : if (DirtySnapshot.speculativeToken)
839 1 : SpeculativeInsertionWait(DirtySnapshot.xmin,
840 : DirtySnapshot.speculativeToken);
841 : else
1490 842 34 : XactLockTableWait(xwait, heap,
1490 andres 843 ECB : &existing_slot->tts_tid, reason_wait);
2907 heikki.linnakangas 844 CBC 35 : goto retry;
845 : }
2907 heikki.linnakangas 846 ECB :
2907 heikki.linnakangas 847 EUB : /*
2893 andres 848 : * We have a definite conflict (or a potential one, but the caller
849 : * didn't want to wait). Return it to caller, or report it.
2907 heikki.linnakangas 850 : */
2893 andres 851 GIC 2731 : if (violationOK)
2893 andres 852 ECB : {
2893 andres 853 CBC 2691 : conflict = true;
854 2691 : if (conflictTid)
1490 855 2679 : *conflictTid = existing_slot->tts_tid;
2893 856 2691 : break;
857 : }
858 :
2907 heikki.linnakangas 859 40 : error_new = BuildIndexValueDescription(index, values, isnull);
2907 heikki.linnakangas 860 GIC 40 : error_existing = BuildIndexValueDescription(index, existing_values,
2907 heikki.linnakangas 861 ECB : existing_isnull);
2907 heikki.linnakangas 862 GIC 40 : if (newIndex)
863 6 : ereport(ERROR,
864 : (errcode(ERRCODE_EXCLUSION_VIOLATION),
865 : errmsg("could not create exclusion constraint \"%s\"",
866 : RelationGetRelationName(index)),
867 : error_new && error_existing ?
2878 bruce 868 ECB : errdetail("Key %s conflicts with key %s.",
869 : error_new, error_existing) :
870 : errdetail("Key conflicts exist."),
2907 heikki.linnakangas 871 : errtableconstraint(heap,
872 : RelationGetRelationName(index))));
873 : else
2907 heikki.linnakangas 874 GIC 34 : ereport(ERROR,
875 : (errcode(ERRCODE_EXCLUSION_VIOLATION),
2907 heikki.linnakangas 876 ECB : errmsg("conflicting key value violates exclusion constraint \"%s\"",
877 : RelationGetRelationName(index)),
878 : error_new && error_existing ?
2878 bruce 879 : errdetail("Key %s conflicts with existing key %s.",
880 : error_new, error_existing) :
881 : errdetail("Key conflicts with existing key."),
882 : errtableconstraint(heap,
883 : RelationGetRelationName(index))));
884 : }
885 :
2907 heikki.linnakangas 886 GIC 4843 : index_endscan(index_scan);
887 :
888 : /*
889 : * Ordinarily, at this point the search should have found the originally
890 : * inserted tuple (if any), unless we exited the loop early because of
2893 andres 891 ECB : * conflict. However, it is possible to define exclusion constraints for
892 : * which that wouldn't be true --- for instance, if the operator is <>. So
893 : * we no longer complain if found_self is still false.
894 : */
895 :
2907 heikki.linnakangas 896 GIC 4843 : econtext->ecxt_scantuple = save_scantuple;
897 :
898 4843 : ExecDropSingleTupleTableSlot(existing_slot);
899 :
900 4843 : return !conflict;
901 : }
902 :
2893 andres 903 ECB : /*
904 : * Check for violation of an exclusion constraint
905 : *
906 : * This is a dumbed down version of check_exclusion_or_unique_constraint
907 : * for external callers. They don't need all the special modes.
908 : */
909 : void
2893 andres 910 GIC 35 : check_exclusion_constraint(Relation heap, Relation index,
911 : IndexInfo *indexInfo,
912 : ItemPointer tupleid,
2893 andres 913 ECB : Datum *values, bool *isnull,
914 : EState *estate, bool newIndex)
915 : {
2893 andres 916 GIC 35 : (void) check_exclusion_or_unique_constraint(heap, index, indexInfo, tupleid,
2893 andres 917 ECB : values, isnull,
918 : estate, newIndex,
919 : CEOUC_WAIT, false, NULL);
2893 andres 920 GIC 20 : }
921 :
922 : /*
923 : * Check existing tuple's index values to see if it really matches the
924 : * exclusion condition against the new_values. Returns true if conflict.
925 : */
926 : static bool
2907 heikki.linnakangas 927 CBC 36 : index_recheck_constraint(Relation index, Oid *constr_procs,
928 : Datum *existing_values, bool *existing_isnull,
929 : Datum *new_values)
930 : {
1828 teodor 931 GIC 36 : int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
932 : int i;
2907 heikki.linnakangas 933 ECB :
1828 teodor 934 GIC 72 : for (i = 0; i < indnkeyatts; i++)
935 : {
936 : /* Assume the exclusion operators are strict */
2907 heikki.linnakangas 937 CBC 63 : if (existing_isnull[i])
2907 heikki.linnakangas 938 UIC 0 : return false;
939 :
2907 heikki.linnakangas 940 GIC 63 : if (!DatumGetBool(OidFunctionCall2Coll(constr_procs[i],
941 63 : index->rd_indcollation[i],
942 63 : existing_values[i],
943 63 : new_values[i])))
2907 heikki.linnakangas 944 CBC 27 : return false;
945 : }
946 :
2907 heikki.linnakangas 947 GIC 9 : return true;
2907 heikki.linnakangas 948 ECB : }
949 :
950 : /*
816 pg 951 : * Check if ExecInsertIndexTuples() should pass indexUnchanged hint.
952 : *
953 : * When the executor performs an UPDATE that requires a new round of index
954 : * tuples, determine if we should pass 'indexUnchanged' = true hint for one
816 pg 955 EUB : * single index.
956 : */
816 pg 957 ECB : static bool
816 pg 958 CBC 252521 : index_unchanged_by_update(ResultRelInfo *resultRelInfo, EState *estate,
816 pg 959 ECB : IndexInfo *indexInfo, Relation indexRelation)
960 : {
452 961 : Bitmapset *updatedCols;
962 : Bitmapset *extraUpdatedCols;
963 : Bitmapset *allUpdatedCols;
816 pg 964 CBC 252521 : bool hasexpression = false;
965 : List *idxExprs;
966 :
967 : /*
968 : * Check cache first
969 : */
452 pg 970 GIC 252521 : if (indexInfo->ii_CheckedUnchanged)
971 230258 : return indexInfo->ii_IndexUnchanged;
972 22263 : indexInfo->ii_CheckedUnchanged = true;
973 :
974 : /*
816 pg 975 ECB : * Check for indexed attribute overlap with updated columns.
976 : *
977 : * Only do this for key columns. A change to a non-key column within an
978 : * INCLUDE index should not be counted here. Non-key column values are
979 : * opaque payload state to the index AM, a little like an extra table TID.
980 : *
452 981 : * Note that row-level BEFORE triggers won't affect our behavior, since
982 : * they don't affect the updatedCols bitmaps generally. It doesn't seem
983 : * worth the trouble of checking which attributes were changed directly.
984 : */
452 pg 985 GIC 22263 : updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
986 22263 : extraUpdatedCols = ExecGetExtraUpdatedCols(resultRelInfo, estate);
816 pg 987 CBC 24836 : for (int attr = 0; attr < indexInfo->ii_NumIndexKeyAttrs; attr++)
816 pg 988 ECB : {
816 pg 989 CBC 23276 : int keycol = indexInfo->ii_IndexAttrNumbers[attr];
990 :
816 pg 991 GIC 23276 : if (keycol <= 0)
992 : {
993 : /*
994 : * Skip expressions for now, but remember to deal with them later
995 : * on
996 : */
997 15 : hasexpression = true;
998 15 : continue;
999 : }
1000 :
1001 23261 : if (bms_is_member(keycol - FirstLowInvalidHeapAttributeNumber,
816 pg 1002 CBC 2558 : updatedCols) ||
1003 2558 : bms_is_member(keycol - FirstLowInvalidHeapAttributeNumber,
816 pg 1004 ECB : extraUpdatedCols))
1005 : {
1006 : /* Changed key column -- don't hint for this index */
452 pg 1007 GIC 20703 : indexInfo->ii_IndexUnchanged = false;
816 pg 1008 CBC 20703 : return false;
1009 : }
1010 : }
1011 :
1012 : /*
1013 : * When we get this far and index has no expressions, return true so that
816 pg 1014 ECB : * index_insert() call will go on to pass 'indexUnchanged' = true hint.
1015 : *
1016 : * The _absence_ of an indexed key attribute that overlaps with updated
1017 : * attributes (in addition to the total absence of indexed expressions)
1018 : * shows that the index as a whole is logically unchanged by UPDATE.
1019 : */
816 pg 1020 CBC 1560 : if (!hasexpression)
1021 : {
452 pg 1022 GIC 1548 : indexInfo->ii_IndexUnchanged = true;
816 1023 1548 : return true;
452 pg 1024 ECB : }
816 1025 :
1026 : /*
1027 : * Need to pass only one bms to expression_tree_walker helper function.
1028 : * Avoid allocating memory in common case where there are no extra cols.
1029 : */
816 pg 1030 GIC 12 : if (!extraUpdatedCols)
1031 12 : allUpdatedCols = updatedCols;
1032 : else
816 pg 1033 UIC 0 : allUpdatedCols = bms_union(updatedCols, extraUpdatedCols);
1034 :
1035 : /*
1036 : * We have to work slightly harder in the event of indexed expressions,
816 pg 1037 ECB : * but the principle is the same as before: try to find columns (Vars,
1038 : * actually) that overlap with known-updated columns.
1039 : *
1040 : * If we find any matching Vars, don't pass hint for index. Otherwise
1041 : * pass hint.
1042 : */
816 pg 1043 GIC 12 : idxExprs = RelationGetIndexExpressions(indexRelation);
1044 12 : hasexpression = index_expression_changed_walker((Node *) idxExprs,
1045 : allUpdatedCols);
1046 12 : list_free(idxExprs);
816 pg 1047 CBC 12 : if (extraUpdatedCols)
816 pg 1048 LBC 0 : bms_free(allUpdatedCols);
1049 :
816 pg 1050 GBC 12 : if (hasexpression)
1051 : {
452 pg 1052 GIC 9 : indexInfo->ii_IndexUnchanged = false;
816 1053 9 : return false;
1054 : }
1055 :
1056 : /*
1057 : * Deliberately don't consider index predicates. We should even give the
1058 : * hint when result rel's "updated tuple" has no corresponding index
1059 : * tuple, which is possible with a partial index (provided the usual
452 pg 1060 ECB : * conditions are met).
1061 : */
452 pg 1062 GIC 3 : indexInfo->ii_IndexUnchanged = true;
816 pg 1063 CBC 3 : return true;
816 pg 1064 ECB : }
816 pg 1065 EUB :
1066 : /*
816 pg 1067 ECB : * Indexed expression helper for index_unchanged_by_update().
1068 : *
1069 : * Returns true when Var that appears within allUpdatedCols located.
1070 : */
1071 : static bool
816 pg 1072 GIC 38 : index_expression_changed_walker(Node *node, Bitmapset *allUpdatedCols)
1073 : {
1074 38 : if (node == NULL)
816 pg 1075 UIC 0 : return false;
1076 :
816 pg 1077 GIC 38 : if (IsA(node, Var))
1078 : {
816 pg 1079 CBC 12 : Var *var = (Var *) node;
816 pg 1080 ECB :
816 pg 1081 GIC 12 : if (bms_is_member(var->varattno - FirstLowInvalidHeapAttributeNumber,
1082 : allUpdatedCols))
1083 : {
1084 : /* Var was updated -- indicates that we should not hint */
1085 9 : return true;
1086 : }
1087 :
1088 : /* Still haven't found a reason to not pass the hint */
816 pg 1089 CBC 3 : return false;
1090 : }
816 pg 1091 ECB :
816 pg 1092 GBC 26 : return expression_tree_walker(node, index_expression_changed_walker,
1093 : (void *) allUpdatedCols);
816 pg 1094 ECB : }
|