Skip to content

[fix](scan) Backport FileScannerV2 predicate ownership fixes - #66356

Closed
Gabriel39 wants to merge 10 commits into
apache:branch-4.1from
Gabriel39:fix/file-scanner-v2-predicate-localization-4.1
Closed

[fix](scan) Backport FileScannerV2 predicate ownership fixes#66356
Gabriel39 wants to merge 10 commits into
apache:branch-4.1from
Gabriel39:fix/file-scanner-v2-predicate-localization-4.1

Conversation

@Gabriel39

@Gabriel39 Gabriel39 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: N/A

Backport the selected FileScannerV2 predicate ownership and localization fixes from #66261 to branch-4.1.

The selected commits require the earlier predicate-refresh and residual-ownership infrastructure from the same PR. This PR therefore includes the complete dependency prefix through the four selected commits, preserving their original order and cleanup pairing.

The changes keep residual predicate ownership ordered across splits, localize each safe predicate independently, recompute localization for every split, and avoid retaining predicate-column payloads after exact file-level filtering when those values are no longer needed.

Release note

None

Check List (For Author)

  • Test:
    • BE targeted suites: 255 tests passed across FileScannerV2, scanner late runtime filters, TableReader, ColumnMapper, Parquet, JNI, Hudi, Paimon, Iceberg position deletes, and adaptive block-size prediction.
    • Final follow-up verification: 105 TableReaderTest.* tests passed.
    • FE FileQueryScanNodeTest: 7 tests passed.
  • Behavior changed: Yes. Predicate localization remains split-local, safe predicates are localized independently, and predicate-only column values can be discarded after exact file-level filtering.
  • Does this need documentation: No.

Keep identity selections implicit, refresh late predicates at row-group boundaries, and reuse dictionary predicate bitmaps within a query.
### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#65998

Problem Summary: Cost sorting could move a late runtime filter ahead of an older unsafe predicate when FileScannerV2 rebuilt partition-pruning predicates for the next split. TableReader also prepared and opened Scanner-owned predicate suffixes even though it retained them only for pruning analysis. Preserve append identity separately from the cost-sorted Scanner snapshot, and clone Scanner-owned TableReader predicates without creating duplicate execution state.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - FileScannerV2Test.*, TableReaderTest.*, and ScannerLateArrivalRfTest.*
- Behavior changed: Yes. Late runtime filters retain predicate ordering barriers, and Scanner-owned predicates have a single execution-state owner.
- Does this need documentation: No
Pass post-filter slot liveness from FE while keeping exact predicate localization a split-local BE decision. Preserve residual and unsafe predicate ordering so payloads are discarded only when all referencing predicates execute in the file reader.
@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?

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Codex completed, but no new pull request review was submitted for the current head SHA.
Workflow run: https://github.com/apache/doris/actions/runs/30731410633

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: Review step was failure (possibly timeout or cancelled)
Workflow run: https://github.com/apache/doris/actions/runs/30732025539

Please inspect the workflow logs and rerun the review after the underlying issue is resolved.

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 87.44% (557/637) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.53% (30111/40949)
Line Coverage 57.45% (329194/573056)
Region Coverage 54.36% (274436/504820)
Branch Coverage 55.33% (122287/221017)

Restore independent predicate localization and add coverage showing that a predicate localized for one split returns to TableReader when the next split cannot localize it. Residual predicates on unrelated physical columns do not prevent predicate-only payload discard.
@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@hello-stephen

Copy link
Copy Markdown
Contributor

Cloud UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 77.59% (1901/2450)
Line Coverage 64.41% (33976/52748)
Region Coverage 64.92% (17519/26985)
Branch Coverage 54.10% (9391/17360)

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

Static review found three correctness regressions and two cooperative-progress gaps in the predicate ownership transfer:

  • residual contexts lose thread-local function state;
  • predicate-only dictionary materialization can corrupt later residual/delete predicates;
  • removing the unsafe-prefix barrier can suppress ordered errors;
  • all-reject late filters can bypass Scanner progress in JNI and refreshed Parquet paths.

