=== Applying patches on top of PostgreSQL commit ID e82e9aaa6a2942505c2c328426778787e4976ea6 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Sat Dec 27 01:41:22 UTC 2025 On branch cf/4971 nothing to commit, working tree clean === using 'git am' to apply patch ./v27-0001-This-is-https-commitfest.postgresql.org-50-5160-.patch === Applying: This is https://commitfest.postgresql.org/50/5160/ and https://commitfest.postgresql.org/patch/5438/ merged in single commit. it is required for stability of stress tests. Using index info to reconstruct a base tree... M contrib/amcheck/verify_nbtree.c M src/backend/commands/indexcmds.c M src/backend/executor/execIndexing.c M src/backend/executor/execPartition.c M src/backend/executor/nodeModifyTable.c M src/backend/optimizer/util/plancat.c M src/backend/utils/time/snapmgr.c Falling back to patching base and 3-way merge... Auto-merging src/backend/utils/time/snapmgr.c CONFLICT (content): Merge conflict in src/backend/utils/time/snapmgr.c Auto-merging src/backend/optimizer/util/plancat.c CONFLICT (content): Merge conflict in src/backend/optimizer/util/plancat.c Auto-merging src/backend/executor/nodeModifyTable.c Auto-merging src/backend/executor/execPartition.c CONFLICT (content): Merge conflict in src/backend/executor/execPartition.c Auto-merging src/backend/executor/execIndexing.c Auto-merging src/backend/commands/indexcmds.c CONFLICT (content): Merge conflict in src/backend/commands/indexcmds.c Auto-merging contrib/amcheck/verify_nbtree.c CONFLICT (content): Merge conflict in contrib/amcheck/verify_nbtree.c error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 This is https://commitfest.postgresql.org/50/5160/ and https://commitfest.postgresql.org/patch/5438/ merged in single commit. it is required for stability of stress tests. When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". Unstaged changes after reset: M contrib/amcheck/verify_nbtree.c M src/backend/commands/indexcmds.c M src/backend/executor/execIndexing.c M src/backend/executor/execPartition.c M src/backend/executor/nodeModifyTable.c M src/backend/optimizer/util/plancat.c M src/backend/utils/time/snapmgr.c === using patch(1) to apply patch ./v27-0001-This-is-https-commitfest.postgresql.org-50-5160-.patch === patching file contrib/amcheck/verify_nbtree.c Hunk #1 FAILED at 382. Hunk #2 FAILED at 433. Hunk #3 FAILED at 476. Hunk #4 FAILED at 555. Hunk #5 FAILED at 569. Hunk #6 FAILED at 578. Hunk #7 FAILED at 602. 7 out of 7 hunks FAILED -- saving rejects to file contrib/amcheck/verify_nbtree.c.rej patching file src/backend/commands/indexcmds.c Hunk #1 succeeded at 1726 with fuzz 2 (offset -63 lines). Hunk #2 FAILED at 4229. Hunk #3 succeeded at 4143 with fuzz 2 (offset -165 lines). 1 out of 3 hunks FAILED -- saving rejects to file src/backend/commands/indexcmds.c.rej patching file src/backend/executor/execIndexing.c Hunk #1 succeeded at 118 (offset 1 line). Hunk #2 succeeded at 944 with fuzz 2 (offset 1 line). patching file src/backend/executor/execPartition.c Hunk #1 succeeded at 550 with fuzz 1 (offset 60 lines). Hunk #2 FAILED at 743. Hunk #3 FAILED at 753. 2 out of 3 hunks FAILED -- saving rejects to file src/backend/executor/execPartition.c.rej patching file src/backend/executor/nodeModifyTable.c Hunk #1 succeeded at 71 with fuzz 1 (offset 1 line). Hunk #2 succeeded at 1179 (offset -1 lines). patching file src/backend/optimizer/util/plancat.c Hunk #1 FAILED at 808. Hunk #2 succeeded at 871 (offset 23 lines). Hunk #3 FAILED at 861. Hunk #4 FAILED at 907. Hunk #5 FAILED at 927. Hunk #6 FAILED at 967. Hunk #7 FAILED at 975. Hunk #8 FAILED at 1012. Hunk #9 FAILED at 1040. 8 out of 9 hunks FAILED -- saving rejects to file src/backend/optimizer/util/plancat.c.rej patching file src/backend/utils/time/snapmgr.c Hunk #1 succeeded at 124 (offset 1 line). Hunk #2 succeeded at 900 with fuzz 2 (offset 441 lines). Unstaged changes after reset: M src/backend/commands/indexcmds.c M src/backend/executor/execIndexing.c M src/backend/executor/execPartition.c M src/backend/executor/nodeModifyTable.c M src/backend/optimizer/util/plancat.c M src/backend/utils/time/snapmgr.c Removing contrib/amcheck/verify_nbtree.c.rej Removing src/backend/commands/indexcmds.c.rej Removing src/backend/executor/execPartition.c.rej Removing src/backend/optimizer/util/plancat.c.rej === using 'git apply' to apply patch ./v27-0001-This-is-https-commitfest.postgresql.org-50-5160-.patch === Applied patch to 'contrib/amcheck/verify_nbtree.c' with conflicts. Applied patch to 'src/backend/commands/indexcmds.c' with conflicts. Applied patch to 'src/backend/executor/execIndexing.c' cleanly. Applied patch to 'src/backend/executor/execPartition.c' with conflicts. Applied patch to 'src/backend/executor/nodeModifyTable.c' cleanly. Applied patch to 'src/backend/optimizer/util/plancat.c' with conflicts. Applied patch to 'src/backend/utils/time/snapmgr.c' with conflicts. U contrib/amcheck/verify_nbtree.c U src/backend/commands/indexcmds.c U src/backend/executor/execPartition.c U src/backend/optimizer/util/plancat.c U src/backend/utils/time/snapmgr.c diff --cc contrib/amcheck/verify_nbtree.c index f91392a3a49,2445f001700..00000000000 --- a/contrib/amcheck/verify_nbtree.c +++ b/contrib/amcheck/verify_nbtree.c @@@ -442,36 -442,39 +442,64 @@@ bt_check_every_level(Relation rel, Rela /* * GetTransactionSnapshot() always acquires a new MVCC snapshot in ++<<<<<<< ours + * READ COMMITTED mode. A new snapshot is guaranteed to have all the + * entries it requires in the index. + * + * We must defend against the possibility that an old xact snapshot + * was returned at higher isolation levels when that snapshot is not + * safe for index scans of the target index. This is possible when + * the snapshot sees tuples that are before the index's indcheckxmin + * horizon. Throwing an error here should be very rare. It doesn't + * seem worth using a secondary snapshot to avoid this. ++======= + * READ COMMITTED mode. A new snapshot is guaranteed to have all + * the entries it requires in the index. + * + * We must defend against the possibility that an old xact + * snapshot was returned at higher isolation levels when that + * snapshot is not safe for index scans of the target index. This + * is possible when the snapshot sees tuples that are before the + * index's indcheckxmin horizon. Throwing an error here should be + * very rare. It doesn't seem worth using a secondary snapshot to + * avoid this. ++>>>>>>> theirs */ if (IsolationUsesXactSnapshot() && rel->rd_index->indcheckxmin && !TransactionIdPrecedes(HeapTupleHeaderGetXmin(rel->rd_indextuple->t_data), state->snapshot->xmin)) ereport(ERROR, ++<<<<<<< ours + errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), + errmsg("index \"%s\" cannot be verified using transaction snapshot", + RelationGetRelationName(rel))); + } ++======= + (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), + errmsg("index \"%s\" cannot be verified using transaction snapshot", + RelationGetRelationName(rel)))); + } ++>>>>>>> theirs /* - * We need a snapshot to check the uniqueness of the index. For better - * performance take it once per index check. If snapshot already taken - * reuse it. + * We need a snapshot to check the uniqueness of the index. For better + * performance, take it once per index check. If one was already taken + * above, use that. */ if (state->checkunique) { state->indexinfo = BuildIndexInfo(state->rel); ++<<<<<<< ours + + if (state->indexinfo->ii_Unique && state->snapshot == InvalidSnapshot) + state->snapshot = RegisterSnapshot(GetTransactionSnapshot()); ++======= + if (state->indexinfo->ii_Unique) + { + if (state->snapshot == InvalidSnapshot) + state->snapshot = RegisterSnapshot(GetTransactionSnapshot()); + } ++>>>>>>> theirs } Assert(!state->rootdescend || state->readonly); diff --cc src/backend/commands/indexcmds.c index d9cccb6ac18,974243c5c60..00000000000 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@@ -1789,7 -1789,7 +1789,11 @@@ DefineIndex(Oid tableId * before the reference snap was taken, we have to wait out any * transactions that might have older snapshots. */ ++<<<<<<< ours + INJECTION_POINT("define-index-before-set-valid", NULL); ++======= + INJECTION_POINT("define_index_before_set_valid", NULL); ++>>>>>>> theirs pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_3); WaitForOlderSnapshots(limitXmin, true); @@@ -4229,8 -4229,7 +4233,12 @@@ ReindexRelationConcurrently(const Reind * the same time to make sure we only get constraint violations from the * indexes with the correct names. */ ++<<<<<<< ours + + INJECTION_POINT("reindex-relation-concurrently-before-swap", NULL); ++======= + INJECTION_POINT("reindex_relation_concurrently_before_swap", NULL); ++>>>>>>> theirs StartTransactionCommand(); /* @@@ -4309,7 -4308,7 +4317,11 @@@ * index_drop() for more details. */ ++<<<<<<< ours + INJECTION_POINT("reindex-relation-concurrently-before-set-dead", NULL); ++======= + INJECTION_POINT("reindex_relation_concurrently_before_set_dead", NULL); ++>>>>>>> theirs pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_4); WaitForLockersMultiple(lockTags, AccessExclusiveLock, true); diff --cc src/backend/executor/execPartition.c index e30db12113b,066686483f0..00000000000 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@@ -493,50 -492,33 +493,80 @@@ ExecFindPartition(ModifyTableState *mts /* * IsIndexCompatibleAsArbiter ++<<<<<<< ours + * Return true if two indexes are identical for INSERT ON CONFLICT + * purposes. + * + * Only indexes of the same relation are supported. + */ +static bool +IsIndexCompatibleAsArbiter(Relation arbiterIndexRelation, + IndexInfo *arbiterIndexInfo, + Relation indexRelation, + IndexInfo *indexInfo) +{ + Assert(arbiterIndexRelation->rd_index->indrelid == indexRelation->rd_index->indrelid); + + /* must match whether they're unique */ + if (arbiterIndexInfo->ii_Unique != indexInfo->ii_Unique) + return false; + + /* No support currently for comparing exclusion indexes. */ + if (arbiterIndexInfo->ii_ExclusionOps != NULL || + indexInfo->ii_ExclusionOps != NULL) + return false; + + /* the "nulls not distinct" criterion must match */ + if (arbiterIndexInfo->ii_NullsNotDistinct != + indexInfo->ii_NullsNotDistinct) + return false; + + /* number of key attributes must match */ + if (arbiterIndexInfo->ii_NumIndexKeyAttrs != + indexInfo->ii_NumIndexKeyAttrs) + return false; + + for (int i = 0; i < arbiterIndexInfo->ii_NumIndexKeyAttrs; i++) + { + if (arbiterIndexRelation->rd_indcollation[i] != + indexRelation->rd_indcollation[i]) + return false; + + if (arbiterIndexRelation->rd_opfamily[i] != + indexRelation->rd_opfamily[i]) + return false; + + if (arbiterIndexRelation->rd_index->indkey.values[i] != + indexRelation->rd_index->indkey.values[i]) ++======= + * Checks if the indexes are identical in terms of being used + * as arbiters for the INSERT ON CONFLICT operation by comparing + * them to the provided arbiter index. + * + * Returns the true if indexes are compatible. + */ + static bool + IsIndexCompatibleAsArbiter(Relation arbiterIndexRelation, + IndexInfo *arbiterIndexInfo, + Relation indexRelation, + IndexInfo *indexInfo) + { + int i; + + if (arbiterIndexInfo->ii_Unique != indexInfo->ii_Unique) + return false; + /* it is not supported for cases of exclusion constraints. */ + if (arbiterIndexInfo->ii_ExclusionOps != NULL || indexInfo->ii_ExclusionOps != NULL) + return false; + if (arbiterIndexRelation->rd_index->indnkeyatts != indexRelation->rd_index->indnkeyatts) + return false; + + for (i = 0; i < indexRelation->rd_index->indnkeyatts; i++) + { + int arbiterAttoNo = arbiterIndexRelation->rd_index->indkey.values[i]; + int attoNo = indexRelation->rd_index->indkey.values[i]; + if (arbiterAttoNo != attoNo) ++>>>>>>> theirs return false; } @@@ -760,107 -742,87 +790,187 @@@ ExecInitPartitionInfo(ModifyTableState */ if (rootResultRelInfo->ri_onConflictArbiterIndexes != NIL) { ++<<<<<<< ours + List *unparented_idxs = NIL, + *arbiters_listidxs = NIL; ++======= + List *childIdxs; + List *nonAncestorIdxs = NIL; + int i, j, additional_arbiters = 0; + + childIdxs = RelationGetIndexList(leaf_part_rri->ri_RelationDesc); ++>>>>>>> theirs - foreach(lc, childIdxs) + for (int listidx = 0; listidx < leaf_part_rri->ri_NumIndices; listidx++) { - Oid childIdx = lfirst_oid(lc); + Oid indexoid; List *ancestors; - ListCell *lc2; ++<<<<<<< ours + /* + * If one of this index's ancestors is in the root's arbiter + * list, then use this index as arbiter for this partition. + * Otherwise, if this index has no parent, track it for later, + * in case REINDEX CONCURRENTLY is working on one of the + * arbiters. + * + * XXX get_partition_ancestors is slow: it scans pg_inherits + * each time. Consider a syscache or some other way to cache? + */ + indexoid = RelationGetRelid(leaf_part_rri->ri_IndexRelationDescs[listidx]); + ancestors = get_partition_ancestors(indexoid); + if (ancestors != NIL) + { + foreach_oid(parent_idx, rootResultRelInfo->ri_onConflictArbiterIndexes) + { + if (list_member_oid(ancestors, parent_idx)) + { + arbiterIndexes = lappend_oid(arbiterIndexes, indexoid); + arbiters_listidxs = lappend_int(arbiters_listidxs, listidx); + break; + } + } + } + else + unparented_idxs = lappend_int(unparented_idxs, listidx); + list_free(ancestors); + } + + /* + * If we found any indexes with no ancestors, it's possible that + * some arbiter index is undergoing concurrent reindex. Match all + * unparented indexes against arbiters; add unparented matching + * ones as "additional arbiters". + * + * This is critical so that all concurrent transactions use the + * same set as arbiters during REINDEX CONCURRENTLY, to avoid + * spurious "duplicate key" errors. + */ + if (unparented_idxs && arbiterIndexes) + { + foreach_int(unparented_i, unparented_idxs) + { + Relation unparented_rel; + IndexInfo *unparenred_ii; + + unparented_rel = leaf_part_rri->ri_IndexRelationDescs[unparented_i]; + unparenred_ii = leaf_part_rri->ri_IndexRelationInfo[unparented_i]; + + Assert(!list_member_oid(arbiterIndexes, + unparented_rel->rd_index->indexrelid)); + + /* Ignore indexes not ready */ + if (!unparenred_ii->ii_ReadyForInserts) + continue; + + foreach_int(arbiter_i, arbiters_listidxs) + { + Relation arbiter_rel; + IndexInfo *arbiter_ii; + + arbiter_rel = leaf_part_rri->ri_IndexRelationDescs[arbiter_i]; + arbiter_ii = leaf_part_rri->ri_IndexRelationInfo[arbiter_i]; + + /* + * If the non-ancestor index is compatible with the + * arbiter, use the non-ancestor as arbiter too. + */ + if (IsIndexCompatibleAsArbiter(arbiter_rel, + arbiter_ii, + unparented_rel, + unparenred_ii)) + { + arbiterIndexes = lappend_oid(arbiterIndexes, + unparented_rel->rd_index->indexrelid); + additional_arbiters++; + break; + } + } + } + } + list_free(unparented_idxs); + list_free(arbiters_listidxs); + } + + /* + * We expect to find as many arbiter indexes on this partition as the + * root has, plus however many "additional arbiters" (to wit: those + * being concurrently rebuilt) we found. + */ + if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) != + list_length(arbiterIndexes) - additional_arbiters) + elog(ERROR, "invalid arbiter index list"); ++======= + ancestors = get_partition_ancestors(childIdx); + if (ancestors) + { + foreach(lc2, rootResultRelInfo->ri_onConflictArbiterIndexes) + { + if (list_member_oid(ancestors, lfirst_oid(lc2))) + arbiterIndexes = lappend_oid(arbiterIndexes, childIdx); + } + } + else /* No ancestor was found for that index. Save it for rechecking later. */ + nonAncestorIdxs = lappend_oid(nonAncestorIdxs, childIdx); + list_free(ancestors); + } + + /* + * If any non-ancestor indexes are found, we need to compare them with other + * indexes of the relation that will be used as arbiters. This is necessary + * when a partitioned index is processed by REINDEX CONCURRENTLY. Both indexes + * must be considered as arbiters to ensure that all concurrent transactions + * use the same set of arbiters. + */ + if (nonAncestorIdxs) + { + for (i = 0; i < leaf_part_rri->ri_NumIndices; i++) + { + if (list_member_oid(nonAncestorIdxs, leaf_part_rri->ri_IndexRelationDescs[i]->rd_index->indexrelid)) + { + Relation nonAncestorIndexRelation = leaf_part_rri->ri_IndexRelationDescs[i]; + IndexInfo *nonAncestorIndexInfo = leaf_part_rri->ri_IndexRelationInfo[i]; + Assert(!list_member_oid(arbiterIndexes, nonAncestorIndexRelation->rd_index->indexrelid)); + + /* It is too early to us non-ready indexes as arbiters */ + if (!nonAncestorIndexInfo->ii_ReadyForInserts) + continue; + + for (j = 0; j < leaf_part_rri->ri_NumIndices; j++) + { + if (list_member_oid(arbiterIndexes, + leaf_part_rri->ri_IndexRelationDescs[j]->rd_index->indexrelid)) + { + Relation arbiterIndexRelation = leaf_part_rri->ri_IndexRelationDescs[j]; + IndexInfo *arbiterIndexInfo = leaf_part_rri->ri_IndexRelationInfo[j]; + + /* If non-ancestor index are compatible to arbiter - use it as arbiter too. */ + if (IsIndexCompatibleAsArbiter(arbiterIndexRelation, arbiterIndexInfo, + nonAncestorIndexRelation, nonAncestorIndexInfo)) + { + arbiterIndexes = lappend_oid(arbiterIndexes, + nonAncestorIndexRelation->rd_index->indexrelid); + additional_arbiters++; + } + } + } + } + } + } + list_free(nonAncestorIdxs); + + /* + * If the resulting lists are of inequal length, something is wrong. + * (This shouldn't happen, since arbiter index selection should not + * pick up a non-ready index.) + * + * But we need to consider an additional arbiter indexes also. + */ + if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) != + list_length(arbiterIndexes) - additional_arbiters) + elog(ERROR, "invalid arbiter index list"); + } ++>>>>>>> theirs leaf_part_rri->ri_onConflictArbiterIndexes = arbiterIndexes; /* diff --cc src/backend/optimizer/util/plancat.c index bf45c355b77,ff416f0522c..00000000000 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@@ -820,21 -806,16 +820,32 @@@ infer_arbiter_indexes(PlannerInfo *root Relation relation; Oid indexOidFromConstraint = InvalidOid; List *indexList; - ListCell *l; + List *indexRelList = NIL; + ++<<<<<<< ours + /* + * Required attributes and expressions used to match indexes to the clause + * given by the user. In the ON CONFLICT ON CONSTRAINT case, we compute + * these from that constraint's index to match all other indexes, to + * account for the case where that index is being concurrently reindexed. + */ + List *inferIndexExprs = (List *) onconflict->arbiterWhere; + Bitmapset *inferAttrs = NULL; + List *inferElems = NIL; + /* Results */ + List *results = NIL; + bool foundValid = false; ++======= + /* Normalized required attributes and expressions: */ + Bitmapset *requiredArbiterAttrs = NULL; + List *requiredArbiterElems = NIL; + List *requiredIndexPredExprs = (List *) onconflict->arbiterWhere; + + /* Results */ + List *results = NIL; + bool foundValid = false; ++>>>>>>> theirs /* * Quickly return NIL for ON CONFLICT DO NOTHING without an inference @@@ -866,13 -847,11 +877,13 @@@ Var *var; int attno; + /* we cannot also have a constraint name, per grammar */ + Assert(!OidIsValid(onconflict->constraint)); + if (!IsA(elem->expr, Var)) { - /* If not a plain Var, just shove it in inferElems for now */ - inferElems = lappend(inferElems, elem->expr); + /* If not a plain Var, just shove it in requiredArbiterElems for now */ + requiredArbiterElems = lappend(requiredArbiterElems, elem->expr); continue; } @@@ -888,83 -867,76 +899,143 @@@ attno - FirstLowInvalidHeapAttributeNumber); } + indexList = RelationGetIndexList(relation); + /* ++<<<<<<< ours + * Next, open all the indexes. We need this list for two things: first, + * if an ON CONSTRAINT clause was given, and that constraint's index is + * undergoing REINDEX CONCURRENTLY, then we need to consider all matches + * for that index. Second, if an attribute list was specified in the ON + * CONFLICT clause, we use the list to find the indexes whose attributes + * match that list. + */ + indexList = RelationGetIndexList(relation); + foreach_oid(indexoid, indexList) + { + Relation idxRel; + + /* obtain the same lock type that the executor will ultimately use */ + idxRel = index_open(indexoid, rte->rellockmode); + indexRelList = lappend(indexRelList, idxRel); + } + + /* + * If a constraint was named in the command, look up its index. We don't + * return it immediately because we need some additional sanity checks, + * and also because we need to include other indexes as arbiters to + * account for REINDEX CONCURRENTLY processing it. ++======= + * Lookup named constraint's index. This is not immediately returned + * because some additional sanity checks are required. Additionally, we + * need to process other indexes as potential arbiters to account for + * cases where REINDEX CONCURRENTLY is processing an index used as a + * named constraint. ++>>>>>>> theirs */ if (onconflict->constraint != InvalidOid) { - indexOidFromConstraint = get_constraint_index(onconflict->constraint); + /* we cannot also have an explicit list of elements, per grammar */ + Assert(onconflict->arbiterElems == NIL); + indexOidFromConstraint = get_constraint_index(onconflict->constraint); if (indexOidFromConstraint == InvalidOid) + { ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++<<<<<<< ours + errmsg("constraint in ON CONFLICT clause has no associated index"))); + + /* + * Find the named constraint index to extract its attributes and + * predicates. + */ + foreach_ptr(RelationData, idxRel, indexRelList) + { + Form_pg_index idxForm = idxRel->rd_index; + + if (indexOidFromConstraint == idxForm->indexrelid) + { + /* Found it. */ + Assert(idxForm->indisready); + + /* + * Set up inferElems and inferPredExprs to match the + * constraint index, so that we can match them in the loop + * below. + */ + for (int natt = 0; natt < idxForm->indnkeyatts; natt++) + { + int attno; + + attno = idxRel->rd_index->indkey.values[natt]; + if (attno != InvalidAttrNumber) + inferAttrs = + bms_add_member(inferAttrs, + attno - FirstLowInvalidHeapAttributeNumber); + } + + inferElems = RelationGetIndexExpressions(idxRel); + inferIndexExprs = RelationGetIndexPredicate(idxRel); + break; + } ++======= + errmsg("constraint in ON CONFLICT clause has no associated index"))); + } + + /* + * Find the named constraint index to extract its attributes and predicates. + * We open all indexes in the loop to avoid deadlock of changed order of locks. + * */ + foreach(l, indexList) + { + Oid indexoid = lfirst_oid(l); + Relation idxRel; + Form_pg_index idxForm; + AttrNumber natt; + + idxRel = index_open(indexoid, rte->rellockmode); + idxForm = idxRel->rd_index; + + if (idxForm->indisready) + { + if (indexOidFromConstraint == idxForm->indexrelid) + { + /* + * Prepare requirements for other indexes to be used as arbiter together + * with indexOidFromConstraint. It is required to involve both equals indexes + * in case of REINDEX CONCURRENTLY. + */ + for (natt = 0; natt < idxForm->indnkeyatts; natt++) + { + int attno = idxRel->rd_index->indkey.values[natt]; + + if (attno != 0) + requiredArbiterAttrs = bms_add_member(requiredArbiterAttrs, + attno - FirstLowInvalidHeapAttributeNumber); + } + requiredArbiterElems = RelationGetIndexExpressions(idxRel); + requiredIndexPredExprs = RelationGetIndexPredicate(idxRel); + /* We are done, so, quite the loop. */ + index_close(idxRel, NoLock); + break; + } + } + index_close(idxRel, NoLock); ++>>>>>>> theirs } } /* * Using that representation, iterate through the list of indexes on the - * target relation to try and find a match + * target relation to find matches. */ ++<<<<<<< ours + foreach_ptr(RelationData, idxRel, indexRelList) ++======= + foreach(l, indexList) ++>>>>>>> theirs { - Oid indexoid = lfirst_oid(l); - Relation idxRel; Form_pg_index idxForm; Bitmapset *indexedAttrs; List *idxExprs; @@@ -982,31 -955,14 +1053,41 @@@ idxForm = idxRel->rd_index; /* ++<<<<<<< ours + * Ignore indexes that aren't indisready, because we cannot trust + * their catalog structure yet. However, if any indexes are marked + * indisready but not yet indisvalid, we still consider them, because + * they might turn valid while we're running. Doing it this way + * allows a concurrent transaction with a slightly later catalog + * snapshot infer the same set of indexes, which is critical to + * prevent spurious 'duplicate key' errors. + * + * However, another critical aspect is that a unique index that isn't + * yet marked indisvalid=true might not be complete yet, meaning it + * wouldn't detect possible duplicate rows. In order to prevent false + * negatives, we require that we include in the set of inferred + * indexes at least one index that is marked valid. + */ + if (!idxForm->indisready) + continue; + + /* + * Ignore invalid indexes for partitioned tables. It's possible that + * some partitions don't have the index (yet), and then we would not + * find a match during ExecInitPartitionInfo. + */ + if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && + !idxForm->indisvalid) + continue; ++======= + * We need to consider both indisvalid and indisready indexes because + * them may become indisvalid before execution phase. It is required + * to keep set of indexes used as arbiter to be the same for all + * concurrent transactions. + */ + if (!idxForm->indisready) + goto next; ++>>>>>>> theirs /* * Note that we do not perform a check against indcheckxmin (like e.g. @@@ -1025,30 -981,21 +1106,48 @@@ ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("ON CONFLICT DO UPDATE not supported with exclusion constraints"))); ++<<<<<<< ours + + /* Consider this one a match already */ + results = lappend_oid(results, idxForm->indexrelid); + foundValid |= idxForm->indisvalid; + continue; + } + else if (indexOidFromConstraint != InvalidOid) + { + /* + * In the case of "ON constraint_name DO UPDATE" we need to skip + * non-unique candidates. + */ + if (!idxForm->indisunique && onconflict->action == ONCONFLICT_UPDATE) + continue; + } + else + { + /* + * Only considering conventional inference at this point (not + * named constraints), so index under consideration can be + * immediately skipped if it's not unique. + */ + if (!idxForm->indisunique) + continue; ++======= + goto found; + } + else if (indexOidFromConstraint != InvalidOid) + { + /* In the case of "ON constraint_name DO UPDATE" we need to skip non-unique candidates. */ + if (!idxForm->indisunique && onconflict->action == ONCONFLICT_UPDATE) + goto next; + } else { + /* + * Only considering conventional inference at this point (not named + * constraints), so index under consideration can be immediately + * skipped if it's not unique + */ + if (!idxForm->indisunique) + goto next; ++>>>>>>> theirs } /* @@@ -1070,26 -1017,22 +1169,42 @@@ } /* Non-expression attributes (if any) must match */ ++<<<<<<< ours + if (!bms_equal(indexedAttrs, inferAttrs)) + continue; + + /* Expression attributes (if any) must match */ + idxExprs = RelationGetIndexExpressions(idxRel); + if (idxExprs) ++======= + if (!bms_equal(indexedAttrs, requiredArbiterAttrs)) + goto next; + + /* Expression attributes (if any) must match */ + idxExprs = RelationGetIndexExpressions(idxRel); + if (idxExprs && varno != 1) + ChangeVarNodes((Node *) idxExprs, 1, varno, 0); + + /* + * If arbiterElems are present, check them. If name >constraint is + * present arbiterElems == NIL. + */ + foreach(el, onconflict->arbiterElems) ++>>>>>>> theirs { - InferenceElem *elem = (InferenceElem *) lfirst(el); + if (varno != 1) + ChangeVarNodes((Node *) idxExprs, 1, varno, 0); + + idxExprs = (List *) eval_const_expressions(root, (Node *) idxExprs); + } + /* + * If arbiterElems are present, check them. (Note that if a + * constraint name was given in the command line, this list is NIL.) + */ + match = true; + foreach_ptr(InferenceElem, elem, onconflict->arbiterElems) + { /* * Ensure that collation/opclass aspects of inference expression * element match. Even though this loop is primarily concerned @@@ -1122,70 -1062,47 +1237,102 @@@ list_member(idxExprs, elem->expr)) continue; - goto next; + match = false; + break; } + if (!match) + continue; /* ++<<<<<<< ours + * In case of inference from an attribute list, ensure that the ++======= + * In case of the conventional inference involved ensure that the ++>>>>>>> theirs * expression elements from inference clause are not missing any * cataloged expressions. This does the right thing when unique * indexes redundantly repeat the same attribute, or if attributes * redundantly appear multiple times within an inference clause. * ++<<<<<<< ours + * In case a constraint was named, ensure the candidate has an equal + * set of expressions as the named constraint's index. + */ + if (list_difference(idxExprs, inferElems) != NIL) + continue; ++======= + * In the case of named constraint ensure candidate has equal set + * of expressions as the named constraint index. + */ + if (list_difference(idxExprs, requiredArbiterElems) != NIL) + goto next; ++>>>>>>> theirs predExprs = RelationGetIndexPredicate(idxRel); - if (predExprs && varno != 1) - ChangeVarNodes((Node *) predExprs, 1, varno, 0); + if (predExprs) + { + if (varno != 1) + ChangeVarNodes((Node *) predExprs, 1, varno, 0); + + predExprs = (List *) + eval_const_expressions(root, + (Node *) make_ands_explicit(predExprs)); + predExprs = make_ands_implicit((Expr *) predExprs); + } /* ++<<<<<<< ours + * Partial indexes affect each form of ON CONFLICT differently: if a + * constraint was named, then the predicates must be identical. In + * conventional inference, the index's predicate must be implied by + * the WHERE clause. + */ + if (OidIsValid(indexOidFromConstraint)) + { + if (list_difference(predExprs, inferIndexExprs) != NIL) + continue; + } + else + { + if (!predicate_implied_by(predExprs, inferIndexExprs, false)) + continue; + } + + /* All good -- consider this index a match */ + results = lappend_oid(results, idxForm->indexrelid); + foundValid |= idxForm->indisvalid; + } + + /* Close all indexes */ + foreach_ptr(RelationData, idxRel, indexRelList) + { ++======= + * If it's a partial index and conventional inference, its predicate must be implied + * by the ON CONFLICT's WHERE clause. + */ + if (indexOidFromConstraint == InvalidOid && !predicate_implied_by(predExprs, requiredIndexPredExprs, false)) + goto next; + /* If it's a partial index and named constraint predicates must be equal. */ + if (indexOidFromConstraint != InvalidOid && list_difference(predExprs, requiredIndexPredExprs) != NIL) + goto next; + + found: + results = lappend_oid(results, idxForm->indexrelid); + foundValid |= idxForm->indisvalid; + next: ++>>>>>>> theirs index_close(idxRel, NoLock); } list_free(indexList); + list_free(indexRelList); table_close(relation, NoLock); ++<<<<<<< ours + /* We require at least one indisvalid index */ ++======= + /* It is required to have at least one indisvalid index during the planning. */ ++>>>>>>> theirs if (results == NIL || !foundValid) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), diff --cc src/backend/utils/time/snapmgr.c index 5af8326d5e8,8e1a918f130..00000000000 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@@ -459,7 -459,7 +460,11 @@@ InvalidateCatalogSnapshot(void pairingheap_remove(&RegisteredSnapshots, &CatalogSnapshot->ph_node); CatalogSnapshot = NULL; SnapshotResetXmin(); ++<<<<<<< ours + INJECTION_POINT("invalidate-catalog-snapshot-end", NULL); ++======= + INJECTION_POINT("invalidate_catalog_snapshot_end", NULL); ++>>>>>>> theirs } }