@@ -806,9 +806,15 @@ infer_arbiter_indexes(PlannerInfo *root)
806806Relation relation ;
807807Oid indexOidFromConstraint = InvalidOid ;
808808List * indexList ;
809- ListCell * l ;
809+ List * indexRelList = NIL ;
810810
811- /* Normalized inference attributes and inference expressions: */
811+ /*
812+ * Required attributes and expressions used to match indexes to the clause
813+ * given by the user. In the ON CONFLICT ON CONSTRAINT case, we compute
814+ * these from that constraint's index to match all other indexes, to
815+ * account for the case where that index is being concurrently reindexed.
816+ */
817+ List * inferIndexExprs = (List * )onconflict -> arbiterWhere ;
812818Bitmapset * inferAttrs = NULL ;
813819List * inferElems = NIL ;
814820
@@ -841,12 +847,14 @@ infer_arbiter_indexes(PlannerInfo *root)
841847 * well as a separate list of expression items. This simplifies matching
842848 * the cataloged definition of indexes.
843849 */
844- foreach ( l ,onconflict -> arbiterElems )
850+ foreach_ptr ( InferenceElem , elem ,onconflict -> arbiterElems )
845851{
846- InferenceElem * elem = (InferenceElem * )lfirst (l );
847852Var * var ;
848853int attno ;
849854
855+ /* we cannot also have a constraint name, per grammar */
856+ Assert (!OidIsValid (onconflict -> constraint ));
857+
850858if (!IsA (elem -> expr ,Var ))
851859{
852860/* If not a plain Var, just shove it in inferElems for now */
@@ -867,45 +875,96 @@ infer_arbiter_indexes(PlannerInfo *root)
867875}
868876
869877/*
870- * Lookup named constraint's index. This is not immediately returned
871- * because some additional sanity checks are required.
878+ * Next, open all the indexes. We need this list for two things: first,
879+ * if an ON CONSTRAINT clause was given, and that constraint's index is
880+ * undergoing REINDEX CONCURRENTLY, then we need to consider all matches
881+ * for that index. Second, if an attribute list was specified in the ON
882+ * CONFLICT clause, we use the list to find the indexes whose attributes
883+ * match that list.
884+ */
885+ indexList = RelationGetIndexList (relation );
886+ foreach_oid (indexoid ,indexList )
887+ {
888+ Relation idxRel ;
889+
890+ /* obtain the same lock type that the executor will ultimately use */
891+ idxRel = index_open (indexoid ,rte -> rellockmode );
892+ indexRelList = lappend (indexRelList ,idxRel );
893+ }
894+
895+ /*
896+ * If a constraint was named in the command, look up its index. We don't
897+ * return it immediately because we need some additional sanity checks,
898+ * and also because we need to include other indexes as arbiters to
899+ * account for REINDEX CONCURRENTLY processing it.
872900 */
873901if (onconflict -> constraint != InvalidOid )
874902{
875- indexOidFromConstraint = get_constraint_index (onconflict -> constraint );
903+ /* we cannot also have an explicit list of elements, per grammar */
904+ Assert (onconflict -> arbiterElems == NIL );
876905
906+ indexOidFromConstraint = get_constraint_index (onconflict -> constraint );
877907if (indexOidFromConstraint == InvalidOid )
878908ereport (ERROR ,
879909(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
880910errmsg ("constraint in ON CONFLICT clause has no associated index" )));
911+
912+ /*
913+ * Find the named constraint index to extract its attributes and
914+ * predicates.
915+ */
916+ foreach_ptr (RelationData ,idxRel ,indexRelList )
917+ {
918+ Form_pg_index idxForm = idxRel -> rd_index ;
919+
920+ if (indexOidFromConstraint == idxForm -> indexrelid )
921+ {
922+ /* Found it. */
923+ Assert (idxForm -> indisready );
924+
925+ /*
926+ * Set up inferElems and inferPredExprs to match the
927+ * constraint index, so that we can match them in the loop
928+ * below.
929+ */
930+ for (int natt = 0 ;natt < idxForm -> indnkeyatts ;natt ++ )
931+ {
932+ int attno ;
933+
934+ attno = idxRel -> rd_index -> indkey .values [natt ];
935+ if (attno != InvalidAttrNumber )
936+ inferAttrs =
937+ bms_add_member (inferAttrs ,
938+ attno - FirstLowInvalidHeapAttributeNumber );
939+ }
940+
941+ inferElems = RelationGetIndexExpressions (idxRel );
942+ inferIndexExprs = RelationGetIndexPredicate (idxRel );
943+ break ;
944+ }
945+ }
881946}
882947
883948/*
884949 * Using that representation, iterate through the list of indexes on the
885- * target relation totry and finda match
950+ * target relation to findmatches.
886951 */
887- indexList = RelationGetIndexList (relation );
888-
889- foreach (l ,indexList )
952+ foreach_ptr (RelationData ,idxRel ,indexRelList )
890953{
891- Oid indexoid = lfirst_oid (l );
892- Relation idxRel ;
893954Form_pg_index idxForm ;
894955Bitmapset * indexedAttrs ;
895956List * idxExprs ;
896957List * predExprs ;
897958AttrNumber natt ;
898- ListCell * el ;
959+ bool match ;
899960
900961/*
901- * Extract info from the relation descriptor for the index. Obtain
902- * the same lock type that the executor will ultimately use.
962+ * Extract info from the relation descriptor for the index.
903963 *
904964 * Let executor complain about !indimmediate case directly, because
905965 * enforcement needs to occur there anyway when an inference clause is
906966 * omitted.
907967 */
908- idxRel = index_open (indexoid ,rte -> rellockmode );
909968idxForm = idxRel -> rd_index ;
910969
911970/*
@@ -924,7 +983,7 @@ infer_arbiter_indexes(PlannerInfo *root)
924983 * indexes at least one index that is marked valid.
925984 */
926985if (!idxForm -> indisready )
927- goto next ;
986+ continue ;
928987
929988/*
930989 * Note that we do not perform a check against indcheckxmin (like e.g.
@@ -934,7 +993,7 @@ infer_arbiter_indexes(PlannerInfo *root)
934993 */
935994
936995/*
937- * Look for matchon "ON constraint_name" variant, which may not be
996+ * Look for matchfor "ON constraint_name" variant, which may not be a
938997 * unique constraint. This can only be a constraint name.
939998 */
940999if (indexOidFromConstraint == idxForm -> indexrelid )
@@ -944,31 +1003,37 @@ infer_arbiter_indexes(PlannerInfo *root)
9441003(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
9451004errmsg ("ON CONFLICT DO UPDATE not supported with exclusion constraints" )));
9461005
1006+ /* Consider this one a match already */
9471007results = lappend_oid (results ,idxForm -> indexrelid );
9481008foundValid |=idxForm -> indisvalid ;
949- index_close (idxRel ,NoLock );
950- break ;
1009+ continue ;
9511010}
9521011else if (indexOidFromConstraint != InvalidOid )
9531012{
954- /* No point in further work for index in named constraint case */
955- gotonext ;
1013+ /*
1014+ * In the case of "ON constraint_name DO UPDATE" we need to skip
1015+ * non-unique candidates.
1016+ */
1017+ if (!idxForm -> indisunique && onconflict -> action == ONCONFLICT_UPDATE )
1018+ continue ;
1019+ }
1020+ else
1021+ {
1022+ /*
1023+ * Only considering conventional inference at this point (not
1024+ * named constraints), so index under consideration can be
1025+ * immediately skipped if it's not unique.
1026+ */
1027+ if (!idxForm -> indisunique )
1028+ continue ;
9561029}
957-
958- /*
959- * Only considering conventional inference at this point (not named
960- * constraints), so index under consideration can be immediately
961- * skipped if it's not unique
962- */
963- if (!idxForm -> indisunique )
964- gotonext ;
9651030
9661031/*
9671032 * So-called unique constraints with WITHOUT OVERLAPS are really
9681033 * exclusion constraints, so skip those too.
9691034 */
9701035if (idxForm -> indisexclusion )
971- goto next ;
1036+ continue ;
9721037
9731038/* Build BMS representation of plain (non expression) index attrs */
9741039indexedAttrs = NULL ;
@@ -983,17 +1048,20 @@ infer_arbiter_indexes(PlannerInfo *root)
9831048
9841049/* Non-expression attributes (if any) must match */
9851050if (!bms_equal (indexedAttrs ,inferAttrs ))
986- goto next ;
1051+ continue ;
9871052
9881053/* Expression attributes (if any) must match */
9891054idxExprs = RelationGetIndexExpressions (idxRel );
9901055if (idxExprs && varno != 1 )
9911056ChangeVarNodes ((Node * )idxExprs ,1 ,varno ,0 );
9921057
993- foreach (el ,onconflict -> arbiterElems )
1058+ /*
1059+ * If arbiterElems are present, check them. (Note that if a
1060+ * constraint name was given in the command line, this list is NIL.)
1061+ */
1062+ match = true;
1063+ foreach_ptr (InferenceElem ,elem ,onconflict -> arbiterElems )
9941064{
995- InferenceElem * elem = (InferenceElem * )lfirst (el );
996-
9971065/*
9981066 * Ensure that collation/opclass aspects of inference expression
9991067 * element match. Even though this loop is primarily concerned
@@ -1002,7 +1070,10 @@ infer_arbiter_indexes(PlannerInfo *root)
10021070 * attributes appearing as inference elements.
10031071 */
10041072if (!infer_collation_opclass_match (elem ,idxRel ,idxExprs ))
1005- gotonext ;
1073+ {
1074+ match = false;
1075+ break ;
1076+ }
10061077
10071078/*
10081079 * Plain Vars don't factor into count of expression elements, and
@@ -1023,37 +1094,59 @@ infer_arbiter_indexes(PlannerInfo *root)
10231094list_member (idxExprs ,elem -> expr ))
10241095continue ;
10251096
1026- gotonext ;
1097+ match = false;
1098+ break ;
10271099}
1100+ if (!match )
1101+ continue ;
10281102
10291103/*
1030- *Now that all inferenceelements were matched , ensure that the
1104+ *In case of inferencefrom an attribute list , ensure that the
10311105 * expression elements from inference clause are not missing any
10321106 * cataloged expressions. This does the right thing when unique
10331107 * indexes redundantly repeat the same attribute, or if attributes
10341108 * redundantly appear multiple times within an inference clause.
1109+ *
1110+ * In case a constraint was named, ensure the candidate has an equal
1111+ * set of expressions as the named constraint's index.
10351112 */
10361113if (list_difference (idxExprs ,inferElems )!= NIL )
1037- goto next ;
1114+ continue ;
10381115
1039- /*
1040- * If it's a partial index, its predicate must be implied by the ON
1041- * CONFLICT's WHERE clause.
1042- */
10431116predExprs = RelationGetIndexPredicate (idxRel );
10441117if (predExprs && varno != 1 )
10451118ChangeVarNodes ((Node * )predExprs ,1 ,varno ,0 );
10461119
1047- if (!predicate_implied_by (predExprs , (List * )onconflict -> arbiterWhere , false))
1048- gotonext ;
1120+ /*
1121+ * Partial indexes affect each form of ON CONFLICT differently: if a
1122+ * constraint was named, then the predicates must be identical. In
1123+ * conventional inference, the index's predicate must be implied by
1124+ * the WHERE clause.
1125+ */
1126+ if (OidIsValid (indexOidFromConstraint ))
1127+ {
1128+ if (list_difference (predExprs ,inferIndexExprs )!= NIL )
1129+ continue ;
1130+ }
1131+ else
1132+ {
1133+ if (!predicate_implied_by (predExprs ,inferIndexExprs , false))
1134+ continue ;
1135+ }
10491136
1137+ /* All good -- consider this index a match */
10501138results = lappend_oid (results ,idxForm -> indexrelid );
10511139foundValid |=idxForm -> indisvalid ;
1052- next :
1140+ }
1141+
1142+ /* Close all indexes */
1143+ foreach_ptr (RelationData ,idxRel ,indexRelList )
1144+ {
10531145index_close (idxRel ,NoLock );
10541146}
10551147
10561148list_free (indexList );
1149+ list_free (indexRelList );
10571150table_close (relation ,NoLock );
10581151
10591152/* We require at least one indisvalid index */