mihaibudiu commented on code in PR #5184:
URL: https://github.com/apache/calcite/pull/5184#discussion_r3834515084


##########
core/src/main/java/org/apache/calcite/rex/RexSimplify.java:
##########
@@ -1200,15 +1200,21 @@ private RexNode simplifyIs(RexCall call, RexUnknownAs 
unknownAs) {
     if (hasCustomNullabilityRules(a.getKind())) {
       return simplifiedResult;
     }
-    if (!isSafe) {
-      return simplifiedResult;
-    }
     switch (Strong.policy(a)) {
     case NOT_NULL:
+      // Drops the subtree; require full-tree safety so we don't hide runtime 
errors

Review Comment:
   should the comment be inside the braces?



##########
core/src/main/java/org/apache/calcite/rex/RexSimplify.java:
##########
@@ -1631,12 +1654,31 @@ enum SafeRexVisitor implements RexVisitor<Boolean> {
           || RexUtil.isLosslessCast(call)
           || safeOps.contains(sqlKind)
           || safeOperators.contains(sqlOperator)) {
-        return RexVisitorImpl.visitArrayAnd(this, call.operands);
+        return !deep || RexVisitorImpl.visitArrayAnd(this, call.operands);
       }
 
       return false;
     }
 
+    /**
+     * Shallow variant of the visitor: reports whether the OUTER node's
+     * operator can be evaluated on non-null operands without throwing at
+     * runtime. Unlike {@link #visitCall(RexCall)}, it does not recurse into
+     * the operands. Callers that only need to know whether the outer
+     * operator itself is safe (e.g. RexSimplify's {@code Strong.ANY}
+     * distribution branches, which preserve subtree evaluation) can use
+     * this in place of the full-tree {@link RexSimplify#isSafeExpression}.
+     *
+     * <p>Non-{@link RexCall} nodes are always shallow-safe (they cannot
+     * throw at their own level).
+     */
+    boolean isShallowSafe(RexNode node) {

Review Comment:
   Can this be called isOperatorSafe? After all, it's a property of the 
operator.



##########
core/src/test/java/org/apache/calcite/rex/RexProgramTest.java:
##########
@@ -3024,6 +3025,149 @@ trueLiteral, literal(1),
     checkSimplifyUnchanged(div(cast(vVarchar(), tInt(false)), nullInt));
   }
 
+  /**
+   * Test cases for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7722";>[CALCITE-7722]
+   * RexSimplify IS [NOT] NULL on a safe operator with Strong policy ANY and 
unsafe operands
+   * can be further simplified</a>.
+   */
+  @Test void testSimplifyIsNotNullDistributesAcrossStrongOpWithLossyCast() {
+    // "(CAST(?0.varchar0):INTEGER + 1) IS NOT NULL" ==> "IS NOT 
NULL(CAST(?0.varchar0):INTEGER)"
+    // The outer PLUS is strong AND shallow-safe; distribution keeps the

Review Comment:
   what is "distribution"?



##########
core/src/test/java/org/apache/calcite/rex/RexProgramTest.java:
##########
@@ -3024,6 +3025,149 @@ trueLiteral, literal(1),
     checkSimplifyUnchanged(div(cast(vVarchar(), tInt(false)), nullInt));
   }
 
+  /**
+   * Test cases for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7722";>[CALCITE-7722]
+   * RexSimplify IS [NOT] NULL on a safe operator with Strong policy ANY and 
unsafe operands
+   * can be further simplified</a>.
+   */
+  @Test void testSimplifyIsNotNullDistributesAcrossStrongOpWithLossyCast() {
+    // "(CAST(?0.varchar0):INTEGER + 1) IS NOT NULL" ==> "IS NOT 
NULL(CAST(?0.varchar0):INTEGER)"
+    // The outer PLUS is strong AND shallow-safe; distribution keeps the
+    // non-lossless CAST inside the rewrapped IS NOT NULL
+    checkSimplify(
+        isNotNull(plus(cast(vVarchar(), tInt(true)), literal(1))),
+        "IS NOT NULL(CAST(?0.varchar0):INTEGER)");
+
+    // Symmetric IS NULL peel:
+    // "(CAST(?0.varchar0):INTEGER + 1) IS NULL" ==> "IS 
NULL(CAST(?0.varchar0):INTEGER)"
+    checkSimplify(
+        isNull(plus(cast(vVarchar(), tInt(true)), literal(1))),
+        "IS NULL(CAST(?0.varchar0):INTEGER)");
+
+    // Confirm this is consistent with same expression without CAST

Review Comment:
   I don't know which cast this is talking about



##########
core/src/test/java/org/apache/calcite/rex/RexProgramTest.java:
##########
@@ -3024,6 +3025,149 @@ trueLiteral, literal(1),
     checkSimplifyUnchanged(div(cast(vVarchar(), tInt(false)), nullInt));
   }
 
+  /**
+   * Test cases for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7722";>[CALCITE-7722]
+   * RexSimplify IS [NOT] NULL on a safe operator with Strong policy ANY and 
unsafe operands
+   * can be further simplified</a>.
+   */
+  @Test void testSimplifyIsNotNullDistributesAcrossStrongOpWithLossyCast() {
+    // "(CAST(?0.varchar0):INTEGER + 1) IS NOT NULL" ==> "IS NOT 
NULL(CAST(?0.varchar0):INTEGER)"
+    // The outer PLUS is strong AND shallow-safe; distribution keeps the
+    // non-lossless CAST inside the rewrapped IS NOT NULL
+    checkSimplify(
+        isNotNull(plus(cast(vVarchar(), tInt(true)), literal(1))),
+        "IS NOT NULL(CAST(?0.varchar0):INTEGER)");
+
+    // Symmetric IS NULL peel:
+    // "(CAST(?0.varchar0):INTEGER + 1) IS NULL" ==> "IS 
NULL(CAST(?0.varchar0):INTEGER)"
+    checkSimplify(
+        isNull(plus(cast(vVarchar(), tInt(true)), literal(1))),
+        "IS NULL(CAST(?0.varchar0):INTEGER)");
+
+    // Confirm this is consistent with same expression without CAST
+    checkSimplify(isNotNull(plus(vInt(), literal(1))), "IS NOT NULL(?0.int0)");
+    checkSimplify(isNull(plus(vInt(), literal(1))), "IS NULL(?0.int0)");
+
+    // MULTIPLY is also strong + shallow-safe
+    checkSimplify(
+        isNotNull(mul(cast(vVarchar(), tInt(true)), literal(2))),
+        "IS NOT NULL(CAST(?0.varchar0):INTEGER)");
+    checkSimplify(
+        isNull(mul(cast(vVarchar(), tInt(true)), literal(2))),
+        "IS NULL(CAST(?0.varchar0):INTEGER)");
+    checkSimplify(isNotNull(mul(vInt(), literal(2))), "IS NOT NULL(?0.int0)");
+    checkSimplify(isNull(mul(vInt(), literal(2))), "IS NULL(?0.int0)");
+
+    // PLUS of two non-lossless CAST
+    checkSimplify(
+        isNotNull(
+            plus(
+                cast(vVarchar(0), tInt(true)),
+                cast(vVarchar(1), tInt(true)))),
+        "AND(IS NOT NULL(CAST(?0.varchar0):INTEGER), IS NOT 
NULL(CAST(?0.varchar1):INTEGER))");
+    checkSimplify(
+        isNull(
+            plus(
+                cast(vVarchar(0), tInt(true)),
+                cast(vVarchar(1), tInt(true)))),
+        "OR(IS NULL(CAST(?0.varchar0):INTEGER), IS 
NULL(CAST(?0.varchar1):INTEGER))");
+
+    // Nested PLUS:
+    // "((CAST(?0.varchar0):INTEGER + 1) + 2) IS NOT NULL"
+    //   ==> "IS NOT NULL(CAST(?0.varchar0):INTEGER)"
+    checkSimplify(
+        isNotNull(
+            plus(plus(cast(vVarchar(), tInt(true)), literal(1)), literal(2))),
+        "IS NOT NULL(CAST(?0.varchar0):INTEGER)");
+
+    // Operators with checked arithmetic: they cannot be peeled because they 
are not "safe"
+    // (they will throw at runtime in case of overflow)
+    checkSimplifyUnchanged(
+        isNotNull(checkedPlus(cast(vVarchar(), tInt(true)), literal(1))));
+    checkSimplifyUnchanged(
+        isNull(checkedPlus(cast(vVarchar(), tInt(true)), literal(1))));
+    checkSimplifyUnchanged(
+        isNotNull(checkedMul(cast(vVarchar(), tInt(true)), literal(2))));
+    checkSimplifyUnchanged(
+        isNull(checkedMul(cast(vVarchar(), tInt(true)), literal(2))));
+
+    // Arithmetic on DATE and INTERVAL
+    checkSimplify(
+        isNotNull(sub(vDate(), cast(vVarchar(), tDate(true)))),
+        "AND(IS NOT NULL(?0.date0), IS NOT NULL(CAST(?0.varchar0):DATE))");
+    checkSimplify(
+        isNotNull(plus(cast(vVarchar(), tDate(true)), interval(10, 
TimeUnit.DAY))),
+        "IS NOT NULL(CAST(?0.varchar0):DATE)");
+    checkSimplify(
+        isNull(plus(cast(vVarchar(), tDate(true)), interval(1, 
TimeUnit.MONTH))),

Review Comment:
   Isn't this wrong? Arithmetic on dates should be checked.
   Or maybe this kind of code can never be generated?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to