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>. */