Repository navigation
[improvement](be) Prioritize Parquet V2 row groups for TopN scans - #69646
Conversation
### What problem does this PR solve? Problem Summary: Physical-order Parquet V2 scans can decode many row groups before finding a useful small-TopN bound. Add TopN ZoneMap evaluation, schedule promising footer ranges first, and recheck the current in-place bound before opening each row group. Preserve physical identities and evaluation-sensitive predicate/project fences. A reader microbenchmark with 128 disjoint row groups and the best group last reduces raw rows from 524,288 to 4,096 and local read bytes from 2,149,888 to 65,602. ASC/DESC median reader-plus-TopK latency improves from 10.698/8.590 ms to 0.669/0.660 ms on warm local files. ### Release note Improve eligible Parquet V2 ORDER BY LIMIT scans by prioritizing row groups and applying the latest TopN bound before additional I/O. ### Check List (For Author) - Test: 370 targeted BE tests; 8 TopNRuntimeFilterTest FE tests; paired reader microbenchmark; FE Checkstyle; clang-format 16; BE build hygiene and clang-tidy. - Behavior changed: Yes. Eligible row groups may execute in TopN order. - Does this need documentation: No. Internal scan optimization.
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
run buildall |
|
/review |
There was a problem hiding this comment.
Static review of PR #69646 at b0466c1. Two P2 findings remain: the new project fence misses deterministic throwing date arithmetic, and the reordering safety check cannot see evaluation-sensitive predicates retained on joins. Both can make the new promising-group order suppress an error encountered before any TopN bound in the original physical order. The inline comments describe separate reachable operator boundaries and fixes.
No existing inline findings were present, so there are no existing blocking comment IDs to carry. User focus: no additional focus was supplied; all 11 changed files and their relevant upstream/downstream paths were reviewed.
Critical checkpoint conclusions:
- Goal and proof: earlier TopN feedback, row-group prioritization and late footer pruning are implemented. Focused BE/FE tests support the scalar cases, but the two error-preservation defects prevent full semantic equivalence.
- Scope and clarity: the changes are focused and reuse existing mapping, statistics conversion and profiling contracts.
- Concurrency and locks: scanner tasks read a copied Field under RuntimePredicate's shared mutex while sort tasks update under its exclusive mutex. Published bounds tighten monotonically; direction and NULL ordering are immutable. No new thread, nested lock order, heavy locked I/O or deadlock path was found.
- Lifecycle and static initialization: execution order is reset with the scan plan; group readers, pending outputs and merged ranges are released at existing boundaries. QueryContext owns the runtime predicate. No new static initializer or ownership cycle was found.
- Configuration: no production configuration or session variable was added.
- Compatibility: no wire or persisted format changes. External statistics still use the existing sanitized logical-type conversion and conservative fallbacks; no new ABI/toolchain dependency was identified by inspection.
- Thrift/protobuf: neither directory changed; cross-branch field-ID comparison is inapplicable.
- Parallel paths: checked the new ZoneMap capability's native storage, legacy Parquet and V2 footer/page consumers; ORC SARG does not consume this interface. Row-ID fetches retain physical order. Join-expression safety is incomplete as reported inline.
- Conditional checks: NULL/no-bound, equality, incompatible types, unusable ranges and unknown NaN information retain candidates conservatively. The upstream expression-fence coverage is incomplete in the two reported cases.
- Test coverage: new tests cover ASC/DESC, late bounds, exact integer TopK values, localized slots, physical GLOBAL_ROWID, missing statistics, row-ID fetch and predicate fences, plus synthetic ZoneMap NULL/tie cases. End-to-end error cases for throwing projections and join predicates are missing.
- Expected test results: integer outputs, physical row-ID formulas and NULL/equality expectations are consistent with the fixtures. No golden result files changed; no generated-output provenance claim is needed.
- Observability: late exclusions feed the existing deferred-pruning/min-max/read-row counters before expensive probes. No new per-row logging was introduced.
- Transactions and persistence: no EditLog, commit, visible-version, delete-bitmap publication or failover protocol changed.
- Writes and crash atomicity: this is read-side scheduling/pruning; no write or transaction protocol changed. Existing error/cancellation cleanup remains in the reader ownership chain.
- FE/BE propagation: no new transmitted variable; the internal request flag is assigned on both initial open and refresh. Direction and NULL placement reuse the existing TopN descriptor.
- Performance: one O(G) priority buffer and O(G log G) stable sort per file, then footer checks before dictionary/index probes and prefetch. No additional per-row allocation was identified. Benchmark code and work counters were inspected; reported speedups were not rerun or independently established.
- Other core invariants: physical plan IDs and file-global row offsets stay unchanged under the execution permutation. Position-delete searches restart per block; equality deletes are order-independent. Production Condition Cache is disabled when TopN source IDs exist. Late runtime-filter activation retains the physical layout and replans unvisited groups. No additional verified memory, nullable-column, unchecked-status or result-pruning defect was found.
Static-only limits: no builds, tests, sanitizers or benchmarks were run. New physical-file fixtures mainly cover Arrow-written INT32; broader writer/type/NULL/delete/cache/late-filter differential combinations are not newly demonstrated by tests. These are validation limits, with no unresolved candidate or assigned static-review coverage gap remaining.
The review converged after 2 rounds: every latest-round reviewer returned NO_NEW_VALUABLE_FINDINGS on the unchanged final payload, all assigned coverage and risk questions were closed, and the final sweep retained exactly the two verified inline findings.
Review converged after 2 round(s).
TPC-H: Total hot run time: 28485 ms |
TPC-DS: Total hot run time: 150917 ms |
ClickBench: Total hot run time: 24.25 s |
FE UT Coverage ReportIncrement line coverage |
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
FE Regression Coverage ReportIncrement line coverage |
### What problem does this PR solve? Problem Summary: TopN row-group reordering and runtime pruning can hide an overflow in a date projection or an assertion retained on a join. Fence filter pushdown across these expressions before creating a scan target, including both hash and nested-loop joins and all join conjuncts. Reuse the existing throwing-date classification while retaining safe literal-zero arithmetic and ordinary join optimizations. ### Release note Preserve date arithmetic errors and join assertion evaluation when pushing down TopN runtime filters. ### Check List (For Author) - Test: 33 FE tests across four suites passed. The new project/hash-join/ nested-loop-join cases failed on the original code. FE Checkstyle passed. Added a SQL error/explain regression; Groovy compilation passed, full cluster execution pending CI. - Behavior changed: Yes. Withhold unsafe TopN filters at evaluation-sensitive projections and joins. - Does this need documentation: No. Internal optimizer correctness fix.
|
run buildall |
|
/review |
There was a problem hiding this comment.
Reviewed exact head 495b081d65f1137a9d3567383ad3aecd86b49d28 against the supplied aggregate diff. Two new P2 findings remain: unchecked residual-filter/generator traversal and throwing epoch conversions can still let Parquet row-group prioritization suppress an error. Both are detailed inline.
Existing review context: independently confirmed the reported weeks_add projection and ON-assertion hash/nested-loop join instances are fixed. Recursive date classification includes nonzero add/subtract and rounding; the join expression list includes hash, other, and mark conjuncts. Neither existing P2 report is reposted. No existing P0/P1 finding remains to carry forward. No additional user focus was supplied.
Critical checkpoint conclusions:
- Goal and proof: best-first Parquet row groups and current-bound footer checks are implemented, with focused reader tests, but error preservation is incomplete as described inline.
- Scope and reuse: changes are focused; footer sanitization, statistics conversion, column mapping, and the existing date classifier are reused. The classifier extraction preserves its previous behavior.
- Concurrency: sort-sink updates take the runtime predicate's exclusive lock; each new ZoneMap evaluation copies one bound under its shared lock. Direction/null flags are immutable. Reader scheduling and request activation remain sequential; no new nested-lock ordering issue found.
- Lifecycle and memory: execution indices remain separate from physical plans; request snapshots pin expression state until a row-group boundary. Reset clears ordering state and readers flush profiles before destruction. No new ownership cycle or cross-translation-unit static initialization found; additional ordering storage is O(row groups).
- Configuration: no new configuration or dynamic-reload requirement. FE test session overrides are restored.
- Compatibility: no storage format or wire-protocol change; inspected shared ZoneMap consumers beyond Parquet V2 and found no separate type/NULL compatibility regression. Release ELF/ABI validation was not run.
- Thrift/protobuf: no definitions changed; field-ID upgrade checks are inapplicable.
- Parallel paths: hash/nested-loop ON fences and native/v1 ZoneMap consumers were checked. Residual filters, generators, and epoch conversions are the remaining verified gaps; CTE/window/nested-TopN boundaries stay intact. ORC ordering is unchanged.
- Conditions and errors: new comments describe concrete safety cases; equality ties, missing statistics, NULL ordering, NaN/unsafe ranges, and row-ID fetch fallbacks are conservative. The inline findings identify missing error-preservation boundaries.
- Tests: added BE tests cover both directions, feedback, localized slots, physical row IDs, missing statistics, fences, NULLs and ties; FE tests cover reported project/ON failures and safe controls. The new SQL suite uses OLAP tables and does not exercise an actual reversed-Parquet-group integration path. The newly identified cases are absent from these tests.
- Expected results: added expected vectors and planner/error assertions are consistent with their tested shapes. No generated regression result file changed.
- Observability: deferred pruning continues updating existing min/max group/row/byte counters, and EXPLAIN exposes TopN target creation. No separate logging regression found.
- Transactions and persistence: no transaction, EditLog, failover, visible-version, or delete-bitmap-version logic changed.
- Writes and atomicity: production changes affect reads/planning; no write atomicity or crash-recovery change.
- FE-BE values: the new row-reordering flag is an internal FileScanRequest field, set on initial and refreshed requests; no new transport field or send path.
- Performance: one footer priority pass and stable O(G log G) sort are added per eligible reader; late footer exclusion precedes dictionary/index probes and prefetch. Benchmark claims were inspected, not reproduced.
- Additional checks: physical row/delete coordinates and Condition Cache remain tied to file positions; production TopN scans disable Condition Cache. No additional actionable BE defect found.
Validation limits: static source and test inspection only. No builds, tests, benchmarks, cluster queries, source modifications, or submodule initialization were performed. Combined external-writer/deletes/page-index/live-refresh scenarios remain execution-validation gaps, rather than unresolved static candidates. All 13 changed files and assigned risks were covered; the review converged in two rounds.
Review converged after 2 round(s).
TPC-H: Total hot run time: 28354 ms |
TPC-DS: Total hot run time: 150552 ms |
### What problem does this PR solve? Problem Summary: Default descent and a blacklist of throwing functions miss residual filters, generators, and epoch conversions. Row-group reordering can then suppress errors by pruning their original input. Require explicit operator traversal and a closed set of total expressions, preserving safe slot queries while fencing unknown operations and cardinality assertions. ### Release note Preserve errors when TopN runtime filters reduce input to sensitive operations. ### Check List (For Author) - Test: 38 related FE unit tests passed; five new test methods failed first on the previous revision. FE Checkstyle and Groovy compilation passed. Full SQL regression execution is pending CI. - Behavior changed: Yes; unclassified operations block TopN filter pushdown. - Does this need documentation: No.
|
run buildall |
|
/review |
ClickBench: Total hot run time: 24.22 s |
There was a problem hiding this comment.
One new P2 finding: the table-layer reorder proof omits implicit file-to-table materialization, allowing a strict schema-evolution overflow to be suppressed. The four existing P2 threads (date arithmetic, ON predicates, retained WHERE/generators, and epoch conversions) are fixed at this head; no duplicate comments are posted and no existing P0/P1 remains to carry forward. No additional user focus was supplied.
Critical checkpoint conclusions:
- Goal/data correctness/error semantics: best-first Parquet reads and late-bound pruning are implemented, but implicit projection errors remain exposed by the inline finding.
- Scope/reuse: changes are focused on TopN ordering and its safety gates; the FE closed expression/operator set deliberately reduces eligibility. Existing date classification and statistics sanitization are reused.
- Concurrency/locks: sink feedback and scanner reads use locked monotonic bound snapshots; no new threads, nested locks, or heavy work under a new lock.
- Lifecycle/memory/static initialization: order state resets per plan, pending requests activate after reader teardown, physical coordinates remain attached to plans, and scratch is file/reader scoped. No new ownership cycle or cross-TU initializer identified.
- Configuration: no new configuration or dynamic-update contract.
- Compatibility: no stored-format or wire change, new external artifact dependency, or new ABI requirement identified by source inspection; binary compatibility was not executed.
- Thrift/protobuf IDs: unchanged; branch-ID comparison is not applicable.
- Parallel paths: traced footer/page and legacy/OLAP ZoneMap consumers, ORC boundaries, request refresh, row-ID fetches, absolute row positions/deletes, and TopN condition-cache exclusion. No additional verified defect.
- Conditional branches: eligibility/fallback comments explain NULL, ties, unsafe predicates, row IDs, missing statistics, and late feedback.
- Coverage: BE tests cover ASC/DESC feedback, exact values, localized slots, physical row IDs, fences, NULL placement and ties; FE tests cover the four previous findings and safe controls. Implicit evolution is missing from the new reorder tests.
- Expected results: no output snapshots changed; the SQL suite uses error/EXPLAIN checks. Assertions and expected values were inspected statically.
- Observability: deferred pruning uses existing counters/profile publication; no new per-row logging.
- Transactions/persistence/failover: unaffected by this read-path change.
- Writes/atomicity/crash recovery: no data-write path changed.
- FE/BE variables: allow_row_reordering is a BE-local request field, set on initial construction and refresh; no new transmitted variable.
- Performance: one per-file priority sort plus boundary footer checks can avoid decoding/I/O. The microbenchmark is opt-in; author timing claims were not independently reproduced.
- Other concerns: no further substantiated finding after complete changed-path and downstream review.
Validation limits: static source review only; no builds, tests, lint, benchmarks, cluster runs, or ELF audits were executed. The SQL regression uses OLAP tables and does not exercise external Parquet permutation; the new implicit-evolution case needs a multi-row-group regression. There are no unresolved review candidates or uncovered changed files. Review converged in 3 rounds.
Review converged after 3 round(s).
### What problem does this PR solve? Problem Summary: FE and scan predicates cannot see file-to-table evolution. Reordering can visit valid low-key groups first and skip an earlier group whose unrelated DATE-to-TIMESTAMP_NS output would overflow. Require every projected mapping to be direct, including nested materialization, after request customization and reconciliation. Apply one shared proof to initial and refreshed requests while retaining a hook's false flag. ### Release note Preserve implicit conversion errors during Parquet V2 TopN scans. ### Check List (For Author) - Test: 483 related BE tests passed; the two new regression methods failed first on the reviewed head. Real Iceberg/Parquet coverage includes both sort directions, top-level/nested outputs, strict/non-strict conversions, identity positive controls and request refresh. clang-format 16, build-hygiene and changed-line clang-tidy checks passed. - Behavior changed: Yes; nontrivial output mappings retain physical order. - Does this need documentation: No.
TPC-H: Total hot run time: 28313 ms |
### What problem does this PR solve? Related PR: apache#69646 Problem Summary: Retained V1 Parquet requests can expose STRING min/max Fields under a VARBINARY context. Newly enabling generic TopN ZoneMap evaluation reaches the shared typed-range assertion after a late bound arrives, failing valid queries during a BE-first rolling upgrade. Validate the bound and both statistic carriers before that helper, using the existing unsupported fallback for incompatible metadata. Correctly typed VARBINARY ranges retain pruning and equality semantics. ### Release note Prevent legacy VARBINARY TopN scans from failing on incompatible statistic carriers during rolling upgrades. ### Check List (For Author) - Test: 498 BE tests passed; one opt-in benchmark skipped. The regression first failed on the previous head and uses the actual V1 statistics decoder with real late-bound RuntimePredicate feedback, ASC/DESC, mixed carriers, typed positive controls, binary bytes and equality. clang-format 16, build hygiene and changed-line clang-tidy verified. - Behavior changed: Yes. Incompatible statistics retain candidate rows. - Does this need documentation: No new configuration or public API.
|
run buildall |
|
/review |
TPC-DS: Total hot run time: 151213 ms |
There was a problem hiding this comment.
Reviewed #69646 at ba5680f using the supplied aggregate diff and all nine changed paths. No new actionable defect was verified. No independently confirmed existing P0/P1 inline findings remain to carry forward.
Existing review context: the legacy VARBINARY statistics issue (4385250260) is fixed by checking the bound and both min/max Field carriers before the asserting typed-range helper; the new regression exercises the retained V1 decoder and late publication. The other existing threads concern evaluation of expressions or conversions in excluded rows. The current implementation and explicit PR contract permit omitting that work when a valid TopN bound excludes the rows; executed failures still propagate. These known concerns are not reposted. There was no additional user-provided review focus.
Critical checkpoint conclusions:
- Goal and evidence: the implementation can visit promising Parquet V2 row groups first and recheck the current TopN frontier before dictionary/index probes and I/O. Added reader tests model real per-block runtime-predicate feedback and check exact TopK values and pruning counters; their execution was not verified in this review.
- Scope and clarity: production changes are limited to the shared TopN ZoneMap capability and Parquet scheduler; FE production behavior is unchanged apart from a comment. Tests and the optional microbenchmark support that scope.
- Concurrency: the sort sink publishes bounds through RuntimePredicate::update under its existing shared mutex; each new ZoneMap evaluation copies one bound under the matching read lock and compares outside it. Direction and NULL ordering are immutable. Scanner-owned scheduling state introduces no new shared mutation or lock ordering.
- Lifecycle: the execution permutation stores indices rather than predicate/schema pointers. Scheduler reset clears ordering and recheck state; request activation retains current-row-group ownership until its readers are released. No cross-translation-unit static initializer or new ownership cycle is introduced.
- Configuration: no new production configuration is added. The benchmark environment switch is test-only.
- Compatibility: no persisted format, wire field, or external function symbol contract is changed. Shared capability consumers retain conservative fallback for incompatible types/carriers and unsuitable ranges; retained V1 VARBINARY statistics are explicitly covered.
- Thrift/protobuf: no files under gensrc/thrift or gensrc/proto change.
- Parallel paths: inspected shared expression consumers and V1/V2 boundaries. Row-group prioritization belongs to the V2 Parquet scheduler; existing mapper localization, ORC paths, and FE filter eligibility are preserved.
- Conditional boundaries: ordering uses the existing metadata-pruning prefix, direct compatible scalar slot, available statistics, and ordered row-ID fetch restriction. Equal leading keys remain candidates for secondary sort keys; NULL ordering and unavailable/unsafe statistics remain conservative.
- Test coverage: inspected ASC/DESC, late-bound publication, secondary-key equality, NULL placement, localized slot identity, absent statistics, pruning fences, row-ID fetch order, native DATE conversion, projected evolution, hidden Iceberg equality-delete conversions, and legacy binary carriers. FE tests cover secondary expressions and translated grouped-key aggregation plans. These are source-inspected tests, not executed validation.
- Expected results: added expected TopK values, physical row IDs, row counts, converted dates, error controls, and pruning counters agree with the fixture construction. No regression .out file is changed.
- Observability: late footer pruning updates existing deferred-pruning profiles before continuing; existing raw-row, min/max-pruning and I/O counters expose avoided work. No new per-row logging is introduced.
- Transactions and persistence: no transaction, EditLog, snapshot-selection, visible-version, or delete-bitmap publication logic is modified.
- Writes and crash atomicity: production changes are read-side scheduling/pruning only. No new durable write or crash-atomicity protocol is introduced.
- FE/BE variable passing: no new transmitted variables; existing TopN descriptors and mapper-owned file-local identities are reused.
- Performance and memory: priorities are computed once from sanitized footer statistics, sorted in O(G log G), and retain O(G) execution indices for G candidate row groups. Footer rechecks precede expensive probes and decode. The included benchmark isolates a favorable one-column reader workload; no full-SQL, remote-storage or broad workload speedup was independently established.
- Other integration checks: physical plans remain in file order while only their execution indices change. Per-group readers use first_file_row for row positions; position-delete evaluation restarts lookup for each batch and equality deletes use key lookup. TopN scans disable production Condition Cache, and row-ID fetches retain physical order. Refresh preserves fixed local positions and reevaluates remaining groups against the active request.
Validation limits: static source review only, as required by the review contract. No build, unit/regression test, benchmark, running cluster, sanitizer, or rolling-upgrade execution was performed. Author-reported test counts and timings were not independently reproduced. Full SQL feedback scheduling and external-writer/remote-storage performance remain empirical validation limits; no unresolved static candidate remains in the reviewed scope.
Review converged after 1 round(s).
Revert ba5680f. Keep legacy FE/BE mixed-version Parquet V1 compatibility outside the scope of the Parquet V2 row-group scheduling optimization. Restore the preceding evaluator and remove its dedicated compatibility regression.
Verify complete translated plans for STRUCT order keys with sibling IS NOT NULL predicates, including TIMESTAMPTZ, INT and DATE in both sort directions. Check the TopN filter and both nested access paths without changing the existing SQL regression assertions.
|
run buildall |
|
/review |
There was a problem hiding this comment.
Static review of PR #69646 at 96e6d38: no new actionable findings.
The existing legacy V1 VARBINARY statistics issue still applies to the older-FE request path. It is already reported as P2, and the author explicitly reverted its compatibility fix at the maintainer's request. It is retained as a known limitation, without a duplicate inline. All supplied original inline findings are P2; no existing P0/P1 finding qualifies for existing_blocking_comment_ids. The other existing threads concern exceptions in excluded rows; the documented contract permits avoiding their evaluation, and no distinct wrong-result case was established.
Critical checkpoint conclusions:
- Goal and correctness: The implementation prioritizes promising Parquet V2 groups and rechecks the current bound before expensive probes. Inspected tests check exact ASC/DESC TopK values, physical row identities, row counts and avoided groups using actual RuntimePredicate feedback. This is source evidence, not an executed validation result.
- Scope and reuse: Production changes are confined to the shared TopN ZoneMap interface and Parquet scheduling; the FE production delta is explanatory. Existing statistics sanitation, conversion and metadata-pruning machinery are reused.
- Concurrency and locks: SortSink publishes the frontier under RuntimePredicate's unique lock; the evaluator copies an owning Field under its shared lock, then evaluates outside the lock. Direction and NULL placement are immutable. Scheduling state is scanner-local. No new nested locking, thread or lock-order edge was found.
- Lifecycle and static initialization: Temporary predicate/schema observers do not escape preparation; retained order state contains indices. Reset clears it with the scan plans. Request refresh waits for a row-group boundary after current readers and pending materialization finish. No new ownership cycle or static initializer is introduced.
- Configuration: No configuration item or dynamic-update requirement is added.
- Compatibility: No wire or storage format changes. The shared capability also reaches retained V1 and native-storage ZoneMap consumers; their types and fallbacks were traced. The known legacy V1 VARBINARY compatibility exception above remains unfixed.
- Thrift/Protobuf: No gensrc definition or field/enum ID changes.
- Parallel paths: Raw/dictionary TopN filtering, V2 footer/page pruning, retained V1 consumers, native storage, aggregate bypass and ORC's separate SARG path were considered. Ordered row-ID fetches retain physical order. Existing FE grouped-key, join, set, window and CTE behavior is unchanged.
- Conditions and semantics: Direct slot/type/local-position checks, the metadata-safe prefix, missing/unusable statistics, NULL placement and strict leading-key inequality retain candidates conservatively. Ties may still win on secondary keys. Position deletes and deletion vectors use absolute row positions and fresh per-batch lookup; immutable physical plans preserve their coordinates. Production TopN scans disable Condition Cache admission through the existing scanner digest guard.
- Test coverage: New GTest cases cover late bounds, ASC/DESC, NULL/equality boundaries, localized slots, absent statistics, pruning fences, ordered fetches and physical IDs. Iceberg cases cover top-level/nested evolution, strict-mode variants, native DATE materialization and hidden equality-delete conversions, with required-row error controls. FE tests cover eligibility plus translated aggregation/nested-field EXPLAIN. Full SQL feedback, matching-delete combinations, remote/multi-scanner execution and the broad external-writer matrix are not newly demonstrated by these tests.
- Expected results: No generated regression .out files change. Inspected unit expectations match retained ties, physical coordinates and conversion behavior; no handwritten result artifact was introduced.
- Observability: Existing deferred-pruning counters, projected-byte accounting, reader timers and profile flush paths remain in use. No per-row logging or timing overhead is added.
- Transactions and persistence: Not applicable to this read-only optimization; no EditLog, snapshot-selection, publish or failover state changes.
- Data writes and crash atomicity: No write path changes. Existing RAII and error/close paths retain reader ownership and cleanup.
- FE/BE propagation: No transmitted variable is added. Key binding follows mapper block position to local column ID to physical leaf; the localized-slot test checks a nonidentity mapping.
- Performance and memory: Priority construction uses O(G) temporary state and an O(G log G) stable sort once per file, without dictionary/page I/O. The O(G) index vector resets with scan state. The opt-in benchmark checks heap keys/checksums and work counters, but it is a local reader microbenchmark with modeled feedback; its timings and full SQL gains were not independently verified.
- Other issues and user focus: All eight changed files and affected producer/consumer paths were reviewed. There was no additional user-provided focus, no new substantiated defect, and no unresolved concrete candidate.
Validation was static only: no builds, tests, benchmarks, formatting, build-hygiene or clang-tidy checks were run; repository source and submodules were unchanged. Author-reported results are not review-executed evidence. Full SQL, rolling-upgrade and broad interoperability/performance validation remain outside this review's execution evidence.
Review converged after two rounds: the latest complete-review and separate risk reviewers returned NO_NEW_VALUABLE_FINDINGS on the unchanged payload, and all candidates and initial risk items have an evidenced disposition.
Review converged after 2 round(s).
TPC-H: Total hot run time: 28240 ms |
TPC-DS: Total hot run time: 150251 ms |
ClickBench: Total hot run time: 24.35 s |
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
FE Regression Coverage ReportIncrement line coverage |
Apply the outstanding changes in master merge order: apache#69545 7d16729 apache#69551 7224a2d apache#69612 61d1aef apache#69615 8d2bd1e apache#69650 3dc7341 apache#69651 c00ecb1 apache#69646 e493c61 apache#69668 7865646 apache#69614 already exists on branch-4.2 through apache#69644. Adapt datasource APIs and tests to the release branch, retaining its existing scanner dispatch and statement-bound metadata contracts.
### What problem does this PR solve? Backport the requested external-data and FileScannerV2 changes to `branch-4.2` in their actual master merge order: | Order | Master PR | Change | Backport | | --- | --- | --- | --- | | 1 | #69545 | Preserve Paimon schema options after field renames | Included | | 2 | #69551 | Expand Iceberg capability, transaction and snapshot tests | Included with 4.2 API adaptations | | 3 | #69614 | Elasticsearch HTTP scans in FileScannerV2 | Already present through #69644 (`2104b7509cda`); not applied twice | | 4 | #69612 | Check catalog visibility before SHOW DATABASES accesses metadata | Included | | 5 | #69615 | Publish Iceberg Parquet split offsets | Included | | 6 | #69650 | Preserve ES compound residuals and expand scanner coverage | Included | | 7 | #69651 | Accept Elasticsearch 9.x during node discovery | Included | | 8 | #69646 | Prioritize Parquet V2 row groups using TopN bounds | Included | | 9 | #69668 | Arrow loads and schema inference in FileScannerV2 | Included | The eight outstanding changes are combined in one commit. This branch retains the 4.2 FE datasource APIs instead of importing master's connector-module refactor. ### Backport adaptations - Port Paimon schema restoration, catalog-option provenance, fallback handling and tests to the existing table-copy/serialization lifecycle. Keep statement-bound fields when reader options are applied after a schema-only rename. - Exercise Iceberg's existing metadata and transaction entry points with real in-memory tables and retained-generation fixtures. Preserve the nine disabled known-bug reproductions; omit two connector-only lifecycle tests. Expiration checks verify live data-file sets and exact delete-file counters; 4.2's separate manifest-name/data-counter issue remains unchanged. The delete/history SQL suite checks visible rows and eager/late-materialization parity because 4.2 does not expose master's `_file`/`_pos` columns. The inventory labels upstream-only evidence explicitly. - Port ES residual tracking to the legacy planner and add compound OR/NOT, docvalue eligibility and Thrift-context tests there. Use Java 8-compatible HTTP APIs for opt-in ES 9 tests and the branch's supported node-address fixtures. - Keep the existing full-schema Iceberg metadata-table scan, which already supplies the file-size dependency for split offsets. Adapt the external write suite to verify offsets without requiring master-only distribution plans or golden output. - Use the existing Parquet statistics interface and physical row-location representation. Keep the TopN scheduling and pruning invariants. - Include the shared Arrow array normalizer required by the new reader, taken from the same master revision. Preserve 4.2 Lance/Avro dispatch; omit the schema-inference test for the unavailable Hive OpenCSV parser. ### Validation - FE production and test sources compile with the branch's Java 8 target. Across the targeted runs and focused reruns, 20 test classes report 181 discovered, 171 passed, zero failures/errors, and 10 skipped: nine intentionally disabled known-bug reproductions plus the opt-in live ES 9 class. - FE Checkstyle: passed with zero violations. - Clang compiler syntax checks: all 23 affected C++ translation units passed against the current sources and freshly generated Thrift, using cached third-party/generated build dependencies. Focused BE runtime validation is listed below. - clang-format 16: all 30 affected C++ source/header files passed. - Groovy 4.0.19 syntax and AST conversion: all seven changed suites passed. All 105 local inventory links resolve. - Complete staged-diff self-review, whitespace/conflict checks, and sensitive-information checks passed. A full BE rebuild and SQL/external-service regressions were not run locally. Live ES 9 tests require their opt-in endpoint. CI is requested for the combined backport. ### Release note Fix Paimon reads after field renames, external-catalog SHOW DATABASES authorization, and ES compound residual filtering. Support ES 9.x node discovery, publish Iceberg Parquet split offsets, improve Parquet V2 TopN row-group scheduling, and use FileScannerV2 for Arrow loads and text/JSON schema inference. ### Check List (For Author) - Test - [x] Unit Test - [x] Regression test additions and syntax checks; SQL execution pending CI - Behavior changed: - [x] Yes. Backport the changes listed above. - Does this need documentation? - [x] No. Existing interfaces and settings are retained; the test coverage inventory is included. ### Follow-up fixes - Set the Thrift `enable_file_scanner_v2` default to `true`, matching the session default. Preserve explicit `false` for legacy query scans and extend the existing load-selection test. - Keep the selected Paimon snapshot and schema together during relation projection. Historical sequence/aggregation options must not be combined with the latest field names. Read-only principals can resolve historical selectors without INSERT permission. - Read legacy append schemas with missing or empty bucket keys without bucket pruning, preserving every historical file. The regression covers both unaware and multiple fixed buckets. Follow-up validation: 169 targeted Paimon FE tests passed with zero failures/errors/skips. FE production compilation and Checkstyle passed. Both changed C++ test translation units passed compiler syntax checks and clang-format 16. Regenerated Java/C++ defaults are enabled; a Java 8-targeted protocol check verifies constructor/clear defaults, an omitted wire field, and an explicit-false round trip. Complete BE CI and live external SQL suites remain pending. The two new reproductions first failed with the same missing-bucket-key and renamed-sequence-field errors as CI. A separate read-only-principal reproduction also failed before the permission correction. Complete external regression execution remains pending CI. ### BE unit-test compatibility - Complete the native Arrow Variant input dependency for `branch-4.2`: accept binary storage and the Parquet Variant extension, validate metadata and payloads before appending, and retain owned data after Arrow buffers are released. Allow that ingestion path through the load reader while preserving query-format restrictions and source-default validation. - Preserve Variant V2 markers in the Arrow test fixtures, including nested types. Verify timestamp input against this branch's SerDe for positive, negative, and second-boundary values. Match the existing DATE range diagnostic in the TopN pruning test. - Add tests for sliced and empty reads, invalid ranges and carriers, malformed metadata/payloads, NULL children, atomic failure, buffer ownership, and the query capability boundary. Validation: all 13 previously failing BE tests passed. The expanded run passed all 309 tests across nine suites, including Arrow/JSON loads, load validation, Iceberg, table mapping, and Variant input/output. The focused executable rebuilds the affected readers and SerDe against cached 4.2 build dependencies; this is not a complete BE rebuild. All seven changed C++ translation units passed compiler checks with current headers/generated Thrift, and all 33 affected C/C++ files passed clang-format 16. The complete staged diff, whitespace, and sensitive-information checks passed. Full CI is requested with `run buildall` and `/review`.
### What problem does this PR solve? Issue Number: None Related PR: #65730 #66608 #66805 #66841 #66999 #67005 #67011 #67110 #67268 #67289 #67325 #67650 #67921 #68028 #68335 #68375 #68615 #68690 #68813 #68815 #68707 #68649 Problem Summary: **In short.** This ports the Lance BE read path of branch-4.1 / branch-4.2 to master, in its branch-4.2 form as of `cabcf45c1af`, plus the BE side of three 4.x changes merged after that snapshot: the search filter `prefilter` flag and profile layout (#68813), distance bounds for `vector_search` (#68815), and the split-level index rule of #68707 that #68815 relies on. It gathers the BE parts of the PRs listed above. Master links `liblance_c` but has no Lance reader, so every `FORMAT_LANCE` scan fails today. With this PR the BE reads Lance datasets through FileScannerV2: plain scans, vector search and full-text search, predicate and runtime-filter pushdown, the schema fetch of a local Lance TVF, and the second phase of TopN lazy materialization. The PR is BE only. The FE connector plugin and the regression suites come in separate PRs, so until then nothing on master plans a Lance scan, and the BE unit tests carry the proof. Original authors: @zhangstar333 @Gabriel39 @wangzhaobo957-cloud @Jay-ju @FANNG1 **Background.** Lance shipped in 4.1.4: the Lance catalog, `local()` / `s3()` with `format=lance`, and the `vector_search` / `full_text_search` TVFs. On master the FE side is being rebuilt as a connector plugin (`fe-connector-lance`), and the BE needs the reader from 4.x. The thrift ids are the branch-4.1 / 4.2 ones, including `TExternalSearchRequest.5` (`prefilter`) and `TVectorSearchParams.6/7` (distance bounds) added after the snapshot, so a BE built from master also reads the Lance scan ranges and search requests that a 4.x FE sends during a rolling upgrade. The one exception is `TVectorSearchOptions` 6, branch-4.2's `approx_mode` (#69852, merged into branch-4.2 after the snapshot and not ported): it is reserved in a comment, and a master BE ignores it, so during a rolling upgrade a 4.2 FE that asks for `fast` or `accurate` gets Lance's default (`normal`). **The problem, and what it cost.** | | master today | with this PR | |---|---|---| | A `FORMAT_LANCE` scan | FileScannerV2 has no Lance reader, so the scan fails with "FileScannerV2 does not support file format" | read by `LanceTableReader` in FileScannerV2 | | `fetch_table_schema` for a local Lance TVF | not handled | returns the Arrow schema mapped to Doris types; Null leaves keep `NULL_TYPE` | | The row location of a Lance row in TopN lazy materialization | `GlobalRowLoacationV2` only has a 32-bit row id, but a Lance row id is a 64-bit id within the snapshot | a version 1 layout carries the 64-bit row id; phase two reads the rows with the dataset's `take_rows` | **How this PR fixes it.** - Reader: `be/src/format_v2/lance/*` and `be/src/format_v2/table/lance_reader.*`, as on branch-4.2 except one local renamed for master's `-Wshadow`. The unit tests construct `RuntimeState(TQueryOptions(), globals)`, because master removed the globals-only constructor. - FileScannerV2: the reader dispatch and `_to_file_format` handle `FORMAT_LANCE` / `"lance"`; `FileFormat::LANCE` is added to its two exhaustive switches. Since #69723 every file scan uses FileScannerV2, so nothing else routes Lance scans (on branch-4.2 a Lance query fails with NotSupported when `enable_file_scanner_v2` is off). - Row locations: `ROW_VERSION {FILE_LOCAL_ROW_ID = 0, LANCE_DATASET_ROW_ID = 1}` and a union layout for `GlobalRowLoacationV2`. Version 0 keeps its byte layout, which static asserts pin. `PRequestBlockDesc.row_id` becomes `uint64` (a proto2 varint, so the wire format is compatible). The materialization operator decodes row locations by version, rejects an unknown version or a wrong size, and stamps `row_location_version`. `RowIdStorageReader` validates the counts and the version, range-checks internal row ids against `uint32`, and groups Lance rows by plan node, snapshot version and dataset URI, so each dataset is read with one `take_rows`. Master's segment batching, scan-slot dedup and fetch concurrency stay as they are. - Thrift (`PlanNodes.thrift`): the external search request body (`TVectorElementType`, `TVectorMetric`, `TSearchVector`, `TVectorSearchParams` 1-7, `TFtsCoverageMode`, `TFtsQueryType`, `TFtsMatchOperator`, `TFullTextSearchParams`, `TSearchFilterFormat`, `TSearchFilter`, `TVectorSearchOptions`, `TExternalSearchQuery`), `TExternalSearchRequest` 2-5 and `TLanceFileDesc` 4-6, all with the branch-4.1 / 4.2 ids. The enums spell out their 4.x values. No id is reused. On master, #70075 reserved `TExternalSearchRequest` 2-5 in comments and added 6 (`paimon_options`); this PR replaces those comments with the fields and leaves 6 as it is. `TVectorSearchOptions` 6 is reserved in a comment for branch-4.2's `approx_mode` (#69852). - After the snapshot (one commit each): - #68813: `prefilter` decides whether the TVF search filter runs before (true) or after (unset / false) candidate selection, for vector and full-text search. The profile groups the Lance counters under `LanceTiming`, `LanceIO`, `LanceIndex`, `LanceVectorIndex`, `LanceScalarIndex`, `LanceExecutionPlan`, `LanceScan`, `LanceSearchPlan` and `LanceDataCache`, uses the 4.x counter names, and registers every scan metric before the statistics callback is installed, so the callback, which Lance invokes from its execution streams, never adds counters. #68813's FE tweak that hides `TUnit.NONE` labels in `AggCounter` belongs to the FE port. - #68707 (part): a vector split searches exactly the index segments FE planned for it, and a split without segments is searched flat whatever `use_index` says; a split with segments under `use_index = false` is rejected. 4.x FEs plan segments only when `use_index` allows them. - #68815: `distance_lower_bound` (inclusive) and `distance_upper_bound` (exclusive) are validated (finite FLOAT values, lower < upper at FLOAT precision, single-vector only) and installed with `lance_scanner_set_distance_range()`, which master's pinned lance-c provides. - Review fixes: - A nested array goes to the Doris SerDes without a copy only when it and every descendant start at offset 0 and reference exactly their child ranges. Lance's legacy (v1) decoder can return a struct or list that is tight at its own level over a Boolean child with a bit offset (a batch that starts mid-byte in the bitmap), and the SerDes reject that under Arrow input validation. - A zero-field `STRUCT<>`, which Lance decodes, is reported as an unsupported column at any nesting level, like the other unsupported Lance types. It used to be typed as a STRUCT that `ColumnStruct` cannot hold (its row count comes from its first child). - A Lance Decimal256 field with precision <= 38 is narrowed to Decimal128 by a safe Arrow cast before decoding. Doris types it DECIMAL32/64/128I by precision, and those SerDes read only Decimal128 arrays; the BE used to crash on the first value, also for nested leaves. Only values a reader can see are cast: before a nested leaf is narrowed, every child slot that no visible row references (under a NULL list, fixed-size-list, map or struct row, or outside a sliced parent) is marked NULL, so a hidden value does not fail the cast and a visible value outside the declared precision still does; map keys, which cannot be NULL, stay checked. - FLOAT / DOUBLE `>=` runtime filters stay residual-only. Lance compares floats by totalOrder, so a pushed `x >= c` dropped negative-NaN rows that Doris keeps. - Runtime filters that mention INT64_MIN stay residual-only. The pinned Lance SQL parser reads `-9223372036854775808` as the negation of a magnitude that it parses as i64 first; the overflow turns the literal into a Float64, which the planner cannot coerce to Int64 or Decimal, so a pushed IN, `>=` or `<=` filter on an INT64 or scale-zero decimal column failed planning and the query failed. Every other i64 is still pushed. - The shared Arrow DATE32 decoding (`DataTypeDateV2SerDe::read_column_from_arrow`) rejects days outside 0000-01-01..9999-12-31 instead of returning a non-null `0000-00-00`, skips null slots and adds the epoch in 64 bits: the DATE32 range part of branch-4.1 / 4.2 #68649. It also changes ADBC, Arrow stream load and paimon-rust reads. - Child values hidden by a NULL list, fixed-size-list, map or struct row are not decoded: the array, map and struct SerDes give the element SerDe a validity bitmap that also clears those slots (`mask_arrow_children_hidden_by_null_parents()`). Arrow leaves them unspecified, and Lance's v2.1 miniblock encoding keeps them, so a hidden 10000-01-01 under a NULL fixed-size list used to fail the DATE32 range check above. Visible values are unchanged; this applies to ADBC, Arrow stream load and paimon-rust as well. The masks come from ExecEnv's tracked Arrow pool. A nested Lance array that is compacted before decoding is masked the same way, at every level, before its `ValidateFull()`, so a hidden invalid UTF-8 string, out-of-precision decimal, Date64 or time no longer fails the batch while a visible one still does. Since #69697 the same masks keep hidden DATETIMEV2 / TIMESTAMPTZ values out of its range checks. A mask costs at most one pass per batch: a NULL row that owns no child slots, or only NULL ones, allocates nothing, and Arrow stream load masks each retained IPC batch once (`mask_arrow_descendants_not_visible()`) before it hands row ranges to the SerDes, so its per-block reads allocate nothing. The one per-block mask left is for map keys under a NULL map row that still owns entries, since a MapArray cannot hold NULL keys; Arrow's map builder writes NULL map rows without entries. - Core: `to_protobuf` keeps `NULL_TYPE` for a NULL literal type, so a Null leaf in ARRAY / MAP / STRUCT no longer comes back as BOOLEAN. The nullable serde reads an Arrow Null array as NULLs without touching buffers that the array does not have. - Configs, caches and metrics, with the 4.x names and defaults: | Config | Default | |---|---| | `lance_index_cache_size_bytes` | 10 GiB | | `lance_metadata_cache_size_bytes` | 1 GiB | | `enable_lance_data_cache` | true | | `lance_data_cache_path` | `${DORIS_HOME}/lance_data_cache` | | `lance_data_cache_disk_capacity_bytes` | 100 GiB | | `lance_data_cache_read_block_size_bytes` | 1 MiB | | `lance_io_buffer_size_bytes` (mutable) | 2 GiB | | `lance_batch_readahead` (mutable) | 5 | | `lance_fragment_readahead` (mutable) | 5 | The BE-wide Lance session (index cache, metadata cache and the optional Foyer disk cache) is created by the first Lance scan, so a BE that never reads Lance creates no cache directory. The first six configs take effect after a restart. A negative cache size (and, while the data cache is enabled, a negative disk capacity or read block size) fails the first Lance scan with a message that names the setting, as does any data-cache value lance-c rejects; the rest of the BE keeps serving. Ten `lance_session_*` metrics report the two in-memory caches. With the defaults, the disk tier keeps 51,200 sparse 2 MiB block files open from the first Lance scan on (100 GiB in blocks of twice the read block size), the same as on branch-4.1 / 4.2. Size `lance_data_cache_disk_capacity_bytes` and `lance_data_cache_read_block_size_bytes` against the BE's open-file limit, or set `enable_lance_data_cache = false`. The index and metadata caches live in lance-c memory: they are not charged to a MemTracker or registered with CacheManager, so memory GC, the cache capacity adjustment under memory pressure and `/api/clear_cache/all` do not shrink them, and `enable_lance_data_cache = false` does not turn them off. lance-c has no call to clear, evict or resize them, so size them with the two configs. The disk tier keys blocks by Lance's store prefix (`scheme$bucket` for S3, GCS and OSS, without the endpoint) and the object path (`thirdparty/patches/lance-c-foyer.patch`). Several endpoints that serve the same bucket name and data-file path with different bytes (data files registered under chosen names or replaced in place; Lance's writers use random file names) share entries, also across a BE restart; such a deployment should set `enable_lance_data_cache = false`. Both are as on branch-4.1 / 4.2 and tracked as follow-ups. A Lance scan's native working set, that is the I/O buffered up to `lance_io_buffer_size_bytes` per scanner, the decode readahead and the operators of a search plan such as the top-k of a flat vector search, is allocated inside lance-c and is charged to neither the query nor its workload group. It counts toward the BE process memory only, so memory GC, which ranks queries by tracked memory, does not attribute it to the Lance query. lance-c accepts no memory budget, so on BEs with little memory headroom size `lance_io_buffer_size_bytes`, `lance_batch_readahead` and `lance_fragment_readahead` for the number of concurrent Lance scanners. As on branch-4.1 / 4.2; tracked as a follow-up. - Build: macOS links `-framework IOKit`. This is the first time objects of `liblance_c.a` are pulled into `doris_be`, so the binary and the link time grow. With this PR `util/rle_encoding.h` reaches 231 TUs and `util/threadpool.h` 589, the same as master `315c2e00331` alone (limits 275 and 660 since #69717), and `gen_cpp/internal_service.pb.h` 562 (master alone 559, limit 660); the reach budgets are unchanged. Not included: the Lance index metadata scanner (`LANCE_INDEX_ENTRIES`); the FE side, including the rolling-upgrade guard that keeps Lance lazy materialization off while an old BE could decode a version 1 row location as version 0; the regression suites; the rest of #68707 (searching historical versions, tags and branches, rejecting fragment ids missing from the snapshot, naming the snapshot in errors and the profile). The `InitReaderAvgTime` / `GetBlockAvgTime` profile fix that #67005 carried goes in a separate PR. **Results.** | | master | with this PR | |---|---|---| | A `FORMAT_LANCE` query | fails before reading anything | read by `LanceTableReader` in FileScannerV2 | | A Lance row in the second phase of TopN lazy materialization | cannot be represented | a version 1 row location, read back with `take_rows` | | A row location of an unknown version or size in phase two | decoded as version 0 | rejected with an error | | An Arrow DATE32 day outside 0000-01-01..9999-12-31 (ADBC, Arrow stream load, paimon-rust, Lance) | read as a non-null `0000-00-00` | the read fails with `InvalidArgument` | BE unit tests (ASAN, macOS arm64, against master's pinned lance-c), on the head rebased onto master `378404eeb12`: 1029 cases in 54 suites run, 1027 pass and 2 Parquet microbenchmarks skip by design. They cover the Lance suites (107: `LanceTableReader*` 83, `LanceNestedNullTest` 10, `LanceRuntimeFilterHelperTest` 10, `LanceSessionManagerTest` 4); the row-location protocol (`MaterializationSharedStateTest`, `RowIdStorageReaderTest`, and the new `RowIdFetchRequestValidationTest`: count mismatch, unknown version, a Lance version on an internal mapping, an internal row id of 2^32); `FileScannerV2Test` (28); the ORC and Parquet reader suites (`NewOrcReaderTest`, `NewParquetReaderTest`, `ParquetScanTest` and the other `Parquet*` suites), whose row ids now go through `file_local`; and the Arrow / protobuf / DATE serde suites with the new `ArrowParentNullsTest`, plus the Iceberg and Parquet reader suites that decode Arrow dates. Reverting any of the review fixes, the #68813 flag, the #68707 split rule or the #68815 bounds makes its new tests fail. The sixth review round adds 13 cases (`ArrowParentNullsTest` 10, `LanceTableReaderTypeTest` 3) and extends one; with them the Arrow SerDe, Lance, ADBC and `IcebergV2ReaderTest` suites run 317 cases: 310 pass and the 7 ADBC SQLite cases skip without a local SQLite driver. The ninth review round adds `LanceRuntimeFilterHelperTest.KeepsInt64MinFiltersResidualOnly` and `LanceTableReaderFilterTest.KeepsInt64MinRuntimeFiltersOutOfLanceScanner`: both pass with the 106 Lance cases (`Lance*`) on the branch rebased onto `339e087b11f`, and restoring `parse_int64()` in either check makes exactly these two fail. On the head rebased onto master `315c2e00331`, with the per-block mask fix (`42cf4e3e5d1`), 328 cases in 24 suites pass: the Arrow SerDe and validation suites, `ArrowParentNullsTest` (15), `LoadArrowPipeTest` (47), the DATETIMEV2 / TIMESTAMPTZ SerDe suites, `ArrowColumnToDorisColumnTest`, `ArrowBlockConvertorTest`, `AdbcReaderTest`, `RemoteDorisV2ReaderTest`, `PaimonRustTableReaderTest` and the Lance suites. Allocating at the first NULL row of each call again fails the new `ArrowParentNullsTest` case and the four new `LoadArrowPipeTest` block cases, dropping the per-batch mask fails the LIST and STRUCT block cases, and returning the child unmasked fails the new timestamp case, four `ArrowParentNullsTest` cases and `LanceTableReaderTypeTest.IgnoresDate32PayloadHiddenByNullParents`. ### Release note Add nine `lance_*` BE configs and `lance_session_*` BE metrics. They take effect once the FE can plan Lance scans. Reading an Arrow DATE32 value outside 0000-01-01..9999-12-31 through ADBC, Arrow stream load or paimon-rust now fails instead of returning 0000-00-00. ### Check List (For Author) - Test <!-- At least one of them must be included. --> - [ ] Regression test - [x] Unit Test - `run-be-ut.sh --run --filter=...` with the suites listed under Results: 420 of 420 pass on macOS arm64 (ASAN) on the content before the rebases, and the Lance and Arrow SerDe suites pass again with the nested-offset, zero-field struct, hidden-child, hidden-Decimal256 and compaction-mask fixes (the ADBC SQLite cases skip without a local SQLite driver). Rebased onto master `43f9ceb48d7`: #69668 conflicted in the scanner selection and in `fetch_table_schema`, which now hands every file format to `format::fetch_file_schema()`, so the Lance branch runs ahead of that call; #69646 added a TopN row-group test that reads the version 0 row location fields, which now go through `file_local` (the earlier rebase onto `5caaeb3e3d4` resolved #69614 and #69601 in the scanner selection, the `FileFormat` enum, its test and the reach budget table). The 95 TUs that this PR changes or that master changed since `5caaeb3e3d4` were syntax-checked against the rebased tree with the UT build flags, and CI runs the suites again. Rebased again onto master `8d88f1cc6f7`: the only conflict was the budget table, where #65858 moved the `util/rle_encoding.h` baseline to 828, and the 163 TUs that this PR changes or that master changed since `43f9ceb48d7` pass the same syntax check. Rebased again onto master `339e087b11f`: the only conflict was the budget table again, where #67292 moved the `util/rle_encoding.h` baseline to 834; #69872 added Arrow type checks ahead of the NULL-parent masking in the map and struct readers and merged cleanly. Of the 114 sources that this PR changes or that master changed since `8d88f1cc6f7`, 111 pass the same syntax check. The other three are master's: two new #67292 files under `storage/segment` fail the same way on master alone, since they compile only inside their unity batch, and this PR adds no source to that target; the #69818 benchmark is not in the UT build and includes only `util/simd` headers that this PR does not touch. Reverting any of the review fixes, the #68813 flag, the #68707 split rule or the #68815 bounds makes its new tests fail. Rebased again onto master `378404eeb12`, which removed the legacy file scanner (#69723): the conflicts were in the scanner-selection code and tests that #69723 deleted, so this PR's Lance lines there went with them (including its `LanceQueriesAlwaysUseScannerV2` test), in the profile keys of `materialization_opertor.cpp`, and in the row fetch of `rowid_fetcher.cpp`, whose non-Lance path is now only `FileScannerV2::read_by_rows`; the deleted V1 ORC / Parquet row id tests took this PR's `file_local` rename with them, and the `util/rle_encoding.h` budget commit is dropped because master is no longer near that limit. Of the 100 sources that this PR changes or that master changed since `339e087b11f`, 99 pass the same syntax check; the other, `format_v2/orc/orc_reader.cpp`, fails the same way on master alone. On the rebased head the suites listed under Results run 1029 cases: 1027 pass and 2 microbenchmarks skip. The test-utility fix that `DataTypeDateTimeV2SerDeTest` needed on macOS (#69695) is now on master. Rebased again onto master `315c2e00331`: the only conflict was `TExternalSearchRequest` in `PlanNodes.thrift`, where #70075 had reserved fields 2-5 in comments; the two commits that add the fields replace those comments, and field 6 stays #70075's `paimon_options`. #69717 cut transitive includes from shared headers: of the 160 sources that this PR changes or that master changed since `378404eeb12`, 156 pass the same syntax check; the two `jemalloc_hook` tests have no compile command on macOS, `runtime/memory/jemalloc_hook.cpp` uses glibc aliases that macOS cannot build (#69819), and `format_v2/table_reader.cpp` alone fails the same way on master alone while its unity batch compiles. The full `doris_be_test` builds and links on the rebased head; `ArrowParentNulls*` and `Lance*` (121 cases) pass before the mask fix, and the 328 cases above with it. - `build-support/check-build-hygiene.sh` (on this branch rebased onto master `315c2e00331`), clang-format 16 and `build-support/run-clang-tidy.sh` on the changed files: all pass; on the changed lines clang-tidy (llvm 22) reports only `readability-inconsistent-ifelse-braces` on the `DORIS_CHECK` lines of the masking functions, the macro's expansion that master's existing `DORIS_CHECK`s report too. - [ ] Manual test (add detailed scripts or steps below) - [ ] No need to test or manual test. Explain why: - Behavior changed: - [ ] No. - [x] Yes. `PRequestBlockDesc.row_id` is `uint64`; phase two of TopN lazy materialization rejects row locations of an unknown version or size; `FORMAT_LANCE` scans run on FileScannerV2; an Arrow DATE32 day outside the Doris DATE range fails the read instead of becoming `0000-00-00`. - Does this need documentation? - [x] No. Lance is documented with the FE connector. - [ ] Yes. <!-- Add document PR link here. eg: apache/doris-website#1214 --> ### Check List (For Reviewer who merge this PR) - [ ] Confirm the release note - [ ] Confirm test cases - [ ] Confirm document - [ ] Add branch pick label <!-- Add branch pick label that this PR should merge into --> --------- Co-authored-by: zhangstar333 <[email protected]> Co-authored-by: Gabriel <[email protected]> Co-authored-by: zhaobo wang <[email protected]> Co-authored-by: jay <[email protected]> Co-authored-by: FANNG <[email protected]> Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]> Co-authored-by: zy-kkk <[email protected]>
What problem does this PR solve?
Problem Summary:
Parquet V2 currently visits row groups in physical file order. For a small
ORDER BY ... LIMIT, the best values can be near the end of a file. Earlier groups are then decoded before the TopN sink publishes a useful bound. Even after a bound arrives, the deferred row-group path does not recheck the footer unless a new scan request is published; TopN updates its bound in place.VTopNPredalso lacks the ZoneMap interface needed to reject whole row groups.For example, consider four groups containing consecutive integer keys
[300, 399],[200, 299],[100, 199],[0, 99]andORDER BY k ASC LIMIT 3. Reading the last group first produces a bound of2; the other three groups can be rejected using their footer minimum before dictionary/index probes, prefetch, or data decoding. The descending case uses the footer maximum.Optimization approach
VTopNPred. Snapshot the current runtime bound at each evaluation, retain equality for secondary sort keys, honor NULL ordering, and fall back when types, ranges, or floating-point NaN information are unsuitable.Result and evaluation contract
The optimization must preserve the final TopN result, including secondary-key ties and NULL placement. It may avoid evaluating expressions or materializing columns for rows that a valid TopN bound proves cannot contribute to that result. Preserving potential exceptions from such excluded rows is not a requirement: a DATE overflow in a discarded row group, for example, does not require reading that group. Existing TopN/runtime-filter pushdown eligibility and file-to-table localization remain unchanged.
No executed error is caught, ignored, or converted to success. A conversion that is actually needed still follows the existing strict/non-strict rules and propagates its failure. This distinction also applies to unprojected equality-delete keys and set-operation inputs reduced through existing runtime filters. It does not permit skipping candidate rows, bypassing deletes for retained rows, re-evaluating a volatile leading key as a different value, or trusting incompatible statistics.
Earlier follow-ups added FE expression whitelists and table-mapping restrictions to preserve errors in rows outside the result. Those restrictions are removed; they unnecessarily reduced existing optimization eligibility. The production change is confined to the TopN ZoneMap capability and Parquet scheduling. Existing metadata-pruning boundaries and ordered row-ID fetches remain respected. Upgrade compatibility for legacy FE requests using Parquet V1 is outside the scope of this PR; the follow-up dedicated to that case has been reverted.
Eligibility and limits
max(estimated_scan_rows, 1) * topn_filter_ratio > LIMIT + OFFSET(default ratio:0.5); existing window, CTE, nested-TopN and join-side rules remain unchanged.abs(k)are not mapped to footer bounds. Projected or secondary-key expressions do not need a new non-throwing proof.Measured optimization effect
The included opt-in
ParquetScanTest.TopNRowGroupOrderMicrobenchexercises the actual Parquet V2 reader plus a bounded TopK heap and per-block updates of the real runtime predicate. Its workload corresponds to:Fixture: one required INT32 column, 128 row groups x 4,096 rows (524,288 rows), disjoint key ranges, and the best group physically last for each direction. Files use uncompressed PLAIN DataPageV2 with dictionary and PageIndex disabled, isolating footer pruning. Every iteration verifies all ten final values.
Method: Clang 20 RELEASE builds, warm local filesystem cache, the same pinned CPU, nine paired samples alternating baseline/candidate order, and 32 reader queries per sample after warmup. These paired timings were collected before the eligibility cleanup; the direct one-column workload exercises the same scheduling path. Fresh reader correctness tests still verify best-first reads and late-bound group rejection. The baseline restores
vtopn_pred.{h,cpp}andparquet_scan.{h,cpp}from the pre-optimization base8d2bd1e14b6d95d923d43e00857da326639cc466while retaining the identical benchmark harness and request plumbing; the benchmark calls ParquetReader directly. All paired checksums match. Work counters are identical across all samples. ASC candidate samples range from 0.647 to 1.097 ms and DESC from 0.642 to 0.745 ms; the shared host has timing outliers. These are reader microbenchmark results; complete SQL gains depend on file splits, parallelism, feedback latency, and storage.Validation and reproduction
TOPN OPT:EXPLAIN assertion. The output-evolution optimization test and FE filter-generation test first failed on the preceding implementation, then passed after removal of the unnecessary fences.nereids_tpch_p0/tpch/topn-filter.groovyalso exposed an actual regression: the blanket visitor stop removedTOPN OPTforGROUP BY k ORDER BY k LIMIT 1. Restore the original visitor traversal and cover grouped keys, aggregation with HAVING and a join, and grouping sets withpush_topn_to_agg=false. These cases run full FE planning and checkTOPN OPT:in the translated EXPLAIN fragments. The same unnecessary expression whitelist also disabledTOPN OPTfor a nested STRUCT order key with anIS NOT NULLpredicate on a sibling field, as exposed bynereids_rules_p0/column_pruning/topn_filter_nested_column_pruning.groovy. Additional translated-plan checks cover TIMESTAMPTZ, INT and DATE fields in ASC/DESC, including the complete and predicate-only nested access paths. Both existing regression suites and their assertions are unchanged. FE production logic matches the pre-PR base; only an explanatory traversal comment remains in the FE production diff.Correctness suites:
./run-be-ut.sh -j8 --run \ --filter='*Parquet*:*ColumnMapper*:*TableReader*:*Iceberg*:*EvolutionCast*:*RuntimePredicate*' ./run-fe-ut.sh --run \ org.apache.doris.nereids.postprocess.TopNRuntimeFilterTest,org.apache.doris.nereids.rules.rewrite.DateArithmeticFilterPushDownTest,org.apache.doris.nereids.postprocess.PushDownFilterThroughProjectTest,org.apache.doris.nereids.util.ExpressionUtilsTestOpt-in benchmark (use a RELEASE UT build):
For paired comparison, build and save baseline/candidate UT executables with the same harness, then use the existing runner:
Select an available idle CPU for
QUERY_ENGINE_BENCH_CPU. The runner emits paired timings/checksums;PARQUET_TOPN_WORKlines report per-query raw rows, groups filtered by min/max, and actual local read bytes.Release note
Improve Parquet V2 TopN scans by reading promising row groups first and applying the latest runtime bound before opening subsequent groups.
Check List (For Author)
Check List (For Reviewer who merge this PR)