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

Gabriel39 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 8c320adefb1 [fix](fe) Prevent invalid Trino string predicate pushdown 
(#67209)
8c320adefb1 is described below

commit 8c320adefb17e876974561efb005e30eb41de305
Author: Socrates <[email protected]>
AuthorDate: Thu Sep 17 09:35:47 2026 +0800

    [fix](fe) Prevent invalid Trino string predicate pushdown (#67209)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: #64304
    
    Problem Summary:
    
    Doris erases CastExpr nodes while converting predicates through the
    Connector SPI. The Trino bridge could therefore apply a literal produced
    for the cast result directly to the raw remote column. This changed
    comparison semantics not only for VARCHAR-to-DATETIME predicates, but
    also for non-string casts, TRY_CAST subclasses, and casted NULL checks,
    and could prune rows before Doris evaluated the residual predicate. In
    addition, a widened or partially accepted Trino TupleDomain could be
    combined with source-side LIMIT and under-return rows.
    
    This change declares Trino CAST predicate pushdown unsupported and keeps
    every CAST-family conjunct in Doris. The engine gate now detects
    CastExpr by instanceof so TryCastExpr and future subclasses are covered.
    Trino metadata filtering is deferred to scan planning, after this
    capability gate has removed unsafe conjuncts. The converter still
    restricts Trino string domains to Doris string-family literals as
    defense in depth. The shared SPI Javadoc and Trino test rationale now
    match the runtime behavior. Expected unsupported conversions log at
    DEBUG while unexpected runtime conversion failures remain WARN.
    
    Source LIMIT is now pushed only for unfiltered Trino scans. This
    conservative gate prevents LIMIT from running before any residual or
    approximate filter; safe non-CAST filters remain pushable, and
    unfiltered LIMIT pushdown remains available.
    
    The Trino Hive regression covers both the STRING partition-column
    DATETIME range and malformed-string TRY_CAST IS NULL/null-safe equality
    behavior.
    
    ### Release note
    
    Fix Trino Connector queries whose CAST or TRY_CAST predicates could be
    pushed with different source semantics or combined unsafely with
    source-side LIMIT.
    
    ### Check List (For Author)
    
    - Test
        - [ ] Regression test
        - [x] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason
    
    Unit tests executed successfully:
    
    - EXTRA_FE_MODULES=trino=fe-connector/fe-connector-trino ./run-fe-ut.sh
    --run
    
org.apache.doris.datasource.scan.PluginDrivenScanNodeLimitStripTest,org.apache.doris.connector.trino.TrinoPredicateConverterTest,org.apache.doris.connector.trino.TrinoScanPlanProviderTest
    (28 tests, 0 failures; full 67-module FE reactor and Checkstyle
    succeeded)
    - mvn -f fe/pom.xml -pl :fe-connector-trino -am test (62 connector
    tests, 0 failures, executed before the TRY_CAST follow-up)
    
    The test_trino_hive_other regression suite loaded and completed, but
    enableHiveTest=false and no local Doris/Hive service was available, so
    the external query body was not exercised locally. A full build.sh --fe
    run is blocked by the unrelated worktree-local build.sh copy, which
    selects a module absent from this checkout; that local script
    modification is excluded from this PR.
    
    - Behavior changed:
        - [ ] No.
    - [x] Yes. Trino CAST and TRY_CAST predicates remain in Doris; filtered
    scans no longer push a source LIMIT; safe non-CAST predicates continue
    to be pushed during scan planning.
    
    - Does this need documentation?
        - [x] No.
        - [ ] Yes.
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label
---
 .../doris/connector/spi/ConnectorPushdownOps.java  |  6 +--
 .../trino/TrinoConnectorDorisMetadata.java         | 60 +++-------------------
 .../connector/trino/TrinoPredicateConverter.java   | 28 ++++++++--
 .../connector/trino/TrinoScanPlanProvider.java     | 10 +++-
 .../trino/TrinoPredicateConverterTest.java         | 31 +++++++++++
 .../connector/trino/TrinoScanPlanProviderTest.java | 59 +++++++++++++++++++++
 .../datasource/scan/PluginDrivenScanNode.java      |  6 +--
 .../scan/PluginDrivenScanNodeLimitStripTest.java   | 12 +++++
 .../hive/test_trino_hive_other.groovy              | 18 ++++++-
 9 files changed, 164 insertions(+), 66 deletions(-)

diff --git 
a/fe/fe-connector/fe-connector-spi/src/main/java/org/apache/doris/connector/spi/ConnectorPushdownOps.java
 
b/fe/fe-connector/fe-connector-spi/src/main/java/org/apache/doris/connector/spi/ConnectorPushdownOps.java
index 7b29363dfb2..add1b46ccdf 100644
--- 
a/fe/fe-connector/fe-connector-spi/src/main/java/org/apache/doris/connector/spi/ConnectorPushdownOps.java
+++ 
b/fe/fe-connector/fe-connector-spi/src/main/java/org/apache/doris/connector/spi/ConnectorPushdownOps.java
@@ -51,7 +51,7 @@ public interface ConnectorPushdownOps {
 
     /**
      * Returns whether this connector supports pushing down predicates that 
contain
-     * implicit CAST expressions.
+     * CAST expressions, including CAST subclasses such as TRY_CAST.
      *
      * <p><b>This switch governs ONE of the two pushdown paths.</b> Returning 
{@code false} makes the engine
      * drop CAST-containing conjuncts from the RESIDUAL predicate it builds 
for the scan node. The
@@ -72,8 +72,8 @@ public interface ConnectorPushdownOps {
      * different type coercion rules (e.g. a JDBC database) overrides this to 
{@code false}, optionally driven
      * by session configuration.</p>
      *
-     * <p><b>Every shipped connector answers this deliberately; none of them 
merely inherits.</b> jdbc, paimon
-     * and maxcompute return {@code false}; iceberg, elasticsearch and the 
trino bridge state {@code true} at
+     * <p><b>Every shipped connector answers this deliberately; none of them 
merely inherits.</b> jdbc, paimon,
+     * maxcompute and the trino bridge return {@code false}; iceberg and 
elasticsearch state {@code true} at
      * their own metadata class, each recording what it does with the 
predicate and that {@code true} is an
      * accepted risk rather than a safety claim. hive and hudi declare nothing 
because for them the switch is
      * INERT — their scan planning ignores the residual filter entirely, and 
the predicate they do consume
diff --git 
a/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoConnectorDorisMetadata.java
 
b/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoConnectorDorisMetadata.java
index 2b54ead6d59..04eb6798728 100644
--- 
a/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoConnectorDorisMetadata.java
+++ 
b/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoConnectorDorisMetadata.java
@@ -36,11 +36,8 @@ import io.trino.Session;
 import io.trino.spi.connector.CatalogHandle;
 import io.trino.spi.connector.ColumnHandle;
 import io.trino.spi.connector.ColumnMetadata;
-import io.trino.spi.connector.Constraint;
-import io.trino.spi.connector.ConstraintApplicationResult;
 import io.trino.spi.connector.SchemaTableName;
 import io.trino.spi.expression.Variable;
-import io.trino.spi.predicate.TupleDomain;
 import io.trino.spi.transaction.IsolationLevel;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -252,18 +249,13 @@ public class TrinoConnectorDorisMetadata implements 
ConnectorMetadata {
     }
 
     /**
-     * The trino-connector bridge accepts CAST-bearing predicates ({@code 
true}, the SPI default, stated here
-     * rather than inherited).
-     *
-     * <p>This is a conscious acceptance of the risk the SPI documents, not a 
claim of safety: the residual
-     * predicate becomes a trino {@code Constraint} and is handed to the 
embedded trino connector's own
-     * {@code applyFilter}, which may turn it into source-side filtering with 
that system's coercion rules. It
-     * stays {@code true} because the bridge cannot tell which embedded 
connector will do so, and dropping all
-     * CAST-bearing conjuncts would silently de-optimize every trino 
catalog.</p>
+     * CAST nodes, including subclasses such as TRY_CAST, are erased at the 
Doris connector-expression
+     * boundary, so the bridge cannot prove that a domain over the raw Trino 
column preserves the casted
+     * Doris comparison. Keep those predicates local.
      */
     @Override
     public boolean supportsCastPredicatePushdown(ConnectorSession session) {
-        return true;
+        return false;
     }
 
     @Override
@@ -271,47 +263,9 @@ public class TrinoConnectorDorisMetadata implements 
ConnectorMetadata {
             ConnectorSession session,
             ConnectorTableHandle handle,
             ConnectorFilterConstraint constraint) {
-        TrinoTableHandle dorisHandle = (TrinoTableHandle) handle;
-        ConnectorExpression expression = constraint.getExpression();
-
-        TrinoPredicateConverter converter = new TrinoPredicateConverter(
-                dorisHandle.getColumnHandleMap(),
-                dorisHandle.getColumnMetadataMap());
-        TupleDomain<ColumnHandle> tupleDomain = converter.convert(expression);
-        if (tupleDomain.isAll()) {
-            return Optional.empty();
-        }
-
-        io.trino.spi.connector.ConnectorSession connSession =
-                trinoSession.toConnectorSession(trinoCatalogHandle);
-        io.trino.spi.connector.ConnectorTransactionHandle txn =
-                
trinoConnector.beginTransaction(IsolationLevel.READ_UNCOMMITTED, true, true);
-        try {
-            io.trino.spi.connector.ConnectorMetadata metadata =
-                    trinoConnector.getMetadata(connSession, txn);
-
-            
Optional<ConstraintApplicationResult<io.trino.spi.connector.ConnectorTableHandle>>
 trinoResult =
-                    metadata.applyFilter(connSession, 
dorisHandle.getTrinoTableHandle(),
-                            new Constraint(tupleDomain));
-            if (!trinoResult.isPresent()) {
-                return Optional.empty();
-            }
-
-            TrinoTableHandle newHandle = new TrinoTableHandle(
-                    dorisHandle.getDbName(),
-                    dorisHandle.getTableName(),
-                    trinoResult.get().getHandle(),
-                    dorisHandle.getColumnHandleMap(),
-                    dorisHandle.getColumnMetadataMap());
-
-            // Trino tracks the remaining filter as a TupleDomain, not as a 
Doris ConnectorExpression.
-            // Returning the original expression keeps BE-side re-evaluation, 
matching the legacy
-            // fe-core scan-node behavior. A future enhancement could try to 
map the remaining
-            // TupleDomain back to a ConnectorExpression and clear 
fully-pushed conjuncts.
-            return Optional.of(new FilterApplicationResult<>(newHandle, 
expression, false));
-        } finally {
-            releaseQuietly(txn);
-        }
+        // PluginDrivenScanNode applies metadata filters before it can strip 
CAST-bearing conjuncts.
+        // Defer Trino filtering to planScan, whose residual filter has 
already passed that capability gate.
+        return Optional.empty();
     }
 
     @Override
diff --git 
a/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoPredicateConverter.java
 
b/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoPredicateConverter.java
index 9035ed2c7a1..d55ce1d9a56 100644
--- 
a/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoPredicateConverter.java
+++ 
b/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoPredicateConverter.java
@@ -46,6 +46,7 @@ import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 import java.time.temporal.ChronoField;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 /**
@@ -77,7 +78,10 @@ public class TrinoPredicateConverter {
         }
         try {
             return doConvert(expr);
-        } catch (Exception e) {
+        } catch (UnsupportedOperationException e) {
+            LOG.debug("Expression is not eligible for Trino predicate 
pushdown: {}", e.getMessage());
+            return TupleDomain.all();
+        } catch (RuntimeException e) {
             LOG.warn("Failed to convert expression to Trino TupleDomain: {}", 
e.getMessage());
             return TupleDomain.all();
         }
@@ -104,7 +108,9 @@ public class TrinoPredicateConverter {
         for (ConnectorExpression child : and.getConjuncts()) {
             try {
                 result = result.intersect(doConvert(child));
-            } catch (Exception e) {
+            } catch (UnsupportedOperationException e) {
+                LOG.debug("AND child is not eligible for Trino predicate 
pushdown: {}", e.getMessage());
+            } catch (RuntimeException e) {
                 LOG.warn("Failed to convert AND child: {}", e.getMessage());
             }
         }
@@ -277,7 +283,7 @@ public class TrinoPredicateConverter {
             case "CharType":
             case "VarbinaryType":
             case "VarcharType":
-                return Slices.utf8Slice(String.valueOf(value));
+                return convertStringLiteralValue(literal);
             case "DateType": {
                 if (value instanceof LocalDate) {
                     return ((LocalDate) value).toEpochDay();
@@ -305,4 +311,20 @@ public class TrinoPredicateConverter {
         }
         return new BigDecimal(String.valueOf(value));
     }
+
+    private Object convertStringLiteralValue(ConnectorLiteral literal) {
+        if (literal.isNull()) {
+            return null;
+        }
+        String literalType = 
literal.getType().getTypeName().toUpperCase(Locale.ROOT);
+        switch (literalType) {
+            case "CHAR":
+            case "VARCHAR":
+            case "STRING":
+                return Slices.utf8Slice((String) literal.getValue());
+            default:
+                throw new UnsupportedOperationException(
+                        "Cannot convert Doris literal type " + literalType + " 
to a Trino string type");
+        }
+    }
 }
diff --git 
a/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoScanPlanProvider.java
 
b/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoScanPlanProvider.java
index 0dc9ed5b24e..acffcb19282 100644
--- 
a/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoScanPlanProvider.java
+++ 
b/fe/fe-connector/fe-connector-trino/src/main/java/org/apache/doris/connector/trino/TrinoScanPlanProvider.java
@@ -117,8 +117,10 @@ public class TrinoScanPlanProvider implements 
ConnectorScanPlanProvider {
                 currentTrinoHandle = filterResult.get().getHandle();
             }
 
-            // Apply limit pushdown
-            if (limit > 0) {
+            // A TupleDomain may be a widened approximation of the Doris 
predicate, and the embedded
+            // connector may retain part of it. Applying LIMIT before Doris 
evaluates the residual can
+            // permanently discard matching rows. With no filter, the ordering 
is unambiguous and safe.
+            if (shouldApplyLimit(limit, filter)) {
                 
Optional<LimitApplicationResult<io.trino.spi.connector.ConnectorTableHandle>>
                         limitResult = metadata.applyLimit(connSession, 
currentTrinoHandle, limit);
                 if (limitResult.isPresent()) {
@@ -260,6 +262,10 @@ public class TrinoScanPlanProvider implements 
ConnectorScanPlanProvider {
         return new Constraint(tupleDomain);
     }
 
+    static boolean shouldApplyLimit(long limit, Optional<ConnectorExpression> 
filter) {
+        return limit > 0 && !filter.isPresent();
+    }
+
     // Serialize only the projected columns, in the same order (and with the 
same filter)
     // applyProjection used, so the column handles passed to the BE scanner 
match
     // JdbcTableHandle.getColumns() exactly (Trino's getRecordSet verifies 
handles.equals(columns)).
diff --git 
a/fe/fe-connector/fe-connector-trino/src/test/java/org/apache/doris/connector/trino/TrinoPredicateConverterTest.java
 
b/fe/fe-connector/fe-connector-trino/src/test/java/org/apache/doris/connector/trino/TrinoPredicateConverterTest.java
index 54a40db1076..e4d99db8e81 100644
--- 
a/fe/fe-connector/fe-connector-trino/src/test/java/org/apache/doris/connector/trino/TrinoPredicateConverterTest.java
+++ 
b/fe/fe-connector/fe-connector-trino/src/test/java/org/apache/doris/connector/trino/TrinoPredicateConverterTest.java
@@ -43,6 +43,7 @@ import io.trino.spi.type.VarcharType;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.Map;
 
@@ -162,6 +163,36 @@ public class TrinoPredicateConverterTest {
                 CONVERTER.convert(cmp));
     }
 
+    @Test
+    public void testVarcharDatetimeComparisonDegradesToAll() {
+        // PluginDrivenScanNode normally keeps CAST predicates local for 
Trino. Exercise this converter
+        // directly as defense in depth: encoding a DATETIME literal as a 
VARCHAR domain would change
+        // datetime comparison into lexicographic comparison and can lose rows.
+        ConnectorComparison cmp = new ConnectorComparison(
+                ConnectorComparison.Operator.GE, col("c_str"),
+                ConnectorLiteral.ofDatetime(LocalDateTime.of(2026, 8, 24, 0, 
0)));
+        Assertions.assertEquals(TupleDomain.<ColumnHandle>all(), 
CONVERTER.convert(cmp));
+    }
+
+    @Test
+    public void testAndSkipsIncompatibleVarcharDatetimeComparison() {
+        // An incompatible conjunct widens the pushed predicate by being 
omitted, while compatible conjuncts
+        // remain useful. Doris keeps and re-evaluates the original filter 
after the source scan.
+        ConnectorAnd and = new ConnectorAnd(Arrays.asList(
+                new ConnectorComparison(ConnectorComparison.Operator.EQ, 
col("c_int"), ConnectorLiteral.ofInt(5)),
+                new ConnectorComparison(ConnectorComparison.Operator.GE, 
col("c_str"),
+                        ConnectorLiteral.ofDatetime(LocalDateTime.of(2026, 8, 
24, 0, 0)))));
+        Assertions.assertEquals(expect("c_int", singleValue("c_int", 5L)), 
CONVERTER.convert(and));
+    }
+
+    @Test
+    public void testVarcharNullSafeEqualityKeepsOnlyNullDomain() {
+        ConnectorComparison cmp = new ConnectorComparison(
+                ConnectorComparison.Operator.EQ_FOR_NULL, col("c_str"),
+                ConnectorLiteral.ofNull(ConnectorType.of("VARCHAR")));
+        Assertions.assertEquals(expect("c_str", 
Domain.onlyNull(type("c_str"))), CONVERTER.convert(cmp));
+    }
+
     @Test
     public void testInProducesMultiValueDomain() {
         // c_int IN (1, 2, 3)  ->  domain of the three discrete values
diff --git 
a/fe/fe-connector/fe-connector-trino/src/test/java/org/apache/doris/connector/trino/TrinoScanPlanProviderTest.java
 
b/fe/fe-connector/fe-connector-trino/src/test/java/org/apache/doris/connector/trino/TrinoScanPlanProviderTest.java
new file mode 100644
index 00000000000..7e53e4120dd
--- /dev/null
+++ 
b/fe/fe-connector/fe-connector-trino/src/test/java/org/apache/doris/connector/trino/TrinoScanPlanProviderTest.java
@@ -0,0 +1,59 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.connector.trino;
+
+import org.apache.doris.connector.spi.pushdown.ConnectorExpression;
+import org.apache.doris.connector.spi.pushdown.ConnectorFilterConstraint;
+import org.apache.doris.connector.spi.pushdown.ConnectorLiteral;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+/** Tests correctness gates shared by Trino metadata and scan planning. */
+public class TrinoScanPlanProviderTest {
+
+    @Test
+    public void testFilterPreventsSourceLimitPushdown() {
+        Optional<ConnectorExpression> filter = 
Optional.of(ConnectorLiteral.ofBoolean(true));
+
+        Assertions.assertFalse(TrinoScanPlanProvider.shouldApplyLimit(10L, 
filter));
+    }
+
+    @Test
+    public void testUnfilteredScanCanPushLimit() {
+        Assertions.assertTrue(TrinoScanPlanProvider.shouldApplyLimit(10L, 
Optional.empty()));
+        Assertions.assertFalse(TrinoScanPlanProvider.shouldApplyLimit(-1L, 
Optional.empty()));
+    }
+
+    @Test
+    public void testTrinoRejectsCastPredicatePushdown() {
+        TrinoConnectorDorisMetadata metadata = new 
TrinoConnectorDorisMetadata(null, null, null);
+
+        Assertions.assertFalse(metadata.supportsCastPredicatePushdown(null));
+    }
+
+    @Test
+    public void testMetadataDefersFilteringUntilAfterCastGate() {
+        TrinoConnectorDorisMetadata metadata = new 
TrinoConnectorDorisMetadata(null, null, null);
+        ConnectorFilterConstraint constraint = new 
ConnectorFilterConstraint(ConnectorLiteral.ofBoolean(true));
+
+        Assertions.assertFalse(metadata.applyFilter(null, null, 
constraint).isPresent());
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/scan/PluginDrivenScanNode.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/scan/PluginDrivenScanNode.java
index ba5b9f1f55a..f3c24c0bb7f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/scan/PluginDrivenScanNode.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/scan/PluginDrivenScanNode.java
@@ -2589,9 +2589,9 @@ public class PluginDrivenScanNode extends 
FileQueryScanNode {
         return 
Optional.of(ExprToConnectorExpressionConverter.convertConjuncts(pushableConjuncts));
     }
 
-    private static boolean containsCastExpr(Expr expr) {
-        List<CastExpr> castExprs = new ArrayList<>();
-        expr.collect(CastExpr.class, castExprs);
+    static boolean containsCastExpr(Expr expr) {
+        List<Expr> castExprs = new ArrayList<>();
+        expr.collect(node -> node instanceof CastExpr, castExprs);
         return !castExprs.isEmpty();
     }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/scan/PluginDrivenScanNodeLimitStripTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/scan/PluginDrivenScanNodeLimitStripTest.java
index 914fea74925..dd86dfda528 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/scan/PluginDrivenScanNodeLimitStripTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/scan/PluginDrivenScanNodeLimitStripTest.java
@@ -17,6 +17,11 @@
 
 package org.apache.doris.datasource.scan;
 
+import org.apache.doris.analysis.IsNullPredicate;
+import org.apache.doris.analysis.StringLiteral;
+import org.apache.doris.analysis.TryCastExpr;
+import org.apache.doris.catalog.Type;
+
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -51,4 +56,11 @@ public class PluginDrivenScanNodeLimitStripTest {
         Assertions.assertEquals(10L, 
PluginDrivenScanNode.effectiveSourceLimit(10L, false));
         Assertions.assertEquals(-1L, 
PluginDrivenScanNode.effectiveSourceLimit(-1L, false));
     }
+
+    @Test
+    public void tryCastSubclassIsNotPushable() {
+        TryCastExpr tryCast = new TryCastExpr(Type.INT, new 
StringLiteral("abc"), true, false);
+
+        Assertions.assertTrue(PluginDrivenScanNode.containsCastExpr(new 
IsNullPredicate(tryCast, false)));
+    }
 }
diff --git 
a/regression-test/suites/external_table_p0/trino_connector/hive/test_trino_hive_other.groovy
 
b/regression-test/suites/external_table_p0/trino_connector/hive/test_trino_hive_other.groovy
index 33dde37d66e..3eae7e78974 100644
--- 
a/regression-test/suites/external_table_p0/trino_connector/hive/test_trino_hive_other.groovy
+++ 
b/regression-test/suites/external_table_p0/trino_connector/hive/test_trino_hive_other.groovy
@@ -29,7 +29,13 @@ suite("test_trino_hive_other", "p0,external") {
         qt_q32 """ select * from test_hive_doris order by id;"""
 
         qt_q33 """ select dt, k1, * from table_with_vertical_line order by dt 
desc, k1 desc limit 10;"""
-        order_qt_q34 """ select dt, k2 from table_with_vertical_line order by 
k2 desc limit 10;"""
+        // The highest k2 values are malformed integers. TRY_CAST makes them 
NULL, so both predicates are
+        // true. Incorrectly pushing either predicate as raw VARCHAR IS NULL 
would produce an empty result.
+        order_qt_q34 """
+            select dt, k2 from table_with_vertical_line
+            where try_cast(k2 as int) is null and try_cast(k2 as int) <=> null
+            order by k2 desc limit 10;
+        """
         qt_q35 """ select dt, k2 from table_with_vertical_line where 
dt='2022-11-24' order by k2 desc limit 10;"""
         qt_q36 """ select k2, k5 from table_with_vertical_line where 
dt='2022-11-25' order by k2 desc limit 10;"""
         order_qt_q37 """ select count(*) from table_with_vertical_line;"""
@@ -37,7 +43,15 @@ suite("test_trino_hive_other", "p0,external") {
         qt_q39 """ select k2, k5 from table_with_vertical_line where dt in 
('2022-11-25', '2022-11-24') order by k2 desc limit 10;"""
         qt_q40 """ select dt, dt, k2, k5, dt from table_with_vertical_line 
where dt in ('2022-11-25') or dt in ('2022-11-25') order by k2 desc limit 10;"""
         qt_q41 """ select dt, dt, k2, k5, dt from table_with_vertical_line 
where dt in ('2022-11-25') and dt in ('2022-11-24') order by k2 desc limit 
10;"""
-        qt_q42 """ select dt, dt, k2, k5, dt from table_with_vertical_line 
where dt in ('2022-11-25') or dt in ('2022-11-24') order by k2 desc limit 10;"""
+        // The datetime range is redundant with the string predicate, so q42 
keeps its generated result.
+        // An incorrect VARCHAR range pushdown loses the 2022-11-24 rows and 
makes this query fail.
+        qt_q42 """
+            select dt, dt, k2, k5, dt from table_with_vertical_line
+            where (dt in ('2022-11-25') or dt in ('2022-11-24'))
+                and cast(dt as datetime) >= timestamp('2022-11-24 00:00:00')
+                and cast(dt as datetime) < timestamp('2022-11-26 00:00:00')
+            order by k2 desc limit 10;
+        """
 
         qt_q43 """ select dt, k1, * from table_with_x01 order by dt desc, k1 
desc limit 10;"""
         qt_q44 """ select dt, k2 from table_with_x01 order by k2 desc limit 
10;"""


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

Reply via email to