From 8152062c9c0cc0e98eff2fe939abe5eb1f2bd5cc Mon Sep 17 00:00:00 2001
From: kenxx <zengxx@vastdata.com.cn>
Date: Wed, 16 Sep 2026 21:00:49 +0800
Subject: [PATCH v1 1/2] Refactor unique-index GROUP BY key matching

---
 src/backend/optimizer/path/indxpath.c  | 65 ++++++++++++++++++++
 src/backend/optimizer/plan/initsplan.c | 82 ++------------------------
 src/include/optimizer/paths.h          | 15 +++++
 3 files changed, 85 insertions(+), 77 deletions(-)

diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 3f5d4fa318..a4cd25d15f 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -4285,6 +4285,71 @@ relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel,
 	return false;
 }
 
+/*
+ * unique_index_keys_match_groupby_cols
+ *	  Test whether an immediate unique index proves uniqueness under the
+ *	  equality semantics of the given GROUP BY columns.
+ *
+ * The caller passes simple GROUP BY Vars belonging to rel.  For each index key
+ * column, there must be a GROUP BY Var on the same column whose mergejoin
+ * opfamilies include the index opfamily and whose collation agrees on
+ * equality.  A NULLS DISTINCT index additionally requires every key column to
+ * be NOT NULL.
+ *
+ * If index_attnos isn't NULL, it is set to the heap attribute numbers of the
+ * matched index key columns.  This allows callers to compare the key against a
+ * set of grouping columns.
+ */
+bool
+unique_index_keys_match_groupby_cols(IndexOptInfo *index, RelOptInfo *rel,
+									 List *groupbycols,
+									 Bitmapset **index_attnos)
+{
+	if (index_attnos)
+		*index_attnos = NULL;
+
+	/*
+	 * Only an immediate, unconditional unique index proves that the input is
+	 * unique.  Expression and partial indexes cannot prove whole-relation
+	 * uniqueness.  Skip hypothetical indexes because they do not prove a
+	 * property of the physical relation.
+	 */
+	if (!index->unique || !index->immediate || index->indpred != NIL ||
+		index->indexprs != NIL || index->hypothetical)
+		return false;
+
+	for (int i = 0; i < index->nkeycolumns; i++)
+	{
+		AttrNumber	indkey = index->indexkeys[i];
+		ListCell   *lc;
+
+		if (indkey <= 0 ||
+			(!index->nullsnotdistinct &&
+			 !bms_is_member(indkey, rel->notnullattnums)))
+			return false;
+
+		foreach(lc, groupbycols)
+		{
+			GroupByColInfo *info = (GroupByColInfo *) lfirst(lc);
+
+			if (info->attno == indkey &&
+				list_member_oid(info->eq_opfamilies, index->opfamily[i]) &&
+				collations_agree_on_equality(index->indexcollations[i],
+											 info->coll))
+				break;
+		}
+		if (lc == NULL)
+			return false;
+
+		if (index_attnos)
+			*index_attnos = bms_add_member(*index_attnos,
+										   indkey -
+										   FirstLowInvalidHeapAttributeNumber);
+	}
+
+	return true;
+}
+
 /*
  * indexcol_is_bool_constant_for_query
  *
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 8893e37c8f..f6d976e813 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -81,19 +81,6 @@ typedef struct JoinTreeItem
 									 * lateral references */
 } JoinTreeItem;
 
-/*
- * Compatibility info for one GROUP BY item, precomputed for use by
- * remove_useless_groupby_columns() when matching unique-index columns against
- * GROUP BY items.
- */
-typedef struct GroupByColInfo
-{
-	AttrNumber	attno;			/* var->varattno */
-	List	   *eq_opfamilies;	/* mergejoin opfamilies of sgc->eqop */
-	Oid			coll;			/* var->varcollid */
-} GroupByColInfo;
-
-
 static bool is_partial_agg_memory_risky(PlannerInfo *root);
 static void create_agg_clause_infos(PlannerInfo *root);
 static void create_grouping_expr_infos(PlannerInfo *root);
@@ -488,73 +475,14 @@ remove_useless_groupby_columns(PlannerInfo *root)
 		foreach_node(IndexOptInfo, index, rel->indexlist)
 		{
 			Bitmapset  *ind_attnos;
-			bool		index_check_ok;
 
 			/*
-			 * Skip any non-unique and deferrable indexes.  Predicate indexes
-			 * have not been checked yet, so we must skip those too as the
-			 * predOK check that's done later might fail.
+			 * Check that this is a usable unique index and that each key
+			 * column agrees with a GROUP BY column's equality semantics.
 			 */
-			if (!index->unique || !index->immediate || index->indpred != NIL)
-				continue;
-
-			/* For simplicity, we currently don't support expression indexes */
-			if (index->indexprs != NIL)
-				continue;
-
-			ind_attnos = NULL;
-			index_check_ok = true;
-			for (int i = 0; i < index->nkeycolumns; i++)
-			{
-				AttrNumber	indkey_attno = index->indexkeys[i];
-				Oid			indkey_opfamily = index->opfamily[i];
-				Oid			indkey_coll = index->indexcollations[i];
-				ListCell   *lc2;
-
-				/*
-				 * We must insist that the index columns are all defined NOT
-				 * NULL otherwise duplicate NULLs could exist.  However, we
-				 * can relax this check when the index is defined with NULLS
-				 * NOT DISTINCT as there can only be 1 NULL row, therefore
-				 * functional dependency on the unique columns is maintained,
-				 * despite the NULL.
-				 */
-				if (!index->nullsnotdistinct &&
-					!bms_is_member(indkey_attno, rel->notnullattnums))
-				{
-					index_check_ok = false;
-					break;
-				}
-
-				/*
-				 * The index proves uniqueness only under its own opfamily and
-				 * collation.  Require some GROUP BY item on this column to
-				 * use a compatible eqop and collation, the same check
-				 * relation_has_unique_index_for() applies to join clauses.
-				 */
-				foreach(lc2, groupbycols[relid])
-				{
-					GroupByColInfo *info = (GroupByColInfo *) lfirst(lc2);
-
-					if (info->attno != indkey_attno)
-						continue;
-					if (list_member_oid(info->eq_opfamilies, indkey_opfamily) &&
-						collations_agree_on_equality(indkey_coll, info->coll))
-						break;
-				}
-				if (lc2 == NULL)
-				{
-					index_check_ok = false;
-					break;
-				}
-
-				ind_attnos =
-					bms_add_member(ind_attnos,
-								   indkey_attno -
-								   FirstLowInvalidHeapAttributeNumber);
-			}
-
-			if (!index_check_ok)
+			if (!unique_index_keys_match_groupby_cols(index, rel,
+													  groupbycols[relid],
+													  &ind_attnos))
 				continue;
 
 			/*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d3853d1c07..3285bd77af 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -79,10 +79,25 @@ extern void generate_partitionwise_join_paths(PlannerInfo *root,
  * indxpath.c
  *	  routines to generate index paths
  */
+/*
+ * Compatibility info for one GROUP BY item, used when matching unique-index
+ * columns against GROUP BY items.
+ */
+typedef struct GroupByColInfo
+{
+	AttrNumber	attno;			/* var->varattno */
+	List	   *eq_opfamilies;	/* mergejoin opfamilies of sgc->eqop */
+	Oid			coll;			/* var->varcollid */
+} GroupByColInfo;
+
 extern void create_index_paths(PlannerInfo *root, RelOptInfo *rel);
 extern bool relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel,
 										  List *restrictlist,
 										  List **extra_clauses);
+extern bool unique_index_keys_match_groupby_cols(IndexOptInfo *index,
+												 RelOptInfo *rel,
+												 List *groupbycols,
+												 Bitmapset **index_attnos);
 extern bool indexcol_is_bool_constant_for_query(PlannerInfo *root,
 												IndexOptInfo *index,
 												int indexcol);
-- 
2.43.0

