This is an automated email from the ASF dual-hosted git repository.
mihaibudiu 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 43a5f22fa4 [CALCITE-7550] SqlUpdate and SqlDelete unparse EXISTS
subqueries without parentheses
43a5f22fa4 is described below
commit 43a5f22fa46ab3a63de94f08d6b9da9870484f16
Author: zzwqqq <[email protected]>
AuthorDate: Tue May 26 17:15:51 2026 +0800
[CALCITE-7550] SqlUpdate and SqlDelete unparse EXISTS subqueries without
parentheses
---
.../java/org/apache/calcite/sql/SqlDelete.java | 3 +-
.../org/apache/calcite/sql/SqlSelectOperator.java | 31 +---------------
.../java/org/apache/calcite/sql/SqlUpdate.java | 3 +-
.../main/java/org/apache/calcite/sql/SqlUtil.java | 43 ++++++++++++++++++++++
.../calcite/rel/rel2sql/RelToSqlConverterTest.java | 24 ++++++++++++
5 files changed, 70 insertions(+), 34 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDelete.java
b/core/src/main/java/org/apache/calcite/sql/SqlDelete.java
index 9e5ff8c87d..c9da35c48b 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDelete.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDelete.java
@@ -152,8 +152,7 @@ public SqlNode getTargetTable() {
}
SqlNode condition = this.condition;
if (condition != null) {
- writer.sep("WHERE");
- condition.unparse(writer, opLeft, opRight);
+ SqlUtil.unparseWhereClause(writer, condition, opLeft, opRight);
}
writer.endList(frame);
}
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java
b/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java
index b8db2d8f5a..7e37ac732f 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java
@@ -16,7 +16,6 @@
*/
package org.apache.calcite.sql;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.util.SqlBasicVisitor;
@@ -24,7 +23,6 @@
import org.checkerframework.checker.nullness.qual.Nullable;
-import java.util.ArrayList;
import java.util.List;
import static org.apache.calcite.linq4j.Nullness.castNonNull;
@@ -175,34 +173,7 @@ public SqlSelect createCall(
SqlNode where = select.where;
if (where != null) {
- writer.sep("WHERE");
-
- if (!writer.isAlwaysUseParentheses()) {
- SqlNode node = where;
-
- // decide whether to split on ORs or ANDs
- SqlBinaryOperator whereSep = SqlStdOperatorTable.AND;
- if ((node instanceof SqlCall)
- && node.getKind() == SqlKind.OR) {
- whereSep = SqlStdOperatorTable.OR;
- }
-
- // unroll whereClause
- final List<SqlNode> list = new ArrayList<>(0);
- while (node.getKind() == whereSep.kind) {
- assert node instanceof SqlCall;
- final SqlCall call1 = (SqlCall) node;
- list.add(0, call1.operand(1));
- node = call1.operand(0);
- }
- list.add(0, node);
-
- // unparse in a WHERE_LIST frame
- writer.list(SqlWriter.FrameTypeEnum.WHERE_LIST, whereSep,
- new SqlNodeList(list, where.getParserPosition()));
- } else {
- where.unparse(writer, 0, 0);
- }
+ SqlUtil.unparseWhereClause(writer, where, 0, 0);
}
if (select.groupBy != null) {
SqlNodeList groupBy =
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
b/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
index 82985f2bd7..22d669efcf 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
@@ -202,8 +202,7 @@ public void setSourceSelect(SqlSelect sourceSelect) {
writer.endList(setFrame);
SqlNode condition = this.condition;
if (condition != null) {
- writer.sep("WHERE");
- condition.unparse(writer, opLeft, opRight);
+ SqlUtil.unparseWhereClause(writer, condition, opLeft, opRight);
}
writer.endList(frame);
}
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
index d561bc8abb..1bafd7cfff 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java
@@ -454,6 +454,49 @@ public static void unparseBinarySyntax(
writer.endList(frame);
}
+ /**
+ * Unparses a WHERE clause.
+ *
+ * <p>Unparsing the condition in a {@link SqlWriter.FrameTypeEnum#WHERE_LIST}
+ * frame lets sub-queries in predicates recognize that they need
+ * parentheses.
+ *
+ * @param writer Writer
+ * @param where WHERE condition
+ * @param leftPrec Left precedence
+ * @param rightPrec Right precedence
+ */
+ public static void unparseWhereClause(SqlWriter writer, SqlNode where,
+ int leftPrec, int rightPrec) {
+ writer.sep("WHERE");
+
+ if (!writer.isAlwaysUseParentheses()) {
+ SqlNode node = where;
+
+ // Decide whether to split on ORs or ANDs.
+ SqlBinaryOperator whereSep = SqlStdOperatorTable.AND;
+ if ((node instanceof SqlCall)
+ && node.getKind() == SqlKind.OR) {
+ whereSep = SqlStdOperatorTable.OR;
+ }
+
+ // Unroll whereClause.
+ final List<SqlNode> list = new ArrayList<>(0);
+ while (node.getKind() == whereSep.kind) {
+ assert node instanceof SqlCall;
+ final SqlCall call1 = (SqlCall) node;
+ list.add(0, call1.operand(1));
+ node = call1.operand(0);
+ }
+ list.add(0, node);
+
+ writer.list(SqlWriter.FrameTypeEnum.WHERE_LIST, whereSep,
+ new SqlNodeList(list, where.getParserPosition()));
+ } else {
+ where.unparse(writer, leftPrec, rightPrec);
+ }
+ }
+
/**
* Concatenates string literals.
*
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 66afda8653..97d782fcae 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
@@ -10178,6 +10178,30 @@ private void checkLiteral2(String expression, String
expected) {
final String expected2 = "UPDATE \"foodmart\".\"product\" SET
\"product_name\" = 'calcite', "
+ "\"product_id\" = 10\nWHERE \"product_id\" = 1";
sql(sql2).ok(expected2);
+
+ final String sql3 = "update \"foodmart\".\"product\"\n"
+ + "set \"product_name\" = 'calcite'\n"
+ + "where exists (\n"
+ + " select 1 from \"foodmart\".\"product_class\")";
+ final String expected3 = "UPDATE \"foodmart\".\"product\" SET
\"product_name\" = "
+ + "'calcite'\nWHERE EXISTS (SELECT *\nFROM
\"foodmart\".\"product_class\")";
+ sql(sql3).ok(expected3);
+ }
+
+ @Test void testDelete() {
+ final String sql0 = "delete from \"foodmart\".\"product\"\n"
+ + "where exists (\n"
+ + " select 1 from \"foodmart\".\"product_class\")";
+ final String expected0 = "DELETE FROM \"foodmart\".\"product\"\n"
+ + "WHERE EXISTS (SELECT *\nFROM \"foodmart\".\"product_class\")";
+ sql(sql0).ok(expected0);
+
+ final String sql1 = "delete from \"foodmart\".\"product\"\n"
+ + "where not exists (\n"
+ + " select 1 from \"foodmart\".\"product_class\")";
+ final String expected1 = "DELETE FROM \"foodmart\".\"product\"\n"
+ + "WHERE NOT EXISTS (SELECT *\nFROM \"foodmart\".\"product_class\")";
+ sql(sql1).ok(expected1);
}
/**