@@ -806,9 +806,15 @@ infer_arbiter_indexes(PlannerInfo *root)
806806 Relation relation ;
807807 Oid indexOidFromConstraint = InvalidOid ;
808808 List * 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 ;
812818 Bitmapset * inferAttrs = NULL ;
813819 List * 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 );
847852 Var * var ;
848853 int attno ;
849854
855+ /* we cannot also have a constraint name, per grammar */
856+ Assert (!OidIsValid (onconflict -> constraint ));
857+
850858 if (!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 */
873901 if (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 );
877907 if (indexOidFromConstraint == InvalidOid )
878908 ereport (ERROR ,
879909 (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
880910 errmsg ("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 to try and find a match
950+ * target relation to find matches.
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 ;
893954 Form_pg_index idxForm ;
894955 Bitmapset * indexedAttrs ;
895956 List * idxExprs ;
896957 List * predExprs ;
897958 AttrNumber 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 );
909968 idxForm = 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 */
926985 if (!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 match on "ON constraint_name" variant, which may not be
996+ * Look for match for "ON constraint_name" variant, which may not be a
938997 * unique constraint. This can only be a constraint name.
939998 */
940999 if (indexOidFromConstraint == idxForm -> indexrelid )
@@ -944,31 +1003,37 @@ infer_arbiter_indexes(PlannerInfo *root)
9441003 (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
9451004 errmsg ("ON CONFLICT DO UPDATE not supported with exclusion constraints" )));
9461005
1006+ /* Consider this one a match already */
9471007 results = lappend_oid (results , idxForm -> indexrelid );
9481008 foundValid |= idxForm -> indisvalid ;
949- index_close (idxRel , NoLock );
950- break ;
1009+ continue ;
9511010 }
9521011 else if (indexOidFromConstraint != InvalidOid )
9531012 {
954- /* No point in further work for index in named constraint case */
955- goto next ;
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- goto next ;
9651030
9661031 /*
9671032 * So-called unique constraints with WITHOUT OVERLAPS are really
9681033 * exclusion constraints, so skip those too.
9691034 */
9701035 if (idxForm -> indisexclusion )
971- goto next ;
1036+ continue ;
9721037
9731038 /* Build BMS representation of plain (non expression) index attrs */
9741039 indexedAttrs = NULL ;
@@ -983,17 +1048,20 @@ infer_arbiter_indexes(PlannerInfo *root)
9831048
9841049 /* Non-expression attributes (if any) must match */
9851050 if (!bms_equal (indexedAttrs , inferAttrs ))
986- goto next ;
1051+ continue ;
9871052
9881053 /* Expression attributes (if any) must match */
9891054 idxExprs = RelationGetIndexExpressions (idxRel );
9901055 if (idxExprs && varno != 1 )
9911056 ChangeVarNodes ((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 */
10041072 if (!infer_collation_opclass_match (elem , idxRel , idxExprs ))
1005- goto next ;
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)
10231094 list_member (idxExprs , elem -> expr ))
10241095 continue ;
10251096
1026- goto next ;
1097+ match = false;
1098+ break ;
10271099 }
1100+ if (!match )
1101+ continue ;
10281102
10291103 /*
1030- * Now that all inference elements were matched , ensure that the
1104+ * In case of inference from 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 */
10361113 if (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- */
10431116 predExprs = RelationGetIndexPredicate (idxRel );
10441117 if (predExprs && varno != 1 )
10451118 ChangeVarNodes ((Node * ) predExprs , 1 , varno , 0 );
10461119
1047- if (!predicate_implied_by (predExprs , (List * ) onconflict -> arbiterWhere , false))
1048- goto next ;
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 */
10501138 results = lappend_oid (results , idxForm -> indexrelid );
10511139 foundValid |= idxForm -> indisvalid ;
1052- next :
1140+ }
1141+
1142+ /* Close all indexes */
1143+ foreach_ptr (RelationData , idxRel , indexRelList )
1144+ {
10531145 index_close (idxRel , NoLock );
10541146 }
10551147
10561148 list_free (indexList );
1149+ list_free (indexRelList );
10571150 table_close (relation , NoLock );
10581151
10591152 /* We require at least one indisvalid index */
0 commit comments