Skip to content

branch-4.2 [improvement](meta path) Port nested column pruning / meta path series - #68214

Open
englefly wants to merge 9 commits into
apache:branch-4.2from
englefly:pick-metapath-4.2
Open

englefly wants to merge 9 commits into
apache:branch-4.2from
englefly:pick-metapath-4.2

Conversation

@englefly

Copy link
Copy Markdown
Contributor

Cherry-pick of #62205, #62315, #62631, #62304, #63229, #63736, #64486 to branch-4.2 (the meta path series, applied in merge order).

Adaptations for branch-4.2:

  • AccessPathInfo already lives in org.apache.doris.analysis on this branch; the new ACCESS_STRING_OFFSET constant was ported there (aliasing the existing ACCESS_OFFSET).
  • master-only types renamed to this branch's thrift types (ColumnAccessPath* -> TColumnAccessPath, ColumnAccessPathType -> TAccessPathType); NestedColumnPruning keeps branch-4.2's comparePathSegments/variant branch alongside upstream's new logic.
  • LazyMaterializeTopN merges upstream's restructure while preserving branch-4.2's Lance lazy-materialization gate; LogicalJoin.getRightConditionSlot() (a master-only helper) had to be added for the ported PushDownTopNThroughJoin.
  • StringEmptyToLengthRuleTest uses ElementAt (branch-4.2 folded StructElement into ElementAt); the regression suite expectation for the same reason.

REQUIRED EXTRA COMMIT: the FE now emits offset-only / null-only nested access paths, which branch-4.2's BE could not read — null_column_pruning.groovy aborted the BE with std::vector<...ColumnIterator>::operator[]: Assertion '__n < this->size()' failed. This PR therefore also cherry-picks the BE-side prerequisite #61888 "Exec Support offset prue column and null column in BE" (adapted to branch-4.2's IColumn::mutate/Defer COW shape and ColumnNullable::get_null_map_column_ptr() signature). Please review that extra commit carefully.

Testing on branch-4.2 (FE + BE rebuilt from this branch):

  • FE unit tests: PruneNestedColumnTest (57), PullUpProjectExprUnderTopNTest (28), OperativeColumnDeriveTest (4), StringEmptyToLengthRuleTest (11), MaterializeProbeVisitorTest (8) — 108 tests, 0 failures.
  • Regression: nereids_rules_p0/column_pruning (7 suites incl. the new string_length/null/nested_container_offset/topn_expr_pullup/topn_lazy_nested suites) 0 failures; shape_check clickbench/tpcds_sf100/tpcds_sf1000/tpcds_sf10t_orc/tpcds_sf1000_constraints suites touched by the series regenerated with -forceGenOut and green; nereids_rules_p0 push_filter_through / null_un_safe_equals / lazy_materialize_topn green.
  • Known deviations: [fix](fe) Fix struct field slot type in NestedColumnPruning for OFFSET-only access #62446 (struct field slot type for OFFSET-only access) is not part of the requested series and was not included; the BE-side MapAccessAllWithOffsetDoesNotPropagateOffsetToKey unit test from [Exec](be) Support offset prue column and null column in BE #61888 was kept byte-identical to upstream although it may not hold (test binary not built with MAKE_TEST=ON).

