From f2d5e46fa705802a24f4af73f1245d3777d73e6d Mon Sep 17 00:00:00 2001 From: zzwqqq Date: Sun, 14 Jun 2026 07:46:06 +0800 Subject: [PATCH] [CALCITE-7585] SqlMerge unparse EXISTS predicates in ON clause without parentheses --- .../org/apache/calcite/sql/SqlDelete.java | 3 +- .../java/org/apache/calcite/sql/SqlMerge.java | 5 ++- .../apache/calcite/sql/SqlSelectOperator.java | 3 +- .../org/apache/calcite/sql/SqlUpdate.java | 3 +- .../java/org/apache/calcite/sql/SqlUtil.java | 34 ++++++++----------- .../rel/rel2sql/RelToSqlConverterTest.java | 17 ++++++++++ 6 files changed, 40 insertions(+), 25 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 c9da35c48bf5..7b3cf41c5cfe 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlDelete.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlDelete.java @@ -152,7 +152,8 @@ public SqlNode getTargetTable() { } SqlNode condition = this.condition; if (condition != null) { - SqlUtil.unparseWhereClause(writer, condition, opLeft, opRight); + writer.sep("WHERE"); + SqlUtil.unparseConditionClause(writer, condition, opLeft, opRight); } writer.endList(frame); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlMerge.java b/core/src/main/java/org/apache/calcite/sql/SqlMerge.java index ddadf32959e1..df450974a56b 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlMerge.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlMerge.java @@ -197,9 +197,8 @@ public void setSourceSelect(SqlSelect sourceSelect) { writer.keyword("USING"); source.unparse(writer, opLeft, opRight); - writer.newlineAndIndent(); - writer.keyword("ON"); - condition.unparse(writer, opLeft, opRight); + writer.sep("ON"); + SqlUtil.unparseConditionClause(writer, condition, opLeft, opRight); SqlUpdate updateCall = this.updateCall; if (updateCall != null) { 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 7e37ac732f2c..8605eef7b31d 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java @@ -173,7 +173,8 @@ public SqlSelect createCall( SqlNode where = select.where; if (where != null) { - SqlUtil.unparseWhereClause(writer, where, 0, 0); + writer.sep("WHERE"); + SqlUtil.unparseConditionClause(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 22d669efcf0b..0f743540d18e 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java @@ -202,7 +202,8 @@ public void setSourceSelect(SqlSelect sourceSelect) { writer.endList(setFrame); SqlNode condition = this.condition; if (condition != null) { - SqlUtil.unparseWhereClause(writer, condition, opLeft, opRight); + writer.sep("WHERE"); + SqlUtil.unparseConditionClause(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 1bafd7cfff85..b5c91a1c7ad0 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlUtil.java @@ -455,34 +455,30 @@ public static void unparseBinarySyntax( } /** - * Unparses a WHERE clause. + * Unparses a condition using a + * {@link SqlWriter.FrameTypeEnum#WHERE_LIST} frame, which provides + * predicate-list formatting. * - *

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 writer Writer + * @param condition Condition * @param leftPrec Left precedence * @param rightPrec Right precedence */ - public static void unparseWhereClause(SqlWriter writer, SqlNode where, - int leftPrec, int rightPrec) { - writer.sep("WHERE"); - + public static void unparseConditionClause(SqlWriter writer, + SqlNode condition, int leftPrec, int rightPrec) { if (!writer.isAlwaysUseParentheses()) { - SqlNode node = where; + SqlNode node = condition; // Decide whether to split on ORs or ANDs. - SqlBinaryOperator whereSep = SqlStdOperatorTable.AND; + SqlBinaryOperator conditionSep = SqlStdOperatorTable.AND; if ((node instanceof SqlCall) && node.getKind() == SqlKind.OR) { - whereSep = SqlStdOperatorTable.OR; + conditionSep = SqlStdOperatorTable.OR; } - // Unroll whereClause. + // Unroll condition. final List list = new ArrayList<>(0); - while (node.getKind() == whereSep.kind) { + while (node.getKind() == conditionSep.kind) { assert node instanceof SqlCall; final SqlCall call1 = (SqlCall) node; list.add(0, call1.operand(1)); @@ -490,10 +486,10 @@ public static void unparseWhereClause(SqlWriter writer, SqlNode where, } list.add(0, node); - writer.list(SqlWriter.FrameTypeEnum.WHERE_LIST, whereSep, - new SqlNodeList(list, where.getParserPosition())); + writer.list(SqlWriter.FrameTypeEnum.WHERE_LIST, conditionSep, + new SqlNodeList(list, condition.getParserPosition())); } else { - where.unparse(writer, leftPrec, rightPrec); + condition.unparse(writer, leftPrec, rightPrec); } } 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 97d782fcaeef..4e2825bb3b62 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 @@ -10046,6 +10046,23 @@ private void checkLiteral2(String expression, String expected) { sql(sql7) .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT) .ok(expected7); + + // [CALCITE-7585] SqlMerge unparse EXISTS predicates in ON clause + // without parentheses. + final String sql8 = "merge into \"DEPT\" as \"t\"\n" + + "using \"DEPT\" as \"s\"\n" + + "on exists (select 1 from \"EMP\" as \"e\" where \"e\".\"DEPTNO\" = \"s\".\"DEPTNO\")\n" + + "when matched then\n" + + "update set \"DNAME\" = \"s\".\"DNAME\""; + final String expected8 = "MERGE INTO \"SCOTT\".\"DEPT\" AS \"DEPT0\"\n" + + "USING \"SCOTT\".\"DEPT\"\n" + + "ON EXISTS (SELECT *\n" + + "FROM \"SCOTT\".\"EMP\"\n" + + "WHERE \"DEPTNO\" = \"DEPT\".\"DEPTNO\")\n" + + "WHEN MATCHED THEN UPDATE SET \"DNAME\" = \"DEPT\".\"DNAME\""; + sql(sql8) + .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT) + .ok(expected8); } /** Test case for