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

xiong 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 82eb4bdd0f [CALCITE-6835] Invalid unparse for IS TRUE,IS FALSE,IS NOT 
TRUE and IS NOT FALSE in StarRocksDialect
82eb4bdd0f is described below

commit 82eb4bdd0f9aa472ca11fed4bfba4ce372d6eaac
Author: Xiong Duan <[email protected]>
AuthorDate: Wed Feb 26 09:15:29 2025 +0800

    [CALCITE-6835] Invalid unparse for IS TRUE,IS FALSE,IS NOT TRUE and IS NOT 
FALSE in StarRocksDialect
---
 .../apache/calcite/rel/rel2sql/SqlImplementor.java | 17 ++--
 .../java/org/apache/calcite/rex/RexBuilder.java    |  7 ++
 .../java/org/apache/calcite/sql/SqlDialect.java    |  5 ++
 .../calcite/sql/dialect/StarRocksSqlDialect.java   | 68 ++++++++++++++++
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 90 +++++++++++++++++++++-
 5 files changed, 180 insertions(+), 7 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 a0733b6924..d8e38d02d1 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
@@ -166,8 +166,12 @@ public abstract class SqlImplementor {
 
   protected final Map<CorrelationId, Context> correlTableMap = new HashMap<>();
 
-  /** Private RexBuilder for short-lived expressions. It has its own
-   * dedicated type factory, so don't trust the types to be canonized. */
+  /**
+   * Private RexBuilder for short-lived expressions. It has its own
+   * dedicated type factory, so don't trust the types to be canonized.
+   *
+   * @deprecated Replaced by {@link RexBuilder#DEFAULT}. */
+  @Deprecated // to be removed before 2.0
   final RexBuilder rexBuilder =
       new RexBuilder(new SqlTypeFactoryImpl(RelDataTypeSystemImpl.DEFAULT));
 
@@ -652,6 +656,7 @@ public SqlNode orderField(int ordinal) {
      * @param rex Expression to convert
      */
     public SqlNode toSql(@Nullable RexProgram program, RexNode rex) {
+      rex = dialect.prepareUnparse(rex);
       final RexSubQuery subQuery;
       final SqlNode sqlSubQuery;
       final RexLiteral literal;
@@ -798,7 +803,7 @@ public SqlNode toSql(@Nullable RexProgram program, RexNode 
rex) {
           //noinspection unchecked
           return toSql(program, search.operands.get(0), literal.getType(), 
sarg);
         }
-        return toSql(program, RexUtil.expandSearch(implementor().rexBuilder, 
program, search));
+        return toSql(program, RexUtil.expandSearch(RexBuilder.DEFAULT, 
program, search));
 
       case EXISTS:
       case UNIQUE:
@@ -953,7 +958,7 @@ private <C extends Comparable<C>> SqlNode toSql(@Nullable 
RexProgram program,
         final RangeSets.Consumer<C> consumer =
             new RangeToSql<>(operandSql, orList, v ->
                 toSql(program,
-                    implementor().rexBuilder.makeLiteral(v, type)));
+                    RexBuilder.DEFAULT.makeLiteral(v, type)));
         RangeSets.forEach(sarg.rangeSet, consumer);
       }
       return SqlUtil.createCall(SqlStdOperatorTable.OR, POS, orList);
@@ -965,7 +970,7 @@ private <C extends Comparable<C>> SqlNode toIn(SqlNode 
operandSql,
       final SqlNodeList list = rangeSet.asRanges().stream()
           .map(range ->
               toSql(program,
-                  implementor().rexBuilder.makeLiteral(range.lowerEndpoint(),
+                  RexBuilder.DEFAULT.makeLiteral(range.lowerEndpoint(),
                       type, true, true)))
           .collect(SqlNode.toList());
       switch (list.size()) {
@@ -1718,7 +1723,7 @@ private JoinContext(SqlDialect dialect, Context 
leftContext,
             && ((RexInputRef) op0).getIndex() >= leftContext.fieldCount) {
           // Arguments were of form 'op1 = op0'
           final SqlOperator op2 = requireNonNull(call.getOperator().reverse());
-          return (RexCall) rexBuilder.makeCall(call.getParserPosition(), op2, 
op1, op0);
+          return (RexCall) 
RexBuilder.DEFAULT.makeCall(call.getParserPosition(), op2, op1, op0);
         }
         // fall through
       default:
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java 
b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
index a7d5559c54..641a0cb351 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -26,6 +26,7 @@
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
 import org.apache.calcite.runtime.FlatLists;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlCollation;
@@ -42,6 +43,7 @@
 import org.apache.calcite.sql.type.IntervalSqlType;
 import org.apache.calcite.sql.type.MapSqlType;
 import org.apache.calcite.sql.type.MultisetSqlType;
+import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
 import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.type.SqlTypeUtil;
@@ -94,6 +96,11 @@
  * <p>Some common literal values (NULL, TRUE, FALSE, 0, 1, '') are cached.
  */
 public class RexBuilder {
+
+  /** Default RexBuilder. */
+  public static final RexBuilder DEFAULT =
+      new RexBuilder(new SqlTypeFactoryImpl(RelDataTypeSystemImpl.DEFAULT));
+
   /**
    * Special operator that accesses an unadvertised field of an input record.
    * This operator cannot be used in SQL queries; it is introduced temporarily
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
index 6402a283ac..e6525a1d21 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
@@ -795,6 +795,11 @@ public boolean supportBooleanCaseWhen() {
     return true;
   }
 
+  /** Converts {@link RexNode} expression to {@link RexNode} expression before 
unparse. */
+  public RexNode prepareUnparse(RexNode rexNode) {
+    return rexNode;
+  }
+
   /** Returns whether this dialect supports a given function or operator.
    * It only applies to built-in scalar functions and operators, since
    * user-defined functions and procedures should be read by JdbcSchema. */
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java
index e22b63b13d..a21c1b577b 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java
@@ -21,6 +21,10 @@
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.sql.SqlAbstractDateTimeLiteral;
 import org.apache.calcite.sql.SqlAlienSystemTypeNameSpec;
 import org.apache.calcite.sql.SqlBasicTypeNameSpec;
@@ -31,11 +35,16 @@
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.fun.SqlFloorFunction;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.SqlTypeName;
 
 import org.checkerframework.checker.nullness.qual.Nullable;
 
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_FALSE;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_FALSE;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_TRUE;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_TRUE;
 import static org.apache.calcite.util.RelToSqlConverterUtil.unparseHiveTrim;
 
 /**
@@ -93,6 +102,65 @@ public StarRocksSqlDialect(Context context) {
     return true;
   }
 
+  @Override public RexNode prepareUnparse(RexNode arg) {
+    if (!(arg instanceof RexCall)) {
+      return arg;
+    }
+    RexCall call = (RexCall) arg;
+    switch (call.getOperator().getKind()) {
+    case IS_FALSE:
+      RexNode operandIsFalse = call.operands.get(0);
+      if (RexUtil.isDeterministic(operandIsFalse)) {
+        // A IS FALSE -> A IS NOT NULL AND NOT A
+        RexNode isNotNullFunc =
+            RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.IS_NOT_NULL, 
operandIsFalse);
+        RexNode notFunc =
+            RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.NOT, 
operandIsFalse);
+        return RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.AND, 
isNotNullFunc, notFunc);
+      } else {
+        throw new UnsupportedOperationException("Unsupported unparse: "
+            + call.getOperator().getName());
+      }
+    case IS_NOT_FALSE:
+      RexNode operandIsNotFalse = call.operands.get(0);
+      if (RexUtil.isDeterministic(operandIsNotFalse)) {
+        // A IS NOT FALSE -> A IS NULL OR A
+        RexNode isNullFunc =
+            RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.IS_NULL, 
operandIsNotFalse);
+        return RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.OR, isNullFunc, 
operandIsNotFalse);
+      } else {
+        throw new UnsupportedOperationException("Unsupported unparse: "
+            + call.getOperator().getName());
+      }
+    case IS_TRUE:
+      RexNode operandIsTrue = call.operands.get(0);
+      if (RexUtil.isDeterministic(operandIsTrue)) {
+        // A IS TRUE -> A IS NOT NULL AND A
+        RexNode isNotNullFunc =
+            RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.IS_NOT_NULL, 
operandIsTrue);
+        return RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.AND, 
isNotNullFunc, operandIsTrue);
+      } else {
+        throw new UnsupportedOperationException("Unsupported unparse: "
+            + call.getOperator().getName());
+      }
+    case IS_NOT_TRUE:
+      RexNode operandIsNotTrue = call.operands.get(0);
+      if (RexUtil.isDeterministic(operandIsNotTrue)) {
+        // A IS NOT TRUE -> A IS NULL OR NOT A
+        RexNode isNullFunc =
+            RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.IS_NULL, 
operandIsNotTrue);
+        RexNode notFunc =
+            RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.NOT, 
operandIsNotTrue);
+        return RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.OR, isNullFunc, 
notFunc);
+      } else {
+        throw new UnsupportedOperationException("Unsupported unparse: "
+            + call.getOperator().getName());
+      }
+    default:
+      return super.prepareUnparse(arg);
+    }
+  }
+
   @Override public void unparseCall(SqlWriter writer, SqlCall call, int 
leftPrec, int rightPrec) {
     switch (call.getKind()) {
     case ARRAY_VALUE_CONSTRUCTOR:
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 ba9d311f20..9169aebe48 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
@@ -374,7 +374,7 @@ private static String toSql(RelNode root, SqlDialect 
dialect,
         + "WHERE `product_id` > 0\n"
         + "GROUP BY `product_id`";
     final String expectedStarRocks = "SELECT"
-        + " SUM(CASE WHEN `net_weight` > 0E0 IS TRUE"
+        + " SUM(CASE WHEN `net_weight` > 0E0 IS NOT NULL AND `net_weight` > 
0E0"
         + " THEN `shelf_width` ELSE NULL END), SUM(`shelf_width`)\n"
         + "FROM `foodmart`.`product`\n"
         + "WHERE `product_id` > 0\n"
@@ -8553,6 +8553,94 @@ private void checkLiteral2(String expression, String 
expected) {
         .ok(expected);
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6835";>[CALCITE-6835]
+   * Invalid unparse for IS TRUE,IS FALSE,IS NOT TRUE and IS NOT FALSE
+   * in StarRocksDialect</a>. */
+  @Test void testIsTrue() {
+    final String sql = "SELECT * FROM \"EMP\" WHERE \"COMM\" > 0 IS TRUE";
+    final String expected = "SELECT *\n"
+        + "FROM \"SCOTT\".\"EMP\"\n"
+        + "WHERE CAST(\"COMM\" AS DECIMAL(12, 2)) > 0.00 IS TRUE";
+    String expectedStarRocks = "SELECT *\n"
+        + "FROM `SCOTT`.`EMP`\n"
+        + "WHERE CAST(`COMM` AS DECIMAL(12, 2)) > 0.00 IS NOT NULL AND 
CAST(`COMM` AS DECIMAL(12, 2)) > 0.00";
+    sql(sql)
+        .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+        .ok(expected)
+        .withStarRocks().ok(expectedStarRocks);
+
+    final String sqlNoDeterministic =
+        "SELECT * FROM \"EMP\" WHERE \"COMM\" > RAND_INTEGER(10) IS TRUE";
+    sql(sqlNoDeterministic)
+        .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+        .withStarRocks()
+        .throws_("Unsupported unparse: IS TRUE");
+  }
+
+  @Test void testIsNotTrue() {
+    final String sql = "SELECT * FROM \"EMP\" WHERE \"COMM\" > 0 IS NOT TRUE";
+    final String expected = "SELECT *\n"
+        + "FROM \"SCOTT\".\"EMP\"\n"
+        + "WHERE CAST(\"COMM\" AS DECIMAL(12, 2)) > 0.00 IS NOT TRUE";
+    String expectedStarRocks = "SELECT *\n"
+        + "FROM `SCOTT`.`EMP`\n"
+        + "WHERE CAST(`COMM` AS DECIMAL(12, 2)) > 0.00 IS NULL OR NOT 
CAST(`COMM` AS DECIMAL(12, 2)) > 0.00";
+    sql(sql)
+        .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+        .ok(expected)
+        .withStarRocks().ok(expectedStarRocks);
+
+    final String sqlNoDeterministic = "SELECT * \n"
+        + "FROM \"EMP\" WHERE \"COMM\" > RAND_INTEGER(10) IS NOT TRUE";
+    sql(sqlNoDeterministic)
+        .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+        .withStarRocks()
+        .throws_("Unsupported unparse: IS NOT TRUE");
+  }
+
+  @Test void testIsFalse() {
+    final String sql = "SELECT * FROM \"EMP\" WHERE \"COMM\" > 0 IS FALSE";
+    final String expected = "SELECT *\n"
+        + "FROM \"SCOTT\".\"EMP\"\n"
+        + "WHERE CAST(\"COMM\" AS DECIMAL(12, 2)) > 0.00 IS FALSE";
+    String expectedStarRocks = "SELECT *\n"
+        + "FROM `SCOTT`.`EMP`\n"
+        + "WHERE CAST(`COMM` AS DECIMAL(12, 2)) > 0.00 IS NOT NULL AND NOT 
CAST(`COMM` AS DECIMAL(12, 2)) > 0.00";
+    sql(sql)
+        .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+        .ok(expected)
+        .withStarRocks().ok(expectedStarRocks);
+
+    final String sqlNoDeterministic = "SELECT * \n"
+        + "FROM \"EMP\" WHERE \"COMM\" > RAND_INTEGER(10) IS FALSE";
+    sql(sqlNoDeterministic)
+        .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+        .withStarRocks()
+        .throws_("Unsupported unparse: IS FALSE");
+  }
+
+  @Test void testIsNotFalse() {
+    final String sql = "SELECT * FROM \"EMP\" WHERE \"COMM\" > 0 IS NOT FALSE";
+    final String expected = "SELECT *\n"
+        + "FROM \"SCOTT\".\"EMP\"\n"
+        + "WHERE CAST(\"COMM\" AS DECIMAL(12, 2)) > 0.00 IS NOT FALSE";
+    String expectedStarRocks = "SELECT *\n"
+        + "FROM `SCOTT`.`EMP`\n"
+        + "WHERE CAST(`COMM` AS DECIMAL(12, 2)) > 0.00 IS NULL OR CAST(`COMM` 
AS DECIMAL(12, 2)) > 0.00";
+    sql(sql)
+        .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+        .ok(expected)
+        .withStarRocks().ok(expectedStarRocks);
+
+    final String sqlNoDeterministic = "SELECT * \n"
+        + "FROM \"EMP\" WHERE \"COMM\" > RAND_INTEGER(10) IS NOT FALSE";
+    sql(sqlNoDeterministic)
+        .schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
+        .withStarRocks()
+        .throws_("Unsupported unparse: IS NOT FALSE");
+  }
+
   @Test void testMerge() {
     final String sql1 = "merge into \"DEPT\" as \"t\"\n"
         + "using \"DEPT\" as \"s\"\n"

Reply via email to