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);
   }
 
   /**

Reply via email to