Skip to content

[improvement](be) Prune nested Parquet leaves with Bloom filters - #66423

Draft
Gabriel39 wants to merge 5 commits into
apache:masterfrom
Gabriel39:dev/doris-27735-parquet-bloom-pruning
Draft

[improvement](be) Prune nested Parquet leaves with Bloom filters#66423
Gabriel39 wants to merge 5 commits into
apache:masterfrom
Gabriel39:dev/doris-27735-parquet-bloom-pruning

Conversation

@Gabriel39

@Gabriel39 Gabriel39 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: File Scanner V2 only used native Parquet Bloom filters for top-level primitive slots, and null-safe equality did not advertise Bloom evaluation. Nested STRUCT and LIST predicates also require stricter ownership and equality handling because one expression tree can mention multiple leaves, multiple predicates can share one physical leaf, and Parquet hashes physical floating-point bytes.

This change:

  • Resolves exact STRUCT and LIST accessor paths to localized primitive leaves.
  • Requires every Bloom-capable branch in a compound predicate to resolve to one unique leaf.
  • Materializes production nested IN values while keeping ZoneMap, dictionary, and raw paths direct-slot-only.
  • Reads and decodes each physical leaf Bloom at most once per row group.
  • Preserves Doris FLOAT/DOUBLE equality for signed zero and NaN payloads.
  • Keeps uncertain, missing, or unreadable paths conservative.

Release note

Enable native Parquet Bloom pruning for STRUCT and LIST leaves and non-null null-safe equality, with conservative floating-point semantics and shared per-leaf Bloom reads.

Check List (For Author)

  • Test: Unit Test
    • 49 relevant BE unit tests passed across ExprZonemapFilterTest, NativeParquetStatisticsTest, and ParquetBloomFilterPruningTest.
  • Behavior changed: Yes. Eligible nested predicates and non-null null-safe equality can prune row groups through native Parquet Bloom filters without binding compound expressions to the wrong leaf or violating floating-point equality.
  • Does this need documentation: No

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: File Scanner V2 only used native Parquet Bloom filters for top-level primitive slots, and null-safe equality did not advertise Bloom evaluation. Resolve exact STRUCT and LIST accessor paths to localized primitive leaves, evaluate equality and IN predicates against the leaf Bloom filter, and allow null-safe equality only for non-null literals. Uncertain or missing paths remain conservative. The focused metadata test reduces selected row groups from one to zero for an absent nested value while retaining the row group for present values and missing paths.

### Release note

Enable native Parquet Bloom pruning for STRUCT and LIST leaves and for null-safe equality with non-null constants.

### Check List (For Author)

- Test: Unit Test
    - ExprZonemapFilterTest.*
    - ParquetBloomFilterPruningTest.*
- Behavior changed: Yes. Eligible nested predicates and non-null null-safe equality can now prune row groups through native Parquet Bloom filters.
- Does this need documentation: No
@Gabriel39
Gabriel39 requested a review from yiguolei as a code owner August 4, 2026 07:13
@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

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

Requesting changes. The implementation is focused, but four material issues remain: compound predicates can bind a Boolean subtree to the wrong nested Bloom leaf; production nested IN never materializes and cannot reach the new capability; FLOAT/DOUBLE raw Parquet Bloom hashes do not preserve Doris signed-zero/NaN equality; and same-leaf nested predicates reread and reparse the Bloom per conjunct.

Critical checkpoint conclusions:

  • Goal and correctness: simple STRUCT/LIST leaf resolution is conservative, but M1 and M4 can falsely skip matching Row Groups, and M2 leaves a stated production feature unreachable.
  • Compatibility and mapping: supported name/ordinal localization and missing-path fallback are sound; external Parquet FLOAT/DOUBLE PLAIN-byte compatibility is not.
  • Parallel paths and lifecycle: direct-slot preparation/grouping was not carried through to nested IN materialization or nested same-leaf Bloom sharing.
  • Performance and observability: M3 duplicates remote header/payload reads and parsing; existing timing records the cost but the tests do not count reads.
  • Tests: the new tests cover synthetic happy paths but bypass production VInPredicate preparation, complete mapper/footer integration, multi-leaf compound ownership, repeated-read counting, and external-writer FLOAT/DOUBLE bytes.
  • Error handling, ownership, concurrency, configuration, and persistence: missing/unsupported/unreadable metadata otherwise retains the Row Group; temporary Bloom ownership is scoped; no new shared-state, locking, configuration, persistence, transaction, data-write, or FE/BE propagation surface was introduced.
  • User focus: no additional focus was supplied, so the entire PR was reviewed without a narrower scope.

Validation was static-only as required by the review environment; builds and tests were intentionally not run. The bundled and live head/base were verified as 19d48a3a90d615c07ab73711b079e90f6839fe33 / 99dd2b330c32f903f39e4a9f1bda154544682298. Three review rounds converged with all full-review and risk-focused reviewers returning NO_NEW_VALUABLE_FINDINGS on this exact four-comment set.

Comment thread be/src/exprs/expr_zonemap_filter.cpp Outdated
continue;
}
auto child_probe = extract_bloom_filter_predicate_probe(child);
if (!child_probe.has_value()) {

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] Reject compound trees without one unique Bloom probe

Here nullopt means either that a child has no probe or that it contains conflicting nested probes, and the parent silently ignores both cases. For ((s.a = 1 AND s.b = 2) OR s.a = 3), the inner AND is skipped, this returns s.a, and VCompoundPred evaluates s.b = 2 against the s.a Bloom; a Row Group containing (1,2) can therefore be reported as kNoMatch (or incompatible leaf types can hit the type check). If a Bloom-capable child does not resolve to the same unique probe, please make the whole compound ineligible, and cover same- and mixed-type sibling leaves.

