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]