Skip to content

[feature](lance) push down common string and boolean predicates - #67051

Open
Jay-ju wants to merge 2 commits into
apache:branch-4.1from
Jay-ju:lance-common-predicate-pushdown
Open

[feature](lance) push down common string and boolean predicates#67051
Jay-ju wants to merge 2 commits into
apache:branch-4.1from
Jay-ju:lance-common-predicate-pushdown

Conversation

@Jay-ju

@Jay-ju Jay-ju commented Aug 24, 2026

Copy link
Copy Markdown

What problem does this PR solve?

Lance regular scans currently leave common string predicates and direct boolean predicates in Doris, so Lance cannot filter rows before Arrow materialization and transfer.

What is changed?

  • Push two-argument LIKE / NOT LIKE predicates without backslash escapes through the existing Substrait filter.
  • Push starts_with(column, literal) and ends_with(column, literal).
  • Push direct boolean-column predicates, including NOT boolean_column.
  • Keep explicit/custom LIKE escapes, REGEXP, non-literal patterns, and non-string inputs as Doris residual predicates.
  • Extend FE unit and external Lance regression coverage.

Testing

  • mvn -pl fe-core -am -Dtest=LancePredicateConverterTest -DfailIfNoTests=false test (21 passed)
  • test_lance_scalar_predicate_pushdown against local MinIO + FE + BE
  • Manual end-to-end LargeUtf8 starts_with verification

Release note

Improve Lance predicate pushdown for common string and boolean expressions.

@Jay-ju
Jay-ju requested a review from yiguolei as a code owner August 24, 2026 02:46
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@Jay-ju

Jay-ju commented Aug 24, 2026

Copy link
Copy Markdown
Author

/review

1 similar comment
@yiguolei

Copy link
Copy Markdown
Contributor

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review outcome: request changes.

I verified the authoritative bundle against the live PR head 597d3fd and base 0e06fc1, reviewed all four changed files, and traced the relevant function-resolution, Nereids translation, scan residual/limit, Doris LIKE, and exact lance-c/DataFusion/Arrow paths.

Two correctness blockers remain: name-only conversion can substitute a Lance built-in for a resolved same-named Doris UDF, and embedded-NUL wildcard LIKE patterns can be pushed despite a proven Doris-versus-Arrow semantic mismatch. Both pushed conjuncts are removed from Doris residual evaluation, so either case can return wrong rows and may also make limit pushdown eligible.

Critical checkpoints: ordinary LIKE/NOT LIKE/ESCAPE behavior, nullable-boolean three-valued logic, recursive boolean forms, LargeUtf8 variation, and the exact native consumer stack were checked with no additional defect found. The added unit/regression coverage exercises the normal paths but does not fence the two boundary cases above. The bundle and live PR had no existing inline or raw review comments, so these are not duplicates. Current lightweight CI, dependency review, style, license, and title checks pass; BE UT on macOS and the automated code-review check are still pending. Per the review contract, I did not run builds or tests locally.

User focus: no additional focus was provided. A fresh Round 2 implementation, tests/integration, and risk-boundary audit all returned NO_NEW_VALUABLE_FINDINGS against this exact two-comment set.

if (function.getFnName() == null || function.getChildren().size() != 2) {
return Optional.empty();
}
String functionName = function.getFnName().getFunction().toLowerCase(Locale.ROOT);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preserve the resolved function identity before dispatching by name. FunctionRegistry intentionally allows a UDF to shadow a built-in via prefer_udf_over_builtin, while a qualified call selects the UDF directly, and Nereids translates Java/Python UDFs to this same FunctionCallExpr with a non-BUILTIN catalog function. A two-argument UDF named starts_with, ends_with, or like can therefore reach this switch, be replaced by Lance's built-in, and be removed from the Doris residual in LanceScanNode; an empty residual can also enable limit pushdown. Please require a non-null resolved Doris BUILTIN before the switch (leaving unknown/UDF identities residual) and cover an analyzed same-named UDF.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f613605. FunctionCallExpr pushdown now requires a resolved Doris BUILTIN function; unresolved functions and same-named UDFs remain as Doris residuals. Added a unit case with a resolved JAVA_UDF named starts_with.

String patternValue = literal.getStringValue();
// Doris uses backslash as LIKE's default escape character, while the Substrait function
// has no escape argument. Keep escaped LIKE patterns in Doris rather than changing meaning.
if (rejectEscapedPattern && patternValue.indexOf('\\') >= 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep embedded-NUL LIKE patterns residual. In the default SQL mode Doris decodes backslash-zero before this guard; a wildcard pattern then reaches hs_compile through re_pattern.c_str(), so the regex is truncated at NUL (for example, a_ becomes ^a and matches abc). The exact lance-c 0.1.6 / DataFusion 53.1.0 / Arrow 58.3.0 consumer preserves the full NUL-bearing pattern, so pushdown filters abc out and the removed Doris residual cannot restore it. Please reject NUL here and add an analyzed end-to-end residual/result case.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f613605. String predicates containing an embedded NUL now remain residual. Added unit coverage for both LikePredicate and FunctionCallExpr forms, plus an end-to-end regression query that checks the residual plan and Doris result.

@zhangstar333

Copy link
Copy Markdown
Contributor

https://github.com/apache/doris-website/blob/master/versioned_docs/version-4.x/lakehouse/catalogs/lance-catalog.mdx
the user doc also need update later about Predicate Pushdown

@Jay-ju

Jay-ju commented Aug 29, 2026

Copy link
Copy Markdown
Author

/review

@Jay-ju

Jay-ju commented Aug 29, 2026

Copy link
Copy Markdown
Author

Documentation update opened: apache/doris-website#4095. It covers direct Boolean predicates, LIKE/NOT LIKE, starts_with/ends_with, pushdown requirements, and the residual cases for escaped/NUL patterns and same-named UDFs in both English and Chinese 4.x docs.

@zhangstar333

Copy link
Copy Markdown
Contributor

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 80.00% (32/40) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 6.91% (25/362) 🎉
Increment coverage report
Complete coverage report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants