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

morrySnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new b96bb6b49be [fix](fe) Preserve narrowing datetimev2 casts in simplify 
in predicate (#63343)
b96bb6b49be is described below

commit b96bb6b49becce2b578460a1fe37d167d741143f
Author: starocean999 <[email protected]>
AuthorDate: Wed May 20 10:42:22 2026 +0800

    [fix](fe) Preserve narrowing datetimev2 casts in simplify in predicate 
(#63343)
    
    ### What problem does this PR solve?
    SimplifyInPredicate could incorrectly remove narrowing DATETIMEV2 casts
    inside IN predicates.
    For expressions such as `CAST(datetimev2(6) AS DATETIMEV2(3)) IN (...)`,
    the rewrite could turn the predicate
    into a direct comparison on the original DATETIMEV2(6) column, which is
    not semantics-preserving because the cast
    uses precision reduction and rounding. The rule also used a binary-based
    microsecond alignment check instead of a
    decimal scale check, which could incorrectly treat some literals as
    losslessly convertible.
    
    This change restricts the rewrite to non-narrowing DATETIMEV2 casts and
    fixes the literal alignment check to use
    decimal scale factors. It also adds FE unit tests and a regression case
    for the reported DATETIMEV2 scenario.
---
 .../expression/rules/SimplifyInPredicate.java      | 11 ++--
 .../rules/expression/SimplifyInPredicateTest.java  | 62 ++++++++++++++++++----
 .../test_simplify_in_predicate.out                 |  7 +++
 .../test_simplify_in_predicate.groovy              | 28 ++++++++++
 4 files changed, 93 insertions(+), 15 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SimplifyInPredicate.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SimplifyInPredicate.java
index 70a964929e2..b66db8e66f0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SimplifyInPredicate.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SimplifyInPredicate.java
@@ -63,10 +63,12 @@ public class SimplifyInPredicate implements 
ExpressionPatternRuleFactory {
                 } else if (cast.child().getDataType().isDateTimeV2Type()
                         && expr.child(1) instanceof DateTimeV2Literal) {
                     List<Expression> literals = expr.children().subList(1, 
expr.children().size());
+                    DateTimeV2Type castType = (DateTimeV2Type) 
cast.getDataType();
                     DateTimeV2Type compareType = (DateTimeV2Type) 
cast.child().getDataType();
-                    if (literals.stream().allMatch(literal -> literal 
instanceof DateTimeV2Literal
-                            && canLosslessConvertToLowScaleLiteral(
-                            (DateTimeV2Literal) literal, 
compareType.getScale()))) {
+                    if (castType.getScale() >= compareType.getScale()
+                            && literals.stream().allMatch(literal -> literal 
instanceof DateTimeV2Literal
+                                    && canLosslessConvertToLowScaleLiteral(
+                                            (DateTimeV2Literal) literal, 
compareType.getScale()))) {
                         ImmutableList.Builder<Expression> children = 
ImmutableList.builder();
                         children.add(cast.child());
                         literals.forEach(l -> children.add(new 
DateTimeV2Literal(compareType,
@@ -99,6 +101,7 @@ public class SimplifyInPredicate implements 
ExpressionPatternRuleFactory {
     }
 
     private static boolean 
canLosslessConvertToLowScaleLiteral(DateTimeV2Literal literal, int targetScale) 
{
-        return literal.getMicroSecond() % (1L << (DateTimeV2Type.MAX_SCALE - 
targetScale)) == 0;
+        long scaleFactor = (long) Math.pow(10, DateTimeV2Type.MAX_SCALE - 
targetScale);
+        return literal.getMicroSecond() % scaleFactor == 0;
     }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyInPredicateTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyInPredicateTest.java
index 09fc7346f56..90a4aed8d07 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyInPredicateTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyInPredicateTest.java
@@ -31,14 +31,22 @@ import java.util.Map;
 
 public class SimplifyInPredicateTest extends ExpressionRewriteTestHelper {
 
+    private ExpressionRuleExecutor newRewriteExecutor() {
+        return new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(
+                        FoldConstantRule.INSTANCE,
+                        SimplifyInPredicate.INSTANCE)));
+    }
+
+    private ExpressionRuleExecutor newFoldExecutor() {
+        return new ExpressionRuleExecutor(ImmutableList.of(
+                bottomUp(
+                        FoldConstantRule.INSTANCE)));
+    }
+
     @Test
     public void test() {
-        executor = new ExpressionRuleExecutor(ImmutableList.of(
-                bottomUp(
-                    FoldConstantRule.INSTANCE,
-                    SimplifyInPredicate.INSTANCE
-                )
-        ));
+        executor = newRewriteExecutor();
         Map<String, Slot> mem = Maps.newHashMap();
         Expression rewrittenExpression = PARSER.parseExpression("cast(CA as 
DATETIME) in ('1992-01-31 00:00:00', '1992-02-01 00:00:00')");
         // after parse and type coercion: CAST(CAST(CA AS DATETIMEV2(0)) AS 
DATETIMEV2(6)) IN ('1992-01-31 00:00:00.000000', '1992-02-01 00:00:00.000000')
@@ -49,12 +57,44 @@ public class SimplifyInPredicateTest extends 
ExpressionRewriteTestHelper {
         rewrittenExpression = executor.rewrite(rewrittenExpression, context);
         Expression expectedExpression = PARSER.parseExpression("CA in 
(cast('1992-01-31' as date), cast('1992-02-01' as date))");
         expectedExpression = replaceUnboundSlot(expectedExpression, mem);
-        executor = new ExpressionRuleExecutor(ImmutableList.of(
-            bottomUp(
-                FoldConstantRule.INSTANCE
-            )
-        ));
+        executor = newFoldExecutor();
         expectedExpression = executor.rewrite(expectedExpression, context);
         Assertions.assertEquals(expectedExpression, rewrittenExpression);
     }
+
+    @Test
+    public void testDoNotEliminateNarrowingDateTimeV2Cast() {
+        ExpressionRuleExecutor rewriteExecutor = newRewriteExecutor();
+        ExpressionRuleExecutor foldExecutor = newFoldExecutor();
+        Map<String, Slot> mem = Maps.newHashMap();
+        Expression rewrittenExpression = PARSER.parseExpression("cast(AA as 
DATETIMEV2(3)) in "
+                + "('2024-01-01 12:34:56.123000', '2024-01-01 
09:30:01.000000', '2024-01-01 22:00:00.000000')");
+        rewrittenExpression = 
typeCoercion(replaceUnboundSlot(rewrittenExpression, mem));
+        rewrittenExpression = rewriteExecutor.rewrite(rewrittenExpression, 
context);
+        Expression rewrittenAgain = 
rewriteExecutor.rewrite(rewrittenExpression, context);
+
+        Expression expectedExpression = PARSER.parseExpression("cast(AA as 
DATETIMEV2(3)) in "
+                + "(cast('2024-01-01 12:34:56.123' as DATETIMEV2(3)), "
+                + "cast('2024-01-01 09:30:01.000' as DATETIMEV2(3)), "
+                + "cast('2024-01-01 22:00:00.000' as DATETIMEV2(3)))");
+        expectedExpression = replaceUnboundSlot(expectedExpression, mem);
+        expectedExpression = foldExecutor.rewrite(expectedExpression, context);
+
+        Assertions.assertEquals(expectedExpression, rewrittenExpression);
+        Assertions.assertEquals(rewrittenExpression, rewrittenAgain);
+    }
+
+    @Test
+    public void testDateTimeV2LiteralMustAlignWithTargetScale() {
+        ExpressionRuleExecutor rewriteExecutor = newRewriteExecutor();
+        Map<String, Slot> mem = Maps.newHashMap();
+        Expression rewrittenExpression = PARSER.parseExpression("cast(cast(AA 
as DATETIMEV2(3)) as DATETIMEV2(6)) "
+                + "in ('2024-01-01 12:34:56.123128')");
+        rewrittenExpression = 
typeCoercion(replaceUnboundSlot(rewrittenExpression, mem));
+        Expression originalExpression = rewrittenExpression;
+
+        rewrittenExpression = rewriteExecutor.rewrite(rewrittenExpression, 
context);
+
+        Assertions.assertEquals(originalExpression, rewrittenExpression);
+    }
 }
diff --git 
a/regression-test/data/nereids_syntax_p0/test_simplify_in_predicate.out 
b/regression-test/data/nereids_syntax_p0/test_simplify_in_predicate.out
new file mode 100644
index 00000000000..17aa212b134
--- /dev/null
+++ b/regression-test/data/nereids_syntax_p0/test_simplify_in_predicate.out
@@ -0,0 +1,7 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !datetimev2_narrow_cast --
+1
+2
+3
+4
+
diff --git 
a/regression-test/suites/nereids_syntax_p0/test_simplify_in_predicate.groovy 
b/regression-test/suites/nereids_syntax_p0/test_simplify_in_predicate.groovy
index 0079d5a2bde..f7b5cbd55b1 100644
--- a/regression-test/suites/nereids_syntax_p0/test_simplify_in_predicate.groovy
+++ b/regression-test/suites/nereids_syntax_p0/test_simplify_in_predicate.groovy
@@ -35,4 +35,32 @@ suite("test_simplify_in_predicate") {
         sql "verbose select * from test_simplify_in_predicate_t where a in 
('1992-01-31', '1992-02-01', '1992-02-02', '1992-02-03', '1992-02-04');"
         notContains "CAST"
     }
+
+    sql 'drop table if exists test_simplify_in_predicate_datetimev2_t'
+    sql """CREATE TABLE IF NOT EXISTS 
`test_simplify_in_predicate_datetimev2_t` (
+            id INT NOT NULL,
+            ts6 DATETIMEV2(6) NOT NULL
+            ) ENGINE=OLAP
+            DUPLICATE KEY(`id`)
+            DISTRIBUTED BY HASH(`id`) BUCKETS 1
+            PROPERTIES (
+            "replication_num" = "1"
+            );"""
+    sql """INSERT INTO test_simplify_in_predicate_datetimev2_t VALUES
+            (1, '2024-01-01 12:34:56.123456'),
+            (2, '2024-01-01 09:30:00.999999'),
+            (3, '2024-01-01 09:30:01.000000'),
+            (4, '2024-01-01 22:00:00.000001');"""
+
+    order_qt_datetimev2_narrow_cast """
+            SELECT id
+            FROM test_simplify_in_predicate_datetimev2_t
+            WHERE CAST(ts6 AS DATETIMEV2(3)) IN (
+                CAST('2024-01-01 12:34:56.123000' AS DATETIMEV2(6)),
+                CAST('2024-01-01 09:30:01.000000' AS DATETIMEV2(6)),
+                CAST('2024-01-01 22:00:00.000000' AS DATETIMEV2(6))
+            )
+            ORDER BY 1
+            """
+
 }
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to