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


##########
core/src/main/java/org/apache/calcite/rex/RexSimplify.java:
##########
@@ -2785,6 +2770,66 @@ && sameTypeOrNarrowsNullability(e.getType(), 
intExpr.getType())) {
     }
   }
 
+  /** Returns whether {@code CAST(CAST(x AS inner) AS outer)} can be 
simplified to
+   * {@code CAST(x AS outer)}, where x has type {@code source} and the cast 
from
+   * {@code source} to {@code inner} is lossless. */
+  private static boolean canRemoveInnerCast(RelDataType source, RelDataType 
inner,
+      RelDataType outer) {
+    // x :: y below means cast(x as y)
+    // Removing the inner cast changes the type of the operand of the outer 
cast
+    // from inner to source, but not its value. The result of the outer cast 
can
+    // still change, because a cast to a character or binary type formats its
+    // operand according to the operand's type, and a cast of a character 
operand
+    // parses its text.
+    // Cases A and B are in simplifyCast.
+    // C) The inner cast can be removed:
+    // C.1) char2expression :: char(4) :: char(5) => char2expression :: char(5)
+    // C.2) char2expression :: char(10) :: char(5) => char2expression :: 
char(5)
+    // C.3) char2expression :: varchar(10) :: char(5) => char2expression :: 
char(5)
+    // C.4) char6expression :: varchar(10) :: char(5) => char6expression :: 
char(5)
+    // C.5) smallintExpr :: int :: varchar => smallintExpr :: varchar
+    //      20 => '20' in both cases
+    // C.6) intExpr :: varchar :: int => intExpr :: int
+    //      20 => 20 in both cases
+    // C.7) intExpr :: varchar :: bigint => intExpr :: bigint
+    //      7369 => 7369 in both cases; the two casts to tinyint give an error 
in both cases
+    // D) The inner cast cannot be removed (see also B.2 and B.3 in 
simplifyCast):
+    // D.1) intExpr :: double :: varchar => must not be changed
+    //      20 => right: '20.0', wrong: '20'
+    //      realExpr :: double :: varchar => must not be changed
+    //      266.66666 => right: '266.6666564941406', wrong: '266.66666'
+    // D.2) intExpr :: decimal(12, 2) :: varchar => must not be changed
+    //      20 => right: '20.00', wrong: '20'
+    // D.3) smallintExpr :: bigint :: varbinary => must not be changed
+    //      20 => right: X'0000000000000014', wrong: X'0014'
+    // D.4) intExpr :: varchar :: timestamp => must not be changed
+    //      7369 => right: error, wrong: 1970-01-01 00:00:07
+    if (SqlTypeUtil.isBinary(outer)) {
+      // D.3
+      return false;
+    }
+    if (SqlTypeUtil.inCharFamily(inner)) {
+      if (inner.getSqlTypeName() == SqlTypeName.CHAR
+          && outer.getSqlTypeName() != SqlTypeName.CHAR) {
+        // B.2 and B.3 from simplifyCast
+        return false;
+      }
+      if (!SqlTypeUtil.inCharFamily(source)
+          && !SqlTypeUtil.inCharFamily(outer)
+          && outer.getSqlTypeName() != source.getSqlTypeName()

Review Comment:
   Despite what that paper claims, I think that formal verification is rather 
hopeless for real implementations of of real SQL because the SQL spec is rather 
loose, and each dialect makes different choices. Perhaps if you fix ONE dialect 
then you can do something by proving that some rewrites do not produce plans 
with equivalent semantics, but Calcite is supposed to be very flexible and can 
emulate features from many dialects based on various configuration flags.
   
   Agents do change the equation, since they never get tired writing test cases 
or debugging test failures; they are seemingly also good at writing proofs; 
tying proofs to the actual code is another matter, though. I don't think Java 
has good enough tooling for this; many people are using Verus these days for 
Rust.
   
   In my recent testing I have been asking agents to generate "all possible 
combinations" of tests for some reduced test matrices and to compare our 
results against postgres. This works very well when the Calcite semantics 
aligns with postgres. I have also been comparing our Rust runtime with the 
calcite constant folding implementation -- by evaluating the same expression on 
constants or on runtime data -- by turning on and off the REDUCE_* passes in 
our Calcite optimizer. Any discrepancy requires investigation: could be our bug 
or a Calcite bug. In the case of this PR the agent figured out that these 
rewrites happen even without the constant folding... so these are really bugs.
   
   I am not sure how useful this answer is. I am still learning how to best do 
things.



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