englefly and others added 9 commits September 18, 2026 22:04
…t sub column (apache#62205)

### What problem does this PR solve?
Optimized the calculation of length(str_col).
Treat the string column as a combination of an offset sub column and a
chars sub column.
Prune the string column via NestedColumnPruning so that the BE only
needs to read the offset sub column, thereby saving I/O for reading the
chars sub column.
… non-SlotReference string expressions (apache#62315)

…

### What problem does this PR solve?

Issue Number: close #xxx

Problem Summary: StringEmptyToLengthRule only rewrites `str_col = ''` to
`length(str_col) = 0` when the non-literal side is a direct
SlotReference. This means expressions like `element_at(struct_col, 'f3')
= ''` (which becomes `struct_element(struct_col, 'f3') = ''` after
analysis) are not rewritten, preventing the OFFSET-only column reading
optimization from applying to struct string fields.

### Release note

StringEmptyToLengthRule now rewrites any string-typed expression
compared against an empty string literal, not just direct column
references. This enables the OFFSET optimization for struct field access
patterns like `element_at(struct_col, 'f3') = ''`.
…array<struct> (apache#62631)

### What problem does this PR solve?

Issue Number: close #xxx

Related PR: apache#62205

Problem Summary:

PR apache#62205 introduced the `length(str)` OFFSET optimization for nested
column pruning. When `length()` is applied to a string-like nested
field, the pruner marks the access path with an OFFSET suffix so BE can
read only the offset array instead of full element data.

However, the OFFSET path dedup logic operates at **slot granularity**:
if ANY non-OFFSET path exists for the same slot, ALL OFFSET paths are
stripped. This is incorrect for `array<struct<...>>` columns where
different struct fields have independent access patterns.

**Bug 1 (crash):** Given a query like:
```sql
SELECT array_match_all(x -> length(struct_element(x, 'str_field')) > 0, arr),
       struct_element(element_at(arr, 1), 'int_field')
FROM t
```
The `int_field` non-OFFSET path causes the `str_field` OFFSET path to be
stripped. BE never reads `str_field` data → crash or wrong results.

**Fix:** Replace slot-level OFFSET dedup with per-field bidirectional
prefix matching. An OFFSET path `P+["OFFSET"]` is only stripped when a
non-OFFSET path Q shares a prefix relationship with P (covering the same
container or ancestor), not when Q accesses a sibling struct field.

**Bug 2 (wrong pruning):** When `isStringOffsetOnly=true` AND
`accessPartialChild=true` (e.g., `cardinality(arr) + arr[*].f1`),
`pruneDataType()` returns the full type instead of pruning unused
fields.

**Fix:** Add `!accessPartialChild` guard to the `isStringOffsetOnly`
check.

### Release note

Fix a BE crash caused by nested column pruning incorrectly stripping
OFFSET access paths for array<struct> columns when different struct
fields have independent access patterns (e.g., length() on one field +
direct access on another).
…dicate (apache#62304)

### What problem does this PR solve?
Treat nullable fields as a combination of a nullable flag and data. When
evaluating the `col IS NULL` predicate, use the NestedColumnPruning rule
to prune the col field to col.NULL, thereby saving I/O on the data.
…type when its children are accessed. (apache#63229)

### What problem does this PR solve?

Issue Number: close #xxx

Related PR: apache#62205

Problem Summary: cardinality/map_size on element_at(map, key) was
collected as an OFFSET-only access path. element_at(map, key) still
needs map keys for lookup, and pushing a nested *.OFFSET predicate path
can make BE route OFFSET to an array item child and fail with an invalid
access path. Fall back to normal element access for these expressions
while preserving OFFSET-only optimization for direct array/map
cardinality.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - tools/fast-compile-fe.sh
- FE_UT_PARALLEL=0 ./run-fe-ut.sh --run
org.apache.doris.nereids.rules.rewrite.PruneNestedColumnTest#testCardinalityMapElementDoesNotUseOffsetPath
- FE_UT_PARALLEL=0 ./run-fe-ut.sh --run
org.apache.doris.nereids.rules.rewrite.PruneNestedColumnTest#testStructRootMapMixedAccessKeepsKeysPath+testCardinalityMapElementDoesNotUseOffsetPath
    - cd fe && mvn checkstyle:check -pl fe-core -q
    - ./build.sh --fe
- Behavior changed: No
- Does this need documentation: No

### What problem does this PR solve?

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

### Release note

None

### Check List (For Author)

- Test <!-- At least one of them must be included. -->
    - [ ] Regression test
    - [ ] Unit Test
    - [ ] Manual test (add detailed scripts or steps below)
    - [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
        - [ ] Previous test can cover this change.
        - [ ] No code files have been changed.
        - [ ] Other reason <!-- Add your reason?  -->

- Behavior changed:
    - [ ] No.
    - [ ] Yes. <!-- Explain the behavior change -->

- Does this need documentation?
    - [ ] No.
- [ ] 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: Copilot <223556219+Copilot@users.noreply.github.com>
…/variant nested column pruning (apache#63736)

Extend TopN lazy materialization to defer reading complex-type base
columns
(struct, variant, map, array) until after TopN filtering, and expand the
scope to all non-trivial projection expressions.

## Core Changes

**PullUpProjectExprUnderTopN** (new CustomRewriter):
- Two-pass design: Collector walks the plan tree top-down to find
qualifying
TopNs, then walks into descendants (through Join/Filter) to find
Projects
with pull-able expressions. Replacer simplifies found Projects bottom-up
  and adds upper Projects to restore pulled-up expressions.
- Eligible expressions: Alias with non-trivial child (not Slot/Literal),
  not referenced by TopN order keys, no NoneMovableFunction.
- Excludes: CTE Producers (output mapping safety), Join/Filter
conditions
  that reference pulled-up outputs (cleared/removed).

**LazyMaterializeTopN** (simplified):
- Expression pull-up moved from physical PlanPostProcessor to logical
CustomRewriter, eliminating hard-coded
`MERGE_SORT→Distribute→LOCAL_SORT→Project`
  shape walking. Now only handles MaterializeNode insertion.

**OperativeColumnDerive**:
- Skip PreferPushDownProject input slots from operative propagation so
  complex-type base columns can be lazy.

**Other**:
- `PhysicalLazyMaterialize`: propagate access paths to lazy output slots
  for nested column/subPath pruning on BE.
- `MaterializationNode`/`PlanNode`: fix nested column display in
EXPLAIN.
- `NoneMovableFunction`: fix missing interface name.
- Session variable `enable_topn_expr_pullup` for rollback.

**Tests**:
- `topn_expr_pullup`: 15 test cases covering struct/variant/map/array,
non-PPD
  expressions, joins, column order preservation, negative cases.
- `topn_lazy_nested_column_pruning`: 17 test cases for struct/variant
nested
  pruning + map/array lazy mat + multi-level variant nesting.
- Updated 48 shape_check .out files to reflect new plan shapes.

### What problem does this PR solve?

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

### Release note

None

### Check List (For Author)

- Test <!-- At least one of them must be included. -->
    - [ ] Regression test
    - [ ] Unit Test
    - [ ] Manual test (add detailed scripts or steps below)
    - [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
        - [ ] Previous test can cover this change.
        - [ ] No code files have been changed.
        - [ ] Other reason <!-- Add your reason?  -->

- Behavior changed:
    - [ ] No.
    - [ ] Yes. <!-- Explain the behavior change -->

- Does this need documentation?
    - [ ] No.
- [ ] 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: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rUnderTopN with chained expressions (apache#64486)

### What problem does this PR solve?
this pr refactor PullUpProjectExprUnderTopN to avoid slot-not-found error.
in this version, pullup is done in bottom up algorithm. it makes pullup simpler than previous version

Issue Number: close #xxx

Related PR: apache#63736

Problem Summary:

### Release note

None

### Check List (For Author)

- Test <!-- At least one of them must be included. -->
    - [ ] Regression test
    - [ ] Unit Test
    - [ ] Manual test (add detailed scripts or steps below)
    - [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
        - [ ] Previous test can cover this change.
        - [ ] No code files have been changed.
        - [ ] Other reason <!-- Add your reason?  -->

- Behavior changed:
    - [ ] No.
    - [ ] Yes. <!-- Explain the behavior change -->

- Does this need documentation?
    - [ ] No.
- [ ] 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 -->
apache#61888)

### What problem does this PR solve?
Problem Summary: This PR includes two main changes:
Add offset-only read optimization support for string, array, and map
types in column reader

### Release note
- [Storage] Add offset-only read optimization for complex types (string,
array, map) to improve read performance
### Check List
- Test: BE unit tests passed
- Behavior changed: No (materialization fix prevents silent failures,
now returns error explicitly)
- Does this need documentation: No

---------

Co-authored-by: englefly <englefly@gmail.com>
… lazy nested pruning suite

branch-4.2 prints the merged element_at function as `element_at` (StructElement was
folded into ElementAt on this branch), so the expected materialize projections use
`substring(element_at(struct_col[apache#2], 'city'), 1, 2147483647)`.
@englefly
englefly requested a review from yiguolei as a code owner September 18, 2026 17:37
@englefly

Copy link
Copy Markdown
Contributor Author

run buildall

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

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 71.99% (753/1046) 🎉
Increment coverage report
Complete coverage report

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants