This is an automated email from the ASF dual-hosted git repository.

asolimando pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 8a7ee97f61 [CALCITE-7483] RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar
8a7ee97f61 is described below

commit 8a7ee97f61a69a4208958f405aa239383e5dc500
Author: Alessandro Solimando <[email protected]>
AuthorDate: Wed Apr 22 19:21:16 2026 +0200

    [CALCITE-7483] RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar
---
 .../apache/calcite/rel/rel2sql/SqlImplementor.java |  48 +++++-
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 162 +++++++++++++++++++++
 2 files changed, 206 insertions(+), 4 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 410cc5ea33..f4dd0f6869 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
@@ -327,9 +327,10 @@ public Result setOpToSql(SqlSetOperator operator, RelNode 
rel) {
     for (Ord<RelNode> input : Ord.zip(rel.getInputs())) {
       final Result result = visitInput(rel, input.i);
       if (node == null) {
-        node = result.asSelect();
+        node = result.maybeExpandStar(result.asSelect());
       } else {
-        node = operator.createCall(POS, node, result.asSelect());
+        node =
+            operator.createCall(POS, node, 
result.maybeExpandStar(result.asSelect()));
       }
     }
     if (node == null) {
@@ -2026,6 +2027,13 @@ private Builder builder(RelNode rel, Set<Clause> 
clauses) {
         } else {
           newContext = aliasContext(aliases, qualified);
         }
+        if (!dialect.supportGenerateSelectStar(rel.getInput(0))) {
+          final List<SqlNode> expandedSelectList = new ArrayList<>();
+          for (int i = 0; i < newContext.fieldCount; i++) {
+            expandedSelectList.add(newContext.field(i));
+          }
+          select.setSelectList(new SqlNodeList(expandedSelectList, POS));
+        }
       }
       return new Builder(rel, clauseList, select, newContext, isAnon(),
           needNew && !aliases.containsKey(neededAlias) ? newAliases : aliases);
@@ -2367,10 +2375,42 @@ public SqlNode asStatement() {
       case MERGE:
         return maybeStrip(node);
       default:
-        return maybeStrip(asSelect());
+        return maybeStrip(maybeExpandStar(asSelect()));
       }
     }
 
+    /** If the dialect does not support {@code SELECT *} and the select list
+     * is {@link SqlNodeList#SINGLETON_STAR}, replaces it with explicit column
+     * references derived from the result's aliases. */
+    SqlSelect maybeExpandStar(SqlSelect select) {
+      if (expectedRel != null
+          && !expectedRel.getInputs().isEmpty()
+          && select.getSelectList().equals(SqlNodeList.SINGLETON_STAR)
+          && !dialect.supportGenerateSelectStar(expectedRel.getInput(0))) {
+        boolean qualified =
+            !dialect.hasImplicitTableAlias() || aliases.size() > 1;
+        final Context ctx = aliasContext(aliases, qualified);
+        final List<SqlNode> expandedList = new ArrayList<>();
+        for (int i = 0; i < ctx.fieldCount; i++) {
+          expandedList.add(ctx.field(i));
+        }
+        return new SqlSelect(select.getParserPosition(),
+            (SqlNodeList) select.getOperandList().get(0),
+            new SqlNodeList(expandedList, POS),
+            select.getFrom(),
+            select.getWhere(),
+            select.getGroup(),
+            select.getHaving(),
+            select.getWindowList(),
+            select.getQualify(),
+            select.getOrderList(),
+            select.getOffset(),
+            select.getFetch(),
+            select.getHints());
+      }
+      return select;
+    }
+
     /** Converts a non-query node into a SELECT node. Set operators (UNION,
      * INTERSECT, EXCEPT) and VALUES remain as is. */
     public SqlNode asQueryOrValues() {
@@ -2381,7 +2421,7 @@ public SqlNode asQueryOrValues() {
       case VALUES:
         return maybeStrip(node);
       default:
-        return maybeStrip(asSelect());
+        return maybeStrip(maybeExpandStar(asSelect()));
       }
     }
 
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 3c5dc7c4b3..7c41f6c9d5 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
@@ -9505,6 +9505,168 @@ private void checkLiteral2(String expression, String 
expected) {
         .withPostgresql().ok(expectedPostgres);
   }
 
+  private static final SqlDialect NO_STAR_DIALECT =
+      new PostgresqlSqlDialect(PostgresqlSqlDialect.DEFAULT_CONTEXT) {
+        @Override public boolean supportGenerateSelectStar(RelNode relNode) {
+          return false;
+        }
+      };
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7483";>[CALCITE-7483]
+   * RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar</a>.
+   * Bare TableScan. */
+  @Test void testNoSelectStarWithBareTableScan() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .build();
+    final String expected = "SELECT \"EMPNO\", \"ENAME\", \"JOB\", \"MGR\","
+        + " \"HIREDATE\", \"SAL\", \"COMM\", \"DEPTNO\"\n"
+        + "FROM \"scott\".\"EMP\"";
+    relFn(relFn).dialect(NO_STAR_DIALECT).ok(expected);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7483";>[CALCITE-7483]
+   * RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar</a>.
+   * Filter without Project. */
+  @Test void testNoSelectStarWithFilterOnly() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .filter(
+            b.equals(b.field("DEPTNO"), b.literal(10)))
+        .build();
+    final String expected = "SELECT \"EMPNO\", \"ENAME\", \"JOB\", \"MGR\","
+        + " \"HIREDATE\", \"SAL\", \"COMM\", \"DEPTNO\"\n"
+        + "FROM \"scott\".\"EMP\"\n"
+        + "WHERE \"DEPTNO\" = 10";
+    relFn(relFn).dialect(NO_STAR_DIALECT).ok(expected);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7483";>[CALCITE-7483]
+   * RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar</a>.
+   * Sort without Project. */
+  @Test void testNoSelectStarWithSortOnly() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .sort(b.field("EMPNO"))
+        .build();
+    final String expected = "SELECT \"EMPNO\", \"ENAME\", \"JOB\", \"MGR\","
+        + " \"HIREDATE\", \"SAL\", \"COMM\", \"DEPTNO\"\n"
+        + "FROM \"scott\".\"EMP\"\n"
+        + "ORDER BY \"EMPNO\"";
+    relFn(relFn).dialect(NO_STAR_DIALECT).ok(expected);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7483";>[CALCITE-7483]
+   * RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar</a>.
+   * Aggregate without Project. */
+  @Test void testNoSelectStarWithAggregateOnly() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .aggregate(b.groupKey("DEPTNO"),
+            b.count(false, "CNT"))
+        .build();
+    final String expected = "SELECT \"DEPTNO\", COUNT(*) AS \"CNT\"\n"
+        + "FROM \"scott\".\"EMP\"\n"
+        + "GROUP BY \"DEPTNO\"";
+    relFn(relFn).dialect(NO_STAR_DIALECT).ok(expected);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7483";>[CALCITE-7483]
+   * RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar</a>.
+   * Sort over Filter. */
+  @Test void testNoSelectStarWithSortAndFilter() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .filter(b.equals(b.field("DEPTNO"), b.literal(10)))
+        .sort(b.field("EMPNO"))
+        .build();
+    final String expected = "SELECT \"EMPNO\", \"ENAME\", \"JOB\", \"MGR\","
+        + " \"HIREDATE\", \"SAL\", \"COMM\", \"DEPTNO\"\n"
+        + "FROM \"scott\".\"EMP\"\n"
+        + "WHERE \"DEPTNO\" = 10\n"
+        + "ORDER BY \"EMPNO\"";
+    relFn(relFn).dialect(NO_STAR_DIALECT).ok(expected);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7483";>[CALCITE-7483]
+   * RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar</a>.
+   * Limit (fetch). */
+  @Test void testNoSelectStarWithLimit() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .limit(0, 5)
+        .build();
+    final String expected = "SELECT \"EMPNO\", \"ENAME\", \"JOB\", \"MGR\","
+        + " \"HIREDATE\", \"SAL\", \"COMM\", \"DEPTNO\"\n"
+        + "FROM \"scott\".\"EMP\"\n"
+        + "FETCH NEXT 5 ROWS ONLY";
+    relFn(relFn).dialect(NO_STAR_DIALECT).ok(expected);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7483";>[CALCITE-7483]
+   * RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar</a>.
+   * Sort over Union. */
+  @Test void testNoSelectStarWithUnion() {
+    final Function<RelBuilder, RelNode> relFn = b -> {
+      b.scan("EMP").project(b.field("EMPNO"), b.field("ENAME"));
+      b.scan("EMP").project(b.field("EMPNO"), b.field("ENAME"));
+      return b.union(true).sort(b.field("EMPNO")).build();
+    };
+    final String expected = "SELECT \"EMPNO\", \"ENAME\"\n"
+        + "FROM (SELECT \"EMPNO\", \"ENAME\"\n"
+        + "FROM \"scott\".\"EMP\"\n"
+        + "UNION ALL\n"
+        + "SELECT \"EMPNO\", \"ENAME\"\n"
+        + "FROM \"scott\".\"EMP\") AS \"t\"\n"
+        + "ORDER BY \"EMPNO\"";
+    relFn(relFn).dialect(NO_STAR_DIALECT).ok(expected);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7483";>[CALCITE-7483]
+   * RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar</a>.
+   * Join. */
+  @Test void testNoSelectStarWithJoin() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .scan("DEPT")
+        .join(JoinRelType.INNER,
+            b.equals(b.field(2, 0, "DEPTNO"),
+                b.field(2, 1, "DEPTNO")))
+        .build();
+    final String expected = "SELECT"
+        + " \"EMP\".\"EMPNO\", \"EMP\".\"ENAME\", \"EMP\".\"JOB\","
+        + " \"EMP\".\"MGR\", \"EMP\".\"HIREDATE\", \"EMP\".\"SAL\","
+        + " \"EMP\".\"COMM\", \"EMP\".\"DEPTNO\","
+        + " \"DEPT\".\"DEPTNO\","
+        + " \"DEPT\".\"DNAME\", \"DEPT\".\"LOC\"\n"
+        + "FROM \"scott\".\"EMP\"\n"
+        + "INNER JOIN \"scott\".\"DEPT\""
+        + " ON \"EMP\".\"DEPTNO\" = \"DEPT\".\"DEPTNO\"";
+    relFn(relFn).dialect(NO_STAR_DIALECT).ok(expected);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7483";>[CALCITE-7483]
+   * RelToSqlConverter generates SELECT * despite 
supportGenerateSelectStar</a>.
+   * Project (regression test for the original visit(Project) path). */
+  @Test void testNoSelectStarWithProject() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .project(b.field("EMPNO"), b.field("ENAME"))
+        .build();
+    final String expected = "SELECT \"EMPNO\", \"ENAME\"\n"
+        + "FROM \"scott\".\"EMP\"";
+    relFn(relFn).dialect(NO_STAR_DIALECT).ok(expected);
+  }
+
   /** Test case for
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-5265";>[CALCITE-5265]
    * JDBC adapter sometimes adds unnecessary parentheses around SELECT in 
INSERT</a>. */

Reply via email to