zabetak commented on code in PR #4616:
URL: https://github.com/apache/calcite/pull/4616#discussion_r2522688958


##########
core/src/main/java/org/apache/calcite/rex/RexSimplify.java:
##########
@@ -1549,17 +1563,23 @@ enum SafeRexVisitor implements RexVisitor<Boolean> {
       SqlOperator sqlOperator = call.getOperator();
 
       switch (sqlKind) {
+      case CHECKED_DIVIDE:
       case DIVIDE:
       case MOD:
         List<RexNode> operands = call.getOperands();
-        boolean isSafe = RexVisitorImpl.visitArrayAnd(this, 
ImmutableList.of(operands.get(0)));
-        if (!isSafe) {
+        boolean hasNullOperand = RexUtil.isNullLiteral(operands.get(0), true)
+            || RexUtil.isNullLiteral(operands.get(1), true);
+        if (hasNullOperand) {
+          return true;

Review Comment:
   I agree it's better to be more conservative.



##########
core/src/main/java/org/apache/calcite/rex/RexSimplify.java:
##########
@@ -1231,7 +1230,7 @@ private RexNode simplifyIs(RexCall call, RexUnknownAs 
unknownAs) {
       return RexUtil.composeDisjunction(rexBuilder, operands, false);
     case AS_IS:
     default:
-      return null;
+      return rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, a);

Review Comment:
   Instead of bailing out now the method now returns a different potentially 
simplified expression. In addition, the way that we handle casts and perform 
the simplifications has also changed and as observed by the tests it has impact 
on expressions that have nothing to do with the division operator. The division 
topic has captured lots of discussion so to keep the PR minimal I would suggest 
to commit the improvements in `simplifyIsNotNull` and `simplifyIsNull` (that 
are not strictly related to division) to another JIRA/PR.



##########
core/src/main/java/org/apache/calcite/rex/RexSimplify.java:
##########
@@ -2338,14 +2372,28 @@ private void verify(RexNode before, RexNode simplified, 
RexUnknownAs unknownAs)
           continue assignment_loop;
         }
       }
-      Comparable v0 = RexInterpreter.evaluate(foo0.e, map);
-      if (v0 == null) {
-        throw new AssertionError("interpreter returned null for " + foo0.e);
-      }
-      Comparable v1 = RexInterpreter.evaluate(foo1.e, map);
-      if (v1 == null) {
-        throw new AssertionError("interpreter returned null for " + foo1.e);
+      Pair<Comparable, RuntimeException> p0 = evaluate(foo0.e, map);
+      Pair<Comparable, RuntimeException> p1 = evaluate(foo1.e, map);
+      if (p0.right != null || p1.right != null) {
+        if (p0.right == null || p1.right == null) {
+          throw Util.first(p0.right, p1.right);
+        }
+        if (!p0.right.getClass().equals(p1.right.getClass())) {
+          AssertionError error = new AssertionError("exception class does not 
match");
+          error.addSuppressed(p0.right);
+          error.addSuppressed(p1.right);
+          throw error;
+        }
+        if (!java.util.Objects.equals(p0.right.getMessage(), 
p1.right.getMessage())) {
+          AssertionError error = new AssertionError("exception message does 
not match");
+          error.addSuppressed(p0.right);
+          error.addSuppressed(p1.right);
+          throw error;
+        }
+        continue;

Review Comment:
   Thanks for the explanation. It makes sense to extend the support for 
verifying errors/exceptions but let's do it in a separate JIRA/PR. I guess we 
may also be able to turn paranoid to `true` for various existing test cases so 
that would be a more complete fix.



##########
core/src/main/java/org/apache/calcite/rex/RexSimplify.java:
##########
@@ -2308,6 +2328,20 @@ private RexNode simplifyOrs(List<RexNode> terms, 
RexUnknownAs unknownAs) {
     return RexUtil.composeDisjunction(rexBuilder, terms);
   }
 
+  private Pair<Comparable, RuntimeException> evaluate(RexNode e, Map<RexNode, 
Comparable> map) {
+    Comparable c = null;
+    RuntimeException ex = null;
+    try {
+      c = RexInterpreter.evaluate(e, map);
+    } catch (RuntimeException exception) {
+      ex = exception;
+    }
+    if (c == null && ex == null) {
+      throw new AssertionError("interpreter returned null for " + e);
+    }
+    return Pair.of(c, ex);
+  }

Review Comment:
   The performance impact of verifying every single expression is significant 
so this "feature" is unlikely to be used in production. Moreover, production 
code should never throw an `AssertionError` to begin with. Anyways, I don't 
feel that strongly about it so we can keep the original version.



##########
core/src/test/resources/sql/sub-query.iq:
##########
@@ -1472,7 +1472,7 @@ EnumerableCalc(expr#0..3=[{inputs}], 
expr#4=[null:BOOLEAN], expr#5=[IS NOT NULL(
 # Test project null IN nullable
 select sal,
   cast(null as int) IN (
-    select case when true then deptno else null end
+    select case when deptno>0 then deptno else null end

Review Comment:
   When we are dealing with end-to-end tests (like .iq files) it's rather 
hard/impossible to keep the original intent of the test intact across 
revisions. Changing the query is also changing the test to some extend so in 
many cases the most reasonable option is to just update the plan.
   
   As far as I see the changes done here for the queries do not retain the 
original plan but just lead to different variants. If we kept the original 
queries the diff on this file would be probably smaller and the changes easier 
to reason about in the context of this (or another) PR.
   
   Personally, I would prefer to leave the queries in .iq files as is. If you 
think that we may miss test coverage I would rather invest in adding unit tests 
(or new end-to-end tests) around this area rather than modifying existing cases.



-- 
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