Skip to content

Commit 17eb91d

Browse files
author
Commitfest Bot
committed
[CF 5160] v11 - Issues with ON CONFLICT UPDATE and REINDEX CONCURRENTLY
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5160 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CADzfLwU+oXdzY9xZQ2ayTROzgrEHxf=S8BV_mRSkXv_ZCukrug@mail.gmail.com Author(s): Michail Nikolaev, Mihail Nikalayeu
2 parents cf8be02 + aec9b15 commit 17eb91d

18 files changed

+1525
-50
lines changed

src/backend/commands/indexcmds.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1789,6 +1789,7 @@ DefineIndex(Oid tableId,
17891789
* before the reference snap was taken, we have to wait out any
17901790
* transactions that might have older snapshots.
17911791
*/
1792+
INJECTION_POINT("define_index_before_set_valid", NULL);
17921793
pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
17931794
PROGRESS_CREATEIDX_PHASE_WAIT_3);
17941795
WaitForOlderSnapshots(limitXmin, true);
@@ -4228,7 +4229,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
42284229
* the same time to make sure we only get constraint violations from the
42294230
* indexes with the correct names.
42304231
*/
4231-
4232+
INJECTION_POINT("reindex_relation_concurrently_before_swap", NULL);
42324233
StartTransactionCommand();
42334234

42344235
/*
@@ -4307,6 +4308,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
43074308
* index_drop() for more details.
43084309
*/
43094310

4311+
INJECTION_POINT("reindex_relation_concurrently_before_set_dead", NULL);
43104312
pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
43114313
PROGRESS_CREATEIDX_PHASE_WAIT_4);
43124314
WaitForLockersMultiple(lockTags, AccessExclusiveLock, true);

src/backend/executor/execIndexing.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
#include "utils/multirangetypes.h"
118118
#include "utils/rangetypes.h"
119119
#include "utils/snapmgr.h"
120+
#include "utils/injection_point.h"
120121

121122
/* waitMode argument to check_exclusion_or_unique_constraint() */
122123
typedef enum
@@ -942,6 +943,8 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
942943
econtext->ecxt_scantuple = save_scantuple;
943944

944945
ExecDropSingleTupleTableSlot(existing_slot);
946+
if (!conflict)
947+
INJECTION_POINT("check_exclusion_or_unique_constraint_no_conflict", NULL);
945948

946949
return !conflict;
947950
}

src/backend/executor/execPartition.c

Lines changed: 107 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,48 @@ ExecFindPartition(ModifyTableState *mtstate,
490490
return rri;
491491
}
492492

493+
/*
494+
* IsIndexCompatibleAsArbiter
495+
* Checks if the indexes are identical in terms of being used
496+
* as arbiters for the INSERT ON CONFLICT operation by comparing
497+
* them to the provided arbiter index.
498+
*
499+
* Returns the true if indexes are compatible.
500+
*/
501+
static bool
502+
IsIndexCompatibleAsArbiter(Relation arbiterIndexRelation,
503+
IndexInfo *arbiterIndexInfo,
504+
Relation indexRelation,
505+
IndexInfo *indexInfo)
506+
{
507+
int i;
508+
509+
if (arbiterIndexInfo->ii_Unique != indexInfo->ii_Unique)
510+
return false;
511+
/* it is not supported for cases of exclusion constraints. */
512+
if (arbiterIndexInfo->ii_ExclusionOps != NULL || indexInfo->ii_ExclusionOps != NULL)
513+
return false;
514+
if (arbiterIndexRelation->rd_index->indnkeyatts != indexRelation->rd_index->indnkeyatts)
515+
return false;
516+
517+
for (i = 0; i < indexRelation->rd_index->indnkeyatts; i++)
518+
{
519+
int arbiterAttoNo = arbiterIndexRelation->rd_index->indkey.values[i];
520+
int attoNo = indexRelation->rd_index->indkey.values[i];
521+
if (arbiterAttoNo != attoNo)
522+
return false;
523+
}
524+
525+
if (list_difference(RelationGetIndexExpressions(arbiterIndexRelation),
526+
RelationGetIndexExpressions(indexRelation)) != NIL)
527+
return false;
528+
529+
if (list_difference(RelationGetIndexPredicate(arbiterIndexRelation),
530+
RelationGetIndexPredicate(indexRelation)) != NIL)
531+
return false;
532+
return true;
533+
}
534+
493535
/*
494536
* ExecInitPartitionInfo
495537
* Lock the partition and initialize ResultRelInfo. Also setup other
@@ -701,6 +743,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
701743
if (rootResultRelInfo->ri_onConflictArbiterIndexes != NIL)
702744
{
703745
List *childIdxs;
746+
List *nonAncestorIdxs = NIL;
747+
int i, j, additional_arbiters = 0;
704748

705749
childIdxs = RelationGetIndexList(leaf_part_rri->ri_RelationDesc);
706750

@@ -711,23 +755,74 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
711755
ListCell *lc2;
712756

713757
ancestors = get_partition_ancestors(childIdx);
714-
foreach(lc2, rootResultRelInfo->ri_onConflictArbiterIndexes)
758+
if (ancestors)
715759
{
716-
if (list_member_oid(ancestors, lfirst_oid(lc2)))
717-
arbiterIndexes = lappend_oid(arbiterIndexes, childIdx);
760+
foreach(lc2, rootResultRelInfo->ri_onConflictArbiterIndexes)
761+
{
762+
if (list_member_oid(ancestors, lfirst_oid(lc2)))
763+
arbiterIndexes = lappend_oid(arbiterIndexes, childIdx);
764+
}
718765
}
766+
else /* No ancestor was found for that index. Save it for rechecking later. */
767+
nonAncestorIdxs = lappend_oid(nonAncestorIdxs, childIdx);
719768
list_free(ancestors);
720769
}
721-
}
722770

723-
/*
724-
* If the resulting lists are of inequal length, something is wrong.
725-
* (This shouldn't happen, since arbiter index selection should not
726-
* pick up an invalid index.)
727-
*/
728-
if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) !=
729-
list_length(arbiterIndexes))
730-
elog(ERROR, "invalid arbiter index list");
771+
/*
772+
* If any non-ancestor indexes are found, we need to compare them with other
773+
* indexes of the relation that will be used as arbiters. This is necessary
774+
* when a partitioned index is processed by REINDEX CONCURRENTLY. Both indexes
775+
* must be considered as arbiters to ensure that all concurrent transactions
776+
* use the same set of arbiters.
777+
*/
778+
if (nonAncestorIdxs)
779+
{
780+
for (i = 0; i < leaf_part_rri->ri_NumIndices; i++)
781+
{
782+
if (list_member_oid(nonAncestorIdxs, leaf_part_rri->ri_IndexRelationDescs[i]->rd_index->indexrelid))
783+
{
784+
Relation nonAncestorIndexRelation = leaf_part_rri->ri_IndexRelationDescs[i];
785+
IndexInfo *nonAncestorIndexInfo = leaf_part_rri->ri_IndexRelationInfo[i];
786+
Assert(!list_member_oid(arbiterIndexes, nonAncestorIndexRelation->rd_index->indexrelid));
787+
788+
/* It is too early to us non-ready indexes as arbiters */
789+
if (!nonAncestorIndexInfo->ii_ReadyForInserts)
790+
continue;
791+
792+
for (j = 0; j < leaf_part_rri->ri_NumIndices; j++)
793+
{
794+
if (list_member_oid(arbiterIndexes,
795+
leaf_part_rri->ri_IndexRelationDescs[j]->rd_index->indexrelid))
796+
{
797+
Relation arbiterIndexRelation = leaf_part_rri->ri_IndexRelationDescs[j];
798+
IndexInfo *arbiterIndexInfo = leaf_part_rri->ri_IndexRelationInfo[j];
799+
800+
/* If non-ancestor index are compatible to arbiter - use it as arbiter too. */
801+
if (IsIndexCompatibleAsArbiter(arbiterIndexRelation, arbiterIndexInfo,
802+
nonAncestorIndexRelation, nonAncestorIndexInfo))
803+
{
804+
arbiterIndexes = lappend_oid(arbiterIndexes,
805+
nonAncestorIndexRelation->rd_index->indexrelid);
806+
additional_arbiters++;
807+
}
808+
}
809+
}
810+
}
811+
}
812+
}
813+
list_free(nonAncestorIdxs);
814+
815+
/*
816+
* If the resulting lists are of inequal length, something is wrong.
817+
* (This shouldn't happen, since arbiter index selection should not
818+
* pick up a non-ready index.)
819+
*
820+
* But we need to consider an additional arbiter indexes also.
821+
*/
822+
if (list_length(rootResultRelInfo->ri_onConflictArbiterIndexes) !=
823+
list_length(arbiterIndexes) - additional_arbiters)
824+
elog(ERROR, "invalid arbiter index list");
825+
}
731826
leaf_part_rri->ri_onConflictArbiterIndexes = arbiterIndexes;
732827

733828
/*

src/backend/executor/nodeModifyTable.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
#include "utils/datum.h"
7171
#include "utils/rel.h"
7272
#include "utils/snapmgr.h"
73+
#include "utils/injection_point.h"
7374

7475

7576
typedef struct MTTargetRelLookup
@@ -1179,6 +1180,7 @@ ExecInsert(ModifyTableContext *context,
11791180
return NULL;
11801181
}
11811182
}
1183+
INJECTION_POINT("exec_insert_before_insert_speculative", NULL);
11821184

11831185
/*
11841186
* Before we start insertion proper, acquire our "speculative

0 commit comments

Comments
 (0)