@@ -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 /*
0 commit comments