From 3cd7d3483e1eeed600913f3673da03dcb45ebd8e Mon Sep 17 00:00:00 2001 From: Dongsheng He Date: Tue, 28 Jul 2026 23:01:42 +0800 Subject: [PATCH] [CALCITE-7679] RelToSqlConverter generates GROUP BY literals for dialects that do not support them when the constant is hidden by nested Projects --- .../calcite/rel/rel2sql/SqlImplementor.java | 2 + .../rel/rel2sql/RelToSqlConverterTest.java | 64 ++++++++++++++++--- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java index b65a797f14f..38d34a621b3 100644 --- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java +++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java @@ -194,6 +194,8 @@ protected SqlImplementor(SqlDialect dialect) { public final Result visitRoot(RelNode r) { List rules = new ArrayList<>(); if (!this.dialect.supportsGroupByLiteral()) { + rules.add(CoreRules.PROJECT_MERGE); + rules.add(CoreRules.PROJECT_REMOVE); rules.add(AggregateProjectConstantToDummyJoinRule.Config.DEFAULT.toRule()); } diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java index 279454f6b31..d88859aa3b1 100644 --- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java +++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java @@ -370,6 +370,56 @@ private static String toSql(RelNode root, SqlDialect dialect, .withInformix().ok(expectedInformix); } + /** Test case for + * [CALCITE-7679] + * RelToSqlConverter generates GROUP BY literals for dialects that do not + * support them when the constant is hidden by nested Projects. */ + @Test void testGroupByLiteralWithNestedProjects() { + final String query = "SELECT \"id\"\n" + + "FROM (\n" + + " SELECT \"id\"\n" + + " FROM (\n" + + " SELECT NULL AS \"id\"\n" + + " FROM \"employee\"\n" + + " ) AS \"t1\"\n" + + ") AS \"t2\"\n" + + "GROUP BY \"id\""; + final String expectedPostgresql = "SELECT \"t\".\"id\"\n" + + "FROM \"foodmart\".\"employee\",\n" + + "(VALUES (NULL)) AS \"t\" (\"id\")\n" + + "GROUP BY \"t\".\"id\""; + sql(query) + // Disable RelBuilder's eager Project merging to retain the nested + // Projects that reproduce the constant GROUP BY conversion issue. + .withConfig(c -> c.withRelBuilderConfigTransform(b -> b.withBloat(-1))) + .withPostgresql().ok(expectedPostgresql); + } + + /** Test case for + * [CALCITE-7679] + * RelToSqlConverter generates GROUP BY literals for dialects that do not + * support them when the constant is hidden by nested Projects. */ + @Test void testGroupByLiteralWithReorderedNestedProjects() { + final String query = "SELECT \"id\", \"employee_id\"\n" + + "FROM (\n" + + " SELECT \"id\", \"employee_id\"\n" + + " FROM (\n" + + " SELECT \"employee_id\", NULL AS \"id\"\n" + + " FROM \"employee\"\n" + + " ) AS \"t1\"\n" + + ") AS \"t2\"\n" + + "GROUP BY \"id\", \"employee_id\""; + final String expectedPostgresql = "SELECT \"t\".\"id\", \"employee\".\"employee_id\"\n" + + "FROM \"foodmart\".\"employee\",\n" + + "(VALUES (NULL)) AS \"t\" (\"id\")\n" + + "GROUP BY \"t\".\"id\", \"employee\".\"employee_id\""; + sql(query) + // Disable RelBuilder's eager Project merging to retain the nested + // Projects that reproduce the constant GROUP BY conversion issue. + .withConfig(c -> c.withRelBuilderConfigTransform(b -> b.withBloat(-1))) + .withPostgresql().ok(expectedPostgresql); + } + /** Test case for [CALCITE-6910] * RelToSql does not handle ASOF joins. */ @Test void testAsofJoin() { @@ -8123,12 +8173,11 @@ private void checkLiteral2(String expression, String expected) { @Test void testThreeValues() { final String sql = "select * from (values (1), (2), (3)) as t(\"a\")\n"; sql(sql) - .withRedshift().ok("SELECT *\n" - + "FROM (SELECT 1 AS \"a\"\n" + .withRedshift().ok("SELECT 1 AS \"a\"\n" + "UNION ALL\n" + "SELECT 2 AS \"a\"\n" + "UNION ALL\n" - + "SELECT 3 AS \"a\")"); + + "SELECT 3 AS \"a\""); } @Test void testValuesEmpty() { @@ -9805,13 +9854,12 @@ private void checkLiteral2(String expression, String expected) { + "ON t1.DEPTNO = t2.c0"; final String expectedPostgres = "SELECT *\n" + "FROM \"SCOTT\".\"DEPT\"\n" - + "LEFT JOIN (SELECT \"t\".\"C0\", \"t0\".\"C0\" AS \"C00\"\n" - + "FROM (SELECT \"DEPTNO\" AS \"C0\"\n" - + "FROM \"SCOTT\".\"DEPT\") AS \"t\"\n" + + "LEFT JOIN ((SELECT \"DEPTNO\" AS \"C0\"\n" + + "FROM \"SCOTT\".\"DEPT\") AS \"t\" " + "INNER JOIN (SELECT \"DEPTNO\" AS \"C0\"\n" + "FROM \"SCOTT\".\"DEPT\") AS \"t0\"" - + " ON \"t\".\"C0\" = \"t0\".\"C0\") AS \"t1\"" - + " ON \"DEPT\".\"DEPTNO\" = \"t1\".\"C0\""; + + " ON \"t\".\"C0\" = \"t0\".\"C0\")" + + " ON \"DEPT\".\"DEPTNO\" = \"t\".\"C0\""; sql(sql) .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT) .withPostgresql().ok(expectedPostgres);