No additional review focus was provided, so I reviewed the full authoritative 44-file diff. I did not run builds or tests under the static-review constraint.

}
return status;
size_t FileScannerV2::_last_block_rows_read(const Block& block) const {
const auto& stats = _table_reader->last_materialized_block_stats();

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.

[P2] Account for JNI batches before falling back to survivor rows. JniTableReader::get_block() applies the now-owned (including late) conjuncts and loops again whenever a Java batch is fully rejected, but it never records MaterializedBlockStats. Since Scanner filtering is now a no-op, a late selective RF can drain the rest of a large JNI split in one scheduler turn, while these hooks see only zero/survivor rows and cannot enforce the row/byte budget or learn the pre-filter width. Please record the pre-filter JNI rows/bytes and yield after one rejected materialized batch, as the base TableReader path now does.

// `filter_key` while evaluating the predicate, but not afterwards. Discard its payload
// only when every predicate referencing the physical column was localized exactly for this
// split; schema evolution or a TableReader residual must retain the value.
if (referenced_by_filter && referenced_only_by_localized_filters &&

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.

[P1] Keep predicate-only values needed by later consumers. This mapper decision only proves that query filters localized into FileReader; it does not prove Parquet can discard the value while staging them. On dictionary encoding, the direct path checks only same-column dictionary residuals, emits a default placeholder, and marks the position materialized even when residual_predicate_positions says a multi-column residual or Iceberg equality-delete conjunct still needs the real value. Those later predicates then evaluate defaults and can return wrong rows. Please gate dictionary predicate-only mode on the residual/delete position set (as the fixed/runtime direct path does) and cover both overlaps.

Status TableReader::_prepare_conjunct(const VExprContextSPtr& source, VExprContextSPtr* prepared) {
DORIS_CHECK(prepared != nullptr);
VExprContextSPtr conjunct;
RETURN_IF_ERROR(_clone_conjunct(source, &conjunct));

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.

[P1] Open rebuilt predicate contexts with per-scanner function state. _clone_conjunct() creates a fresh VExprContext, so this open() uses FRAGMENT_LOCAL; however residual functions such as LIKE/REGEXP initialize their LikeState only for THREAD_LOCAL and then dereference it during execution (Java/Python UDFs have the same contract). Because LIKE is unsafe to localize, WHERE s LIKE 'x%' now reaches TableReader's residual filter after Scanner relinquishes the predicate and fails with missing/null function state. Please preserve the original fragment state and clone/open the rewritten context with thread-local state, and add a residual LIKE regression.

// Unsafe or non-deterministic predicates remain at TableReader. Safe predicates are
// independent pruning candidates even when an earlier predicate cannot be pre-executed.
if (!is_safe_to_pre_execute(conjunct)) {
continue;

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.

[P1] Preserve the unsafe conjunct as an ordering barrier. With a partition value p = BIGINT_MIN and ordered predicates mod(p, -1) > 0 AND p > 0, the first predicate is unsafe because it must raise INVALID_ARGUMENT, while the second is safely false. Continuing here lets the second predicate prune the split before the modulo executes, suppressing that required error; V1 and the pre-change V2 path stop at the first unsafe conjunct for exactly this reason. Please restore the safe-prefix barrier for partition/constant pruning and later localization, and cover this error-preserving order.

};
while (true) {
if (!_has_current_row_group) {
activate_pending_scan_request_at_row_group_boundary();

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.

[P2] Bound all-reject work after activating the refreshed request. If a late RF rejects the remaining row groups, read_next_batch() keeps growing and retrying zero-survivor probes until a survivor or EOF; Parquet publishes its raw-row delta only after that call returns, so Scanner cannot charge its row budget or reach cancellation checkpoints during the drain. This PR newly moves active-split late RFs into that loop. Please return a bounded empty progress unit (with consumed raw rows/bytes) and add an all-reject late-RF regression.

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 80.00% (8/10) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 83.08% (540/650) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 58.06% (24346/41929)
Line Coverage 42.15% (242881/576186)
Region Coverage 38.24% (191949/501902)
Branch Coverage 39.53% (87306/220884)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 88.05% (560/636) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.47% (30086/40949)
Line Coverage 57.31% (328386/573045)
Region Coverage 54.28% (274016/504805)
Branch Coverage 55.16% (121911/221011)

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 100.00% (10/10) 🎉
Increment coverage report
Complete coverage report

@Gabriel39 Gabriel39 closed this Aug 3, 2026
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.

2 participants