@@ -218,7 +218,7 @@ ZoneMapFilterResult VInPredicate::evaluate_bloom_filter(const BloomFilterEvalCon

bool VInPredicate::can_evaluate_bloom_filter() const {
return _zonemap_materialized && !_is_not_in &&

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] Materialize nested IN probes before checking Bloom capability

This new accessor-aware check still requires _zonemap_materialized, but _materialize_for_zonemap_filter() returns early unless child 0 is a direct slot. A production element_at(s, 'a') IN (2, 4) therefore always reports false here, so the nested-IN part of this feature is unreachable; the test's BloomInExpr masks this by hard-coding capability and values. Please allow valid primitive nested probes to materialize their constant set while retaining the direct-slot gates for ZoneMap/dictionary/raw paths, and add a prepared production VInPredicate test.

}
if (ParquetStatisticsUtils::NativeBloomFilterExcludes(*column_schema, slot_index, conjuncts,
*bloom_filter)) {
if (bloom_filter_excludes(*column_schema, probe->slot_index, {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.

[P2] Share one Bloom read across predicates on the same nested leaf

This loop calls bloom_filter_excludes once per conjunct, and each call reads the header and payload and reparses the same physical Bloom. Two independently retained Bloom-capable predicates, such as equality and non-null null-safe equality on the same nested leaf, therefore duplicate remote I/O for every surviving Row Group, whereas the top-level path groups predicates and reads once. Please group by the resolved physical leaf (or cache the decoded Bloom per leaf) and add a counting-reader test that asserts one header/payload pair.


bool can_evaluate_bloom_filter_equality(const VExprSPtrs& args) {
auto slot_literal = extract_bloom_filter_slot_and_literal(args);
return slot_literal.has_value() && !slot_literal->literal.is_null() &&

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 FLOAT/DOUBLE equality classes when probing Parquet Blooms

This capability now admits nested FLOAT/DOUBLE equality (and is reused by null-safe equality), but the native adapter hashes the literal's raw IEEE bytes. Doris considers +0.0 == -0.0 and equates NaNs, while Parquet Bloom filters hash their distinct PLAIN encodings, so a Bloom containing -0.0 can reject a +0.0 probe and falsely skip a matching Row Group. Please probe both zero encodings and conservatively return kMayMatch for NaN, or disable FLOAT/DOUBLE Bloom pruning; add an external-writer signed-zero/NaN regression.

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

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

Requesting changes. The implementation is focused and the current head addresses the four existing review threads, but three material issues remain: nested Bloom pruning can suppress an earlier required query error; the decoded-Bloom cache can retain unbounded aggregate memory across distinct leaves; and LIST-to-STRUCT probes remain unreachable through the production mapper even for identical schemas.

Critical checkpoint conclusions:

  • Goal, scope, and correctness: direct STRUCT/LIST leaf resolution, nested IN materialization, unique compound ownership, null-safe equality, and FLOAT/DOUBLE equality handling are now conservative. MAIN-001 is a correctness regression, MAIN-002 is an availability/memory regression, and MAIN-003 leaves part of the stated nested path support undelivered. The eight-file change is otherwise focused.
  • Concurrency and lifecycle: pruning state is stack-local to one Row Group call and introduces no shared mutable state or locks. Scan-local expressions are cloned, prepared, and opened before metadata selection. The cache is released at return, but its aggregate successful-entry lifetime is the MAIN-002 defect.
  • Compatibility, parallel paths, and conditions: no storage format, protocol, configuration, persistence, transaction, data-write, or FE/BE variable surface changes. Missing, unsupported, unreadable, or type-incompatible metadata retains the Row Group; top-level and nested paths share the same physical-leaf identity and conservative fallback. The v1 reader path is unchanged.
  • Tests and results: the changed unit tests cover prepared nested IN, direct STRUCT/LIST resolution, compound ownership, signed zero/NaN, and same-leaf read reuse. They do not cover the error-order, multi-leaf retention, or mapper-to-Parquet LIST-to-STRUCT regressions requested inline. Review validation was static-only as required; builds and tests were not run.
  • Performance and observability: Bloom read timing and the filtered-Row-Group counter remain correctly scoped, and one read is reused per physical leaf. Aggregate decoded-filter retention still needs a bound; no additional logging or metrics gap was substantiated.
  • User focus: no extra focus was supplied, so the complete PR was reviewed without a narrower scope.

Review status: complete and converged after two rounds; every Round 2 full-review and risk-focused agent returned NO_NEW_VALUABLE_FINDINGS on this exact three-comment set. The live base/head were reverified as 99dd2b330c32f903f39e4a9f1bda154544682298 / 866a6906913eb9cd2a5eb3c94c2b57499f77afc9 immediately before submission.

}
}

for (const auto& conjunct : request.conjuncts) {

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 earlier errors before nested Bloom pruning

request.conjuncts preserves row-level order, but this new loop can evaluate a later nested Bloom past an earlier error-preserving predicate. For example, with assert_true(x <> 0, 'bad') followed by element_at(s, 'a') = 2, a Row Group containing x = 0 whose nested-leaf Bloom excludes 2 returns BLOOM_FILTER here, so no row reaches assert_true and the required error becomes a successful empty result. The existing TODO at lines 507-508 already records the needed invariant. Please fence metadata pruning at the first conjunct that is unsafe on selected rows (across all metadata stages), and add a regression with an earlier assert_true plus a later Bloom-negative nested predicate.

if (file_context == nullptr || file_context->native_file == nullptr) {
return ParquetRowGroupPruneReason::NONE;
}
std::map<int, std::unique_ptr<native::BlockSplitBloomFilter>> bloom_filters_by_leaf;

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] Bound the lifetime of decoded Blooms across leaves

This map retains every successfully decoded filter until the Row Group check returns, and each BlockSplitBloomFilter owns a copy of a payload accepted up to BloomFilter::MAXIMUM_BYTES (128 MiB). On the no-prune path, eight predicate leaves with valid maximum-size Blooms therefore keep roughly 1 GiB live in one scanner (plus each transient read buffer), whereas the previous loop released one leaf before loading the next. Please preserve the one-read guarantee for repeated predicates without retaining all distinct leaves—for example, track remaining uses and erase a leaf after its last evaluation—and cover the multi-leaf retention bound.

}
break;
}
case TYPE_ARRAY:

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] Connect LIST-to-STRUCT probes to production localization

The recursive extractor accepts a LIST_ELEMENT -> STRUCT_FIELD path, but TableColumnMapper::collect_struct_element_chain() rejects a struct accessor whose parent is the computed array element. Thus element_at(element_at(items, 1), 'a') = 7 produces no file-local conjunct even when the table and file ARRAY<STRUCT<a: INT>> schemas are identical, and this Parquet Bloom path is never reached; the existing ArrayWrapperDoesNotBuildNestedPredicateFilter test confirms that request is empty. Please add a schema-safe localization path plus a mapper-to-Parquet regression, or narrow the advertised recursive LIST/STRUCT support to the path shapes that can actually reach the reader.

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 29346 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 866a6906913eb9cd2a5eb3c94c2b57499f77afc9, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17819	4011	3980	3980
q2	2006	333	221	221
q3	10271	1446	809	809
q4	4679	484	351	351
q5	7528	860	578	578
q6	181	177	136	136
q7	790	815	600	600
q8	9343	1619	1625	1619
q9	5322	4079	4063	4063
q10	6776	1626	1356	1356
q11	526	368	337	337
q12	748	567	445	445
q13	18087	3311	2750	2750
q14	271	272	248	248
q15	q16	743	730	664	664
q17	1034	1007	928	928
q18	6627	5648	5565	5565
q19	1401	1260	1034	1034
q20	826	716	589	589
q21	6202	2758	2803	2758
q22	474	368	315	315
Total cold run time: 101654 ms
Total hot run time: 29346 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4872	4608	4525	4525
q2	297	341	210	210
q3	4883	5339	4762	4762
q4	2162	2237	1418	1418
q5	4765	4391	4475	4391
q6	247	204	132	132
q7	1905	1735	1498	1498
q8	2337	2057	1998	1998
q9	7143	6737	6742	6737
q10	4298	4223	3787	3787
q11	513	395	370	370
q12	698	717	488	488
q13	2915	3237	2789	2789
q14	274	271	244	244
q15	q16	667	685	600	600
q17	1241	1220	1214	1214
q18	12102	11022	11741	11022
q19	1058	1071	1047	1047
q20	2196	2187	1901	1901
q21	5323	4562	4592	4562
q22	518	481	405	405
Total cold run time: 60414 ms
Total hot run time: 54100 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 166475 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 866a6906913eb9cd2a5eb3c94c2b57499f77afc9, data reload: false

query5	4294	593	461	461
query6	453	223	206	206
query7	4863	593	333	333
query8	319	161	145	145
query9	8773	4069	4081	4069
query10	458	352	299	299
query11	5858	2178	1991	1991
query12	153	98	95	95
query13	1261	610	423	423
query14	6068	4294	4004	4004
query14_1	3896	3826	3841	3826
query15	199	193	174	174
query16	1022	489	447	447
query17	896	696	537	537
query18	2414	470	343	343
query19	204	180	143	143
query20	103	98	100	98
query21	227	157	134	134
query22	13133	13275	12834	12834
query23	15815	15054	14652	14652
query23_1	14651	14744	14762	14744
query24	7531	1719	1228	1228
query24_1	1248	1235	1233	1233
query25	519	411	344	344
query26	1297	355	219	219
query27	2608	632	376	376
query28	4599	2076	2052	2052
query29	1050	619	460	460
query30	340	262	217	217
query31	1171	1105	1056	1056
query32	118	65	61	61
query33	524	335	269	269
query34	1196	1118	629	629
query35	739	757	622	622
query36	782	790	697	697
query37	158	105	90	90
query38	1842	1770	1696	1696
query39	832	847	800	800
query39_1	790	790	798	790
query40	248	161	146	146
query41	66	61	62	61
query42	93	91	94	91
query43	325	317	280	280
query44	1400	792	788	788
query45	190	174	172	172
query46	1092	1200	721	721
query47	1551	1545	1450	1450
query48	385	448	307	307
query49	590	415	305	305
query50	1098	440	356	356
query51	10717	10284	10473	10284
query52	91	92	78	78
query53	263	269	205	205
query54	294	262	224	224
query55	76	75	69	69
query56	322	312	326	312
query57	1018	999	955	955
query58	275	271	265	265
query59	1592	1628	1399	1399
query60	337	287	269	269
query61	176	194	147	147
query62	395	325	268	268
query63	228	189	204	189
query64	2865	1034	840	840
query65	3921	3857	3804	3804
query66	1831	456	355	355
query67	28635	28204	28140	28140
query68	3504	1569	1059	1059
query69	405	296	265	265
query70	880	777	769	769
query71	360	326	300	300
query72	3004	2649	2461	2461
query73	874	764	423	423
query74	4611	4525	4302	4302
query75	2372	2326	1998	1998
query76	2430	1133	813	813
query77	333	362	264	264
query78	11163	11227	10494	10494
query79	1400	1178	770	770
query80	645	545	482	482
query81	452	324	284	284
query82	631	177	137	137
query83	396	327	297	297
query84	322	163	130	130
query85	914	603	522	522
query86	313	241	229	229
query87	1958	1983	1841	1841
query88	3717	2835	2776	2776
query89	382	321	285	285
query90	1870	197	189	189
query91	200	192	162	162
query92	61	61	52	52
query93	1578	1558	977	977
query94	527	345	303	303
query95	782	505	558	505
query96	1024	772	353	353
query97	2467	2478	2314	2314
query98	195	188	204	188
query99	723	734	625	625
Total cold run time: 252993 ms
Total hot run time: 166475 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 23.93 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 866a6906913eb9cd2a5eb3c94c2b57499f77afc9, data reload: false

query1	0.01	0.01	0.01
query2	0.09	0.05	0.04
query3	0.26	0.14	0.14
query4	1.61	0.14	0.14
query5	0.24	0.22	0.23
query6	1.16	0.83	0.83
query7	0.04	0.00	0.00
query8	0.06	0.04	0.03
query9	0.36	0.32	0.32
query10	0.59	0.55	0.55
query11	0.18	0.14	0.14
query12	0.18	0.14	0.13
query13	0.46	0.46	0.47
query14	1.01	0.98	0.99
query15	0.60	0.60	0.59
query16	0.33	0.31	0.32
query17	1.08	1.09	1.07
query18	0.22	0.20	0.21
query19	1.99	1.97	1.95
query20	0.01	0.01	0.02
query21	15.44	0.21	0.13
query22	4.91	0.06	0.05
query23	16.14	0.31	0.12
query24	2.88	0.41	0.32
query25	0.11	0.06	0.05
query26	0.73	0.21	0.15
query27	0.04	0.03	0.04
query28	3.55	0.77	0.34
query29	12.50	4.02	3.17
query30	0.28	0.15	0.15
query31	2.80	0.57	0.31
query32	3.23	0.59	0.48
query33	3.10	3.20	3.18
query34	15.67	3.93	3.31
query35	3.29	3.24	3.23
query36	0.54	0.45	0.43
query37	0.08	0.06	0.07
query38	0.05	0.03	0.03
query39	0.04	0.03	0.03
query40	0.20	0.15	0.15
query41	0.09	0.03	0.02
query42	0.04	0.03	0.03
query43	0.04	0.04	0.04
Total cold run time: 96.23 s
Total hot run time: 23.93 s

@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

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

Request changes: the patch is focused and its supported Bloom decisions are otherwise conservative, but two P1 correctness/reachability blockers and two P2 production-operability/performance gaps remain.

Critical checkpoints

  • Goal and proof: Not achieved in production. Nested equality/IN and direct eq_for_null are excluded from the metadata-safe prefix, so the advertised Bloom path never runs through the real TableReader flow. The added helper, synthetic-expression, and direct-request tests bypass that gate. They also miss nullable-file-to-required-table ARRAY/STRUCT evolution, Bloom fallback outcomes, and cross-leaf read order.
  • Scope and clarity: The change is reasonably small and concentrated in expression capability, table-to-file localization, request fencing, and Parquet metadata pruning. No unrelated source changes were found.
  • Concurrency: No new thread, lock, or shared mutable state is introduced. Initial and refreshed scan requests remain isolated, layout-checked, and activated at Row Group boundaries; no lock-order or deadlock issue was found.
  • Lifecycle: Per-leaf grouping now reads a reached leaf once and keeps at most one decoded cross-leaf Bloom payload alive, with early-return destruction. No static-initialization or circular-lifetime issue was found.
  • Configuration: No configuration item is added.
  • Compatibility: There is no function-symbol, protocol, or storage-format write change. Read-side schema evolution is not safe, however: ARRAY localization can filter away a nullable physical child before TableReader enforces a required table child, changing an error into a successful smaller result.
  • Parallel paths and conditions: Statistics, Dictionary, Bloom, PageIndex, constant, and partition pruning all honor the new monotone safety barrier, and late refresh does not cross it. The blocker is the production classifier being too conservative for the newly supported total shapes. Existing compound, nested-IN, signed-zero/NaN, same-leaf-read, payload-lifetime, unsafe-order, and original LIST-localization threads were rechecked and not duplicated.
  • Tests and results: The new C++ tests cover helper semantics, conservative barriers, and same-leaf reuse, but not the four production contracts above; no result-file update is applicable. Static inspection only was performed because this review runner explicitly forbids builds and test execution.
  • Observability: Insufficient. The query Profile cannot distinguish Bloom attempts, usable filters, missing/unsupported metadata, corruption, or I/O fallbacks; process-wide Bloom residency gauges do not provide those per-query outcomes.
  • Transactions, persistence, writes, and FE/BE variables: None are involved.
  • Performance: Same-leaf deduplication and one-live-payload ownership are improvements, but sorting groups by physical leaf ID can read lower-numbered may-match payloads (up to 128 MiB each) before the logical first probe that would already prune the Row Group.
  • Other issues: No additional distinct correctness, lifecycle, compatibility, configuration, persistence, protocol, performance, observability, or coverage issue survived the final sweep.

Review coverage and completion

  • review_focus.txt supplied no additional user focus; the complete 16-file PR was reviewed.
  • The live head/base still match the authoritative bundle, and no live review/comment appeared beyond the seven already deduplicated threads.
  • Three full review rounds completed. Every final-round reviewer returned NO_NEW_VALUABLE_FINDINGS for this exact four-comment payload, so the review is complete.

build_table_filters_from_conjunct(conjunct, _runtime_state, &_table_filters));
for (size_t filter_idx = first_new_filter; filter_idx < _table_filters.size();
++filter_idx) {
_table_filters[filter_idx].metadata_pruning_safe = in_safe_prefix;

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 the new Bloom predicates inside the safe prefix

Production element_at/struct_element nodes are VectorizedFnCalls, but their selected-row-safety allowlist excludes both accessors; it also excludes eq_for_null itself. Thus a nested equality/IN fails on its accessor child and even top-level x <=> 7 fails at the root. This assignment excludes each of those filters from the safe prefix; in a request containing only such a predicate, the safe count is zero, so Statistics/Dictionary/Bloom/PageIndex never run the new production capabilities. The tests bypass this with custom default-safe expressions or hand-built TableFilter/FileScanRequest objects. Please classify the proven-total accessor/null-safe shapes without weakening the error barrier, and add a real TableReader-to-Parquet test that observes a positive safe count and Bloom read/prune.

if (is_struct_element_expr(candidate)) {
return true;
}
return candidate != nullptr && candidate->get_num_children() == 2 &&

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 required child nullability through ARRAY localization

This newly admits ARRAY accessors into a guard that compares each physical child with the accessor expression's result type. Production FunctionArrayElement always makes ARRAY and STRUCT access results Nullable, so for table items ARRAY<STRUCT<a: INT NOT NULL>>, file items ARRAY<STRUCT<a: Nullable(INT)>>, and items[1].a > 10, the check later sees Nullable(INT) versus Nullable(INT) and localizes the filter. The file reader can then discard a=NULL before TableReader's required-child alignment reports the schema violation, changing an error into a successful smaller result. The existing guard test uses a synthetic non-null result type, and the new ARRAY test uses identical schemas. Please validate against the mapped table child types/nullability and add a production-expression schema-evolution regression.

: &pruning_stats->bloom_filter_read_time);
status = read_native_bloom_filter(chunk.meta_data, file_context->native_file,
file_context->native_io_ctx, &bloom_filter);
const auto status = read_native_bloom_filter(

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] Expose Bloom probe fallback outcomes

This broadened per-leaf path erases every non-OK Bloom load result—missing metadata, unsupported headers, malformed/truncated payloads, and remote I/O failures all become the same silent Row Group retention. The Profile exposes only Bloom read time and successfully pruned groups, so operators cannot distinguish a legitimate may-match from the feature failing to load every Bloom. The mandatory format-v2 guide calls for attempts, successes, conservative fallbacks, and corrupt rejections separately. Please add and publish those counters (with tests for missing, malformed, truncated, and I/O-error cases).

add_probe(*column_schema, probe->slot_index, {conjunct});
}

for (const auto& [leaf_column_id, probes] : probes_by_leaf) {

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] Preserve first-probe order when grouping Blooms

This physical-leaf-keyed map delays evaluation and sorts groups by Parquet leaf ID. With a reordered field-ID schema, logical slot 0 can map to leaf 1 and exclude while slot 1 maps to leaf 0 and may-match; the previous loop stopped after leaf 1, but this loop reads leaf 0 first. Since each accepted payload may be 128 MiB, lower-numbered leaves can add large remote I/O before the inevitable prune. Please retain per-leaf sharing and one-live-payload ownership while iterating groups in first-probe order, and add a reversed block-slot/physical-leaf test that proves the later may-match payload is not read.

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28567 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 36999630784e40bb41c0e74552b49d593fd8bf59, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17578	3939	3951	3939
q2	2000	315	214	214
q3	10436	1429	793	793
q4	4753	468	337	337
q5	8183	851	562	562
q6	323	165	135	135
q7	814	816	588	588
q8	10685	1662	1503	1503
q9	5808	4026	4022	4022
q10	6804	1663	1372	1372
q11	498	351	319	319
q12	718	569	459	459
q13	18141	3352	2746	2746
q14	268	259	238	238
q15	q16	733	740	669	669
q17	1032	1038	943	943
q18	6552	5597	5648	5597
q19	1171	1152	945	945
q20	817	701	593	593
q21	5579	2629	2294	2294
q22	423	352	299	299
Total cold run time: 103316 ms
Total hot run time: 28567 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4235	4132	4155	4132
q2	279	316	199	199
q3	4633	4888	4404	4404
q4	2152	2265	1388	1388
q5	4378	4116	4093	4093
q6	223	172	126	126
q7	1698	1582	1882	1582
q8	2412	2041	2063	2041
q9	7218	7199	7184	7184
q10	4312	4285	3896	3896
q11	549	388	375	375
q12	705	737	499	499
q13	3248	3744	2954	2954
q14	323	310	294	294
q15	q16	676	720	635	635
q17	1318	1283	1231	1231
q18	12210	11054	11934	11054
q19	1152	1148	1107	1107
q20	2259	2219	1961	1961
q21	5662	4933	4630	4630
q22	525	437	394	394
Total cold run time: 60167 ms
Total hot run time: 54179 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 165948 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 36999630784e40bb41c0e74552b49d593fd8bf59, data reload: false

query5	4296	586	451	451
query6	466	220	193	193
query7	4858	566	349	349
query8	326	158	147	147
query9	8756	4064	4066	4064
query10	483	356	307	307
query11	5812	2217	1990	1990
query12	152	101	93	93
query13	1242	607	434	434
query14	6079	4252	4002	4002
query14_1	3812	3783	3776	3776
query15	204	194	177	177
query16	982	509	475	475
query17	927	705	574	574
query18	2444	452	332	332
query19	198	181	142	142
query20	102	103	107	103
query21	236	149	130	130
query22	13058	13004	12799	12799
query23	15763	14985	14618	14618
query23_1	14704	14737	14612	14612
query24	7528	1699	1232	1232
query24_1	1240	1211	1255	1211
query25	524	418	347	347
query26	1319	351	209	209
query27	2584	612	388	388
query28	4519	2073	2062	2062
query29	1053	635	464	464
query30	337	262	224	224
query31	1189	1129	1081	1081
query32	113	60	58	58
query33	508	303	228	228
query34	1194	1123	660	660
query35	737	744	632	632
query36	761	775	698	698
query37	155	107	90	90
query38	1837	1779	1692	1692
query39	836	833	810	810
query39_1	784	784	784	784
query40	240	169	144	144
query41	65	63	61	61
query42	99	94	94	94
query43	318	320	281	281
query44	1416	778	778	778
query45	192	169	180	169
query46	1076	1150	700	700
query47	1525	1541	1459	1459
query48	396	404	307	307
query49	582	428	292	292
query50	1036	433	360	360
query51	10689	10469	10531	10469
query52	85	87	74	74
query53	251	268	199	199
query54	276	224	229	224
query55	74	71	71	71
query56	308	303	282	282
query57	1014	1000	934	934
query58	280	249	251	249
query59	1545	1592	1370	1370
query60	304	258	263	258
query61	179	171	170	170
query62	399	325	273	273
query63	236	202	204	202
query64	2901	1164	1013	1013
query65	3897	3848	3818	3818
query66	1844	477	367	367
query67	28206	28166	27415	27415
query68	3301	1616	1038	1038
query69	416	312	268	268
query70	873	772	785	772
query71	367	326	313	313
query72	3151	2729	2319	2319
query73	798	731	407	407
query74	4656	4511	4333	4333
query75	2348	2318	1987	1987
query76	2328	1132	748	748
query77	335	372	272	272
query78	11245	11222	10657	10657
query79	1378	1157	729	729
query80	1251	547	472	472
query81	519	329	291	291
query82	655	171	135	135
query83	378	325	299	299
query84	326	161	132	132
query85	990	632	514	514
query86	403	239	221	221
query87	1977	1978	1823	1823
query88	3730	2794	2846	2794
query89	405	314	287	287
query90	1873	208	195	195
query91	201	185	164	164
query92	64	61	58	58
query93	1676	1575	1024	1024
query94	725	341	311	311
query95	801	611	489	489
query96	1060	768	373	373
query97	2450	2447	2359	2359
query98	197	185	187	185
query99	751	730	604	604
Total cold run time: 253200 ms
Total hot run time: 165948 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 23.94 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 36999630784e40bb41c0e74552b49d593fd8bf59, data reload: false

query1	0.00	0.00	0.00
query2	0.09	0.04	0.05
query3	0.25	0.13	0.13
query4	1.61	0.14	0.14
query5	0.23	0.23	0.23
query6	1.16	0.83	0.84
query7	0.04	0.02	0.01
query8	0.06	0.04	0.04
query9	0.38	0.32	0.31
query10	0.55	0.60	0.54
query11	0.19	0.14	0.13
query12	0.17	0.15	0.14
query13	0.46	0.48	0.47
query14	1.00	0.99	1.00
query15	0.60	0.59	0.59
query16	0.32	0.33	0.32
query17	1.12	1.04	1.07
query18	0.21	0.19	0.20
query19	2.05	1.95	1.94
query20	0.02	0.02	0.01
query21	15.42	0.18	0.14
query22	5.00	0.06	0.05
query23	16.13	0.31	0.12
query24	2.96	0.41	0.33
query25	0.11	0.05	0.04
query26	0.74	0.20	0.15
query27	0.03	0.04	0.04
query28	3.54	0.71	0.34
query29	12.54	4.12	3.22
query30	0.27	0.16	0.16
query31	2.77	0.56	0.32
query32	3.22	0.60	0.48
query33	3.16	3.16	3.23
query34	15.70	3.93	3.28
query35	3.23	3.24	3.21
query36	0.54	0.44	0.44
query37	0.09	0.06	0.07
query38	0.05	0.04	0.04
query39	0.04	0.03	0.03
query40	0.17	0.15	0.15
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.39 s
Total hot run time: 23.94 s

### What problem does this PR solve?

Issue Number: None

Related PR: apache#66423

Problem Summary: Production nested accessors and null-safe equality were excluded from the metadata-pruning safe prefix, ARRAY accessor result nullability could hide a required table child, physical-leaf grouping could reorder large Bloom reads, and unavailable Bloom payloads were not observable. Classify only total accessor shapes, retain declared table child types during localization, preserve first-probe order with one decoded payload at a time, and publish separate attempt, success, fallback, and corruption counters.

### Release note

Make nested Parquet Bloom pruning reachable for production accessors while preserving schema validation, probe order, and observable conservative fallbacks.

### Check List (For Author)

- Test: Unit Test
    - 6 focused BE unit tests covering production TableReader integration, nested accessor safety, ARRAY child nullability, Bloom fallback counters, shared reads, and first-probe order
- Behavior changed: Yes. Eligible nested predicates can use Parquet Blooms without hiding required-child errors or reordering physical reads.
- Does this need documentation: No
@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28897 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 096a8af5237bad228bf4e0e974e588544ca13ad4, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17679	4041	4056	4041
q2	2054	322	198	198
q3	10219	1450	774	774
q4	4678	478	342	342
q5	7563	828	566	566
q6	185	173	145	145
q7	774	817	596	596
q8	9819	1672	1563	1563
q9	5708	4059	4047	4047
q10	6831	1663	1385	1385
q11	512	346	337	337
q12	746	570	460	460
q13	18116	3440	2763	2763
q14	272	259	253	253
q15	q16	732	725	663	663
q17	1039	935	1031	935
q18	6585	5636	5574	5574
q19	1231	1265	1098	1098
q20	797	689	586	586
q21	5661	2598	2268	2268
q22	446	359	303	303
Total cold run time: 101647 ms
Total hot run time: 28897 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4349	4282	4251	4251
q2	281	327	216	216
q3	4650	4921	4368	4368
q4	2170	2268	1417	1417
q5	4525	4181	4194	4181
q6	231	178	130	130
q7	1736	1596	1799	1596
q8	2649	2212	2179	2179
q9	7364	7261	7303	7261
q10	4229	4266	3916	3916
q11	587	414	383	383
q12	706	716	503	503
q13	3268	3740	2972	2972
q14	301	312	283	283
q15	q16	723	742	673	673
q17	1378	1339	1380	1339
q18	12311	11146	11869	11146
q19	1169	1128	1121	1121
q20	2242	2238	1940	1940
q21	5862	4835	4619	4619
q22	513	496	398	398
Total cold run time: 61244 ms
Total hot run time: 54892 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 165446 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 096a8af5237bad228bf4e0e974e588544ca13ad4, data reload: false

query5	4318	601	440	440
query6	474	212	218	212
query7	4886	576	327	327
query8	315	158	145	145
query9	8767	4048	4072	4048
query10	446	370	311	311
query11	5861	2212	2011	2011
query12	155	100	99	99
query13	1246	571	450	450
query14	6073	4320	3995	3995
query14_1	3798	3803	3786	3786
query15	196	202	175	175
query16	982	451	437	437
query17	896	682	525	525
query18	2425	462	342	342
query19	214	186	144	144
query20	103	100	101	100
query21	236	157	127	127
query22	13061	13069	12836	12836
query23	15925	15434	14726	14726
query23_1	14753	14739	14618	14618
query24	7636	1740	1245	1245
query24_1	1229	1251	1259	1251
query25	544	447	385	385
query26	1324	376	212	212
query27	2599	648	377	377
query28	4552	2102	2061	2061
query29	1089	614	503	503
query30	341	268	227	227
query31	1195	1132	1046	1046
query32	109	61	61	61
query33	543	305	251	251
query34	1175	1125	660	660
query35	732	741	646	646
query36	781	772	731	731
query37	151	106	95	95
query38	1833	1766	1691	1691
query39	827	827	797	797
query39_1	829	792	797	792
query40	251	165	151	151
query41	71	71	68	68
query42	95	95	97	95
query43	324	328	282	282
query44	1448	771	757	757
query45	186	175	174	174
query46	1104	1158	688	688
query47	1517	1550	1478	1478
query48	403	411	295	295
query49	598	417	301	301
query50	1036	451	338	338
query51	10350	10625	10322	10322
query52	88	90	77	77
query53	270	273	199	199
query54	289	245	229	229
query55	76	77	68	68
query56	320	295	301	295
query57	1023	997	938	938
query58	294	253	270	253
query59	1525	1613	1362	1362
query60	334	271	262	262
query61	175	171	173	171
query62	401	324	270	270
query63	243	199	199	199
query64	2998	997	820	820
query65	3868	3770	3793	3770
query66	1856	477	357	357
query67	28191	28228	27371	27371
query68	3114	1511	992	992
query69	419	314	251	251
query70	846	774	776	774
query71	388	340	296	296
query72	2969	2630	2273	2273
query73	854	790	456	456
query74	4635	4473	4321	4321
query75	2360	2344	2004	2004
query76	2327	1169	743	743
query77	332	366	279	279
query78	11053	11119	10487	10487
query79	1439	1174	783	783
query80	1270	523	457	457
query81	552	321	283	283
query82	675	170	130	130
query83	364	330	311	311
query84	321	159	131	131
query85	970	608	505	505
query86	411	230	215	215
query87	1998	1978	1838	1838
query88	3728	2824	2837	2824
query89	388	328	288	288
query90	1858	203	193	193
query91	202	185	168	168
query92	61	60	54	54
query93	1715	1547	1049	1049
query94	705	370	271	271
query95	757	584	482	482
query96	1125	768	367	367
query97	2486	2456	2328	2328
query98	193	187	184	184
query99	734	727	610	610
Total cold run time: 253487 ms
Total hot run time: 165446 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.12 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 096a8af5237bad228bf4e0e974e588544ca13ad4, data reload: false

query1	0.01	0.01	0.00
query2	0.10	0.05	0.05
query3	0.25	0.14	0.14
query4	1.62	0.14	0.14
query5	0.24	0.23	0.23
query6	1.17	0.81	0.83
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.37	0.32	0.33
query10	0.54	0.60	0.55
query11	0.19	0.14	0.14
query12	0.19	0.16	0.15
query13	0.46	0.47	0.47
query14	1.01	1.00	0.99
query15	0.61	0.59	0.58
query16	0.30	0.34	0.32
query17	1.06	1.10	1.14
query18	0.22	0.21	0.20
query19	2.05	1.99	1.93
query20	0.02	0.02	0.01
query21	15.43	0.21	0.14
query22	4.99	0.06	0.06
query23	16.11	0.32	0.12
query24	3.02	0.43	0.32
query25	0.12	0.05	0.04
query26	0.75	0.20	0.14
query27	0.03	0.04	0.03
query28	3.56	0.80	0.33
query29	12.52	4.04	3.22
query30	0.27	0.16	0.16
query31	2.77	0.55	0.32
query32	3.23	0.59	0.50
query33	3.24	3.19	3.30
query34	15.62	3.98	3.31
query35	3.26	3.27	3.28
query36	0.57	0.44	0.43
query37	0.09	0.07	0.06
query38	0.05	0.04	0.04
query39	0.04	0.03	0.02
query40	0.18	0.15	0.14
query41	0.08	0.04	0.04
query42	0.04	0.03	0.03
query43	0.05	0.04	0.03
Total cold run time: 96.53 s
Total hot run time: 24.12 s

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

Requesting changes. The Parquet nested-Bloom path is substantially corrected and the change is focused, but one P1 cross-format correctness issue remains: the new request-wide metadata-pruning safe prefix is not consumed by ORC SARG pruning, so ORC can suppress an earlier required error.

Critical checkpoint conclusions:

  • Goal and tests: Nested equality, non-NULL null-safe equality, and IN now reach the Parquet Bloom path with conservative physical-leaf and equality semantics. The goal is not fully achieved because the shared error-order fence is incomplete across format readers. The changed BE tests cover mapper localization, safe-prefix classification, leaf grouping, fallback counters, and hash edge cases, but no ORC regression proves an earlier throwing predicate survives a later SARG-negative equality.
  • Scope, minimality, and parallel paths: The implementation is concentrated in expression capability, table-to-file localization, request metadata, Parquet pruning/profile state, and focused tests. Parquet Statistics, Dictionary, Bloom, and PageIndex consumers honor the prefix; ORC is the one unmigrated pre-row metadata consumer.
  • Concurrency, lifecycle, and memory safety: No new thread, lock, atomic, shared mutable state, cross-TU static, or circular lifetime is introduced. Bloom state is scanner/Row-Group local, each reached physical leaf is read once, and only one decoded payload remains live while that leaf's probes run.
  • Error handling, data correctness, and nullable handling: Parquet metadata failures and unsupported types retain the Row Group, physical hashing covers Doris equality classes conservatively, and declared table-child nullability is checked before file filtering. The inline ORC path is the remaining correctness failure because stripe pruning can turn assert_true into a successful empty result.
  • Compatibility and non-applicable stateful surfaces: No configuration, storage/wire format, transaction, persistence, data-write, or FE/BE variable-propagation change is introduced.
  • Performance and observability: Same-leaf read sharing and bounded payload ownership are sound, and attempt/success/fallback/corruption counters are published for eager and deferred Parquet work. The remaining first-probe-order inefficiency is already covered by live discussion 3714634458 and is not duplicated here.
  • Test results: Static review only; this review contract forbids builds and test execution, so no test result is claimed and no result-file change applies.
  • User focus: review_focus.txt supplied no additional guidance, so the complete 21-file PR was reviewed without a narrower scope.

Completion and deduplication: all 11 live inline discussions were rechecked and no substantially similar comment is repeated. Two complete review rounds converged; every Round 2 normal and risk-focused reviewer returned NO_NEW_VALUABLE_FINDINGS for this exact one-comment set. The live base/head were reverified as 99dd2b330c32f903f39e4a9f1bda154544682298 / 096a8af5237bad228bf4e0e974e588544ca13ad4 immediately before submission.

VExprContextSPtrs conjuncts;
// Metadata pruning may use only this prefix. A later predicate must not jump over an earlier
// non-deterministic or error-preserving conjunct in the original row-level order.
size_t metadata_pruning_safe_conjunct_count = std::numeric_limits<size_t>::max();

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] Apply this safe prefix to ORC SARG pruning too

This request-wide contract is now honored by Parquet, but OrcReader::_init_search_argument_from_local_filters() still compiles every localized conjunct. For [assert_true(x <> 0, 'bad'), y = 7], this count is zero; the ORC compiler drops the unsupported assert_true yet emits y = 7, so a stripe containing (x=0, y=1) can be rejected before _execute_conjuncts() raises the required error. Please bound ORC SARG construction by min(metadata_pruning_safe_conjunct_count, conjuncts.size()) as well, and add an ORC regression with an earlier throwing predicate and later SARG-negative equality.

### What problem does this PR solve?

Issue Number: None

Related PR: apache#66423

Problem Summary: ORC built search arguments from every localized conjunct even when an earlier expression was unsafe for metadata pruning. Because ORC can omit unsupported expressions, a later pushdown predicate could prune every stripe and suppress a required row-level error. Limit SARG compilation to the request metadata-safe prefix so stripe pruning cannot cross the error-preserving barrier.

### Release note

Preserve row-level error behavior when ORC stripe pruning is enabled.

### Check List (For Author)

- Test: Unit Test
    - Focused ORC safe-prefix and stripe-pruning BE unit tests
- Behavior changed: Yes. ORC SARG pruning now stops at the metadata-safe conjunct prefix.
- Does this need documentation: No
@Gabriel39

Copy link
Copy Markdown
Contributor Author

run buildall

@Gabriel39

Copy link
Copy Markdown
Contributor Author

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

Request changes: two P1 correctness gaps remain in ARRAY predicate localization and its interaction with table-side constant pruning. Both can turn a required file-to-table nullability error into successful output or EOF.

Critical checkpoint conclusions:

  • Goal and proof: The change broadly reaches nested STRUCT/LIST equality, IN, and null-safe Bloom pruning, with shared physical-leaf reads and bounded error-preserving metadata prefixes. The goal is not yet complete because full ARRAY projections can hide an invalid sibling, and the new accessor-safe classification can let a later constant/default filter bypass a rejected accessor. The added unit tests do not cover either end-to-end sequence.
  • Scope and clarity: The implementation is focused on expression capability, format-v2 mapping, Parquet/ORC pruning, profiles, and their tests. The new contracts are mostly clear, but the mapper and TableReader do not carry the same materialization barrier through every early-pruning consumer.
  • Concurrency and thread safety: No new threads, shared cross-reader mutable state, locks, or lock ordering are introduced. Request, pruning, and profile state remain reader/scanner scoped.
  • Lifecycle and static initialization: Decoded Bloom ownership is bounded to one physical-leaf group at a time, request refresh occurs at reader boundaries, and eager/deferred counters have a single publication path. No new cross-TU static initialization or circular ownership issue was found.
  • Configuration: No configuration item is added, so dynamic-reload behavior is not applicable.
  • Compatibility: No storage, wire, function-symbol, or FE-BE protocol format changes are introduced. The internal FileScanRequest default preserves conservative behavior for callers that do not set the new prefix explicitly.
  • Parallel paths: Parquet Statistics, Dictionary, Bloom, and PageIndex pruning and ORC SARG construction all consume the bounded metadata prefix. Parquet and ORC row filtering were also traced. The remaining constant/default pruning gap is table-side and therefore bypasses those format-specific fences.
  • Conditions, errors, and nullability: The selected-chain nullability condition is insufficient when ARRAY access forces a full descendant projection. Separately, expression-totality is not enough to establish that mapping/materialization cannot fail later; both accepted comments provide concrete failure paths. No unchecked new Status, exception boundary, or const/nullable column-shape issue was found outside those paths.
  • Test coverage: The diff adds substantial BE unit coverage for extraction, mapping, safe prefixes, Parquet Bloom behavior, ORC SARG behavior, counters, and production accessor reachability. Missing negative coverage is precisely the unreferenced required sibling and the rejected accessor followed by a false missing/default-column predicate.
  • Test results: This review runner explicitly forbids builds and test execution, so no tests were run here. No regression result files are changed; the reviewed unit-test expectations are internally consistent but do not prove the two missing cases.
  • Observability: Bloom attempts, successes, conservative fallbacks, corrupt rejections, and eager/deferred publication are now represented. No new observability issue remains beyond already-known review threads.
  • Transactions, persistence, and writes: The PR changes read-side predicate/pruning behavior only; transactionality, EditLog replay, data-write atomicity, crash recovery, and MoW visibility are not applicable.
  • Transmitted variables: No new FE-BE or persisted variable is added.
  • Performance and memory: Physical-leaf grouping shares repeated probes and keeps one decoded Bloom payload live. The remaining first-probe ordering concern is already covered by existing thread r3714634458; no duplicate is submitted.
  • Other reviewed areas: Direct primitive ARRAY equality/IN reaches localization and Parquet leaf resolution; negative ARRAY indices remain a conservative optimization gap; fallback/profile, refresh, condition-cache, and aggregate-pushdown interactions yielded no additional distinct issue.

User focus: no additional user-provided review focus was supplied, so the complete PR was reviewed without a narrower focus.

Review completion: complete and converged. All three final-round reviewers returned NO_NEW_VALUABLE_FINDINGS for the same frozen two-comment set; every other candidate was independently dismissed or deduplicated against the 12 existing inline threads.

// Accessor results become nullable for missing ARRAY indices and NULL parents. Compare the
// file child with the declared table child instead, or that execution-only wrapper can
// hide a nullable-file-to-required-table contract violation before alignment reports it.
if (!can_filter_before_table_nullability_alignment(resolved.file_child_types[idx],

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] Validate every child in an ARRAY's full projection

These checks cover only the selected accessor chain, but ARRAY indexing requests project_all_children, so TableReader later aligns unreferenced siblings too. For table items ARRAY<STRUCT<a Nullable(INT), b INT NOT NULL>>, file items ARRAY<STRUCT<a Nullable(INT), b Nullable(INT)>>, rows [{a=0,b=NULL}] and [{a=20,b=1}], and items[1].a > 5, this guard sees compatible ARRAY/element/a types and localizes the filter. The file reader then discards the first row before required sibling b is aligned, changing the schema error into successful output. Please validate every projected descendant before localizing a full ARRAY projection (or keep the filter table-level), and add an end-to-end sibling-nullability regression.

"not_in",
"is_null_pred",
"is_not_null_pred",
"element_at",

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] Fence constant pruning after a rejected accessor

Adding element_at here also keeps the table-side constant-pruning prefix open. For table items ARRAY<STRUCT<a INT NOT NULL>>, z INT DEFAULT 0, a file containing only items ARRAY<STRUCT<a Nullable(INT)>>, predicates [items[1].a > 10, z = 7], and a row with a=NULL, the mapper correctly leaves the first predicate above TableReader so required-child alignment can report the schema error. But missing z is a constant/default filter entry, so _evaluate_constant_filters() skips the nonconstant first filter, evaluates z=7 as false, and closes the split before any row reaches alignment. Before this accessor was classified safe, the first conjunct closed that prefix. Please propagate a rejected mapping/materialization barrier into constant pruning too, and add a missing/default-column regression for this order.

@Gabriel39
Gabriel39 marked this pull request as draft August 5, 2026 04:13
Gabriel39 added a commit to Gabriel39/incubator-doris that referenced this pull request Aug 5, 2026
Issue Number: None

Related PR: apache#66423

Problem Summary: Production nested accessors and null-safe equality were excluded from the metadata-pruning safe prefix, ARRAY accessor result nullability could hide a required table child, physical-leaf grouping could reorder large Bloom reads, and unavailable Bloom payloads were not observable. Classify only total accessor shapes, retain declared table child types during localization, preserve first-probe order with one decoded payload at a time, and publish separate attempt, success, fallback, and corruption counters.

Make nested Parquet Bloom pruning reachable for production accessors while preserving schema validation, probe order, and observable conservative fallbacks.

- Test: Unit Test
    - 6 focused BE unit tests covering production TableReader integration, nested accessor safety, ARRAY child nullability, Bloom fallback counters, shared reads, and first-probe order
- Behavior changed: Yes. Eligible nested predicates can use Parquet Blooms without hiding required-child errors or reordering physical reads.
- Does this need documentation: No
Gabriel39 added a commit to Gabriel39/incubator-doris that referenced this pull request Aug 5, 2026
### What problem does this PR solve?

Issue Number: None

Related PR: apache#66423

Problem Summary: ORC built search arguments from every localized conjunct even when an earlier expression was unsafe for metadata pruning. Because ORC can omit unsupported expressions, a later pushdown predicate could prune every stripe and suppress a required row-level error. Limit SARG compilation to the request metadata-safe prefix so stripe pruning cannot cross the error-preserving barrier.

### Release note

Preserve row-level error behavior when ORC stripe pruning is enabled.

### Check List (For Author)

- Test: Unit Test
    - Focused ORC safe-prefix and stripe-pruning BE unit tests
- Behavior changed: Yes. ORC SARG pruning now stops at the metadata-safe conjunct prefix.
- Does this need documentation: No
Gabriel39 added a commit to Gabriel39/incubator-doris that referenced this pull request Aug 6, 2026
Issue Number: None

Related PR: apache#66423

Problem Summary: Production nested accessors and null-safe equality were excluded from the metadata-pruning safe prefix, ARRAY accessor result nullability could hide a required table child, physical-leaf grouping could reorder large Bloom reads, and unavailable Bloom payloads were not observable. Classify only total accessor shapes, retain declared table child types during localization, preserve first-probe order with one decoded payload at a time, and publish separate attempt, success, fallback, and corruption counters.

Make nested Parquet Bloom pruning reachable for production accessors while preserving schema validation, probe order, and observable conservative fallbacks.

- Test: Unit Test
    - 6 focused BE unit tests covering production TableReader integration, nested accessor safety, ARRAY child nullability, Bloom fallback counters, shared reads, and first-probe order
- Behavior changed: Yes. Eligible nested predicates can use Parquet Blooms without hiding required-child errors or reordering physical reads.
- Does this need documentation: No
Gabriel39 added a commit to Gabriel39/incubator-doris that referenced this pull request Aug 6, 2026
### What problem does this PR solve?

Issue Number: None

Related PR: apache#66423

Problem Summary: ORC built search arguments from every localized conjunct even when an earlier expression was unsafe for metadata pruning. Because ORC can omit unsupported expressions, a later pushdown predicate could prune every stripe and suppress a required row-level error. Limit SARG compilation to the request metadata-safe prefix so stripe pruning cannot cross the error-preserving barrier.

### Release note

Preserve row-level error behavior when ORC stripe pruning is enabled.

### Check List (For Author)

- Test: Unit Test
    - Focused ORC safe-prefix and stripe-pruning BE unit tests
- Behavior changed: Yes. ORC SARG pruning now stops at the metadata-safe conjunct prefix.
- Does this need documentation: No
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants