Skip to content

[fix](adbc) Materialize Arrow list view arrays - #66819

Open
Gabriel39 wants to merge 5 commits into
apache:masterfrom
Gabriel39:fix/adbc-list-view-materialization
Open

[fix](adbc) Materialize Arrow list view arrays#66819
Gabriel39 wants to merge 5 commits into
apache:masterfrom
Gabriel39:fix/adbc-list-view-materialization

Conversation

@Gabriel39

@Gabriel39 Gabriel39 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

ADBC maps Arrow ListView and LargeListView fields to Doris arrays, but these encoding variants must be converted to canonical lists before materialization. Incremental child-builder growth can also cause avoidable allocation spikes, and nested views can overflow before a parent builder reports capacity. A source schema can also become nullable while a cached Doris target remains non-nullable.

What is changed and how does it work?

  • Validate ListView ranges before copying child values.
  • Preflight the total logical child length before any builder mutation and reject offset overflow.
  • Reject nested ListView encodings before allocation until recursive canonicalization is supported.
  • Reserve the expanded child capacity once before appending values.
  • Route all normalization allocations through a caller-owned Doris-tracked Arrow memory pool.
  • Reject runtime null rows when the cached Doris target is non-nullable, preventing null arrays from becoming empty arrays.
  • Add allocation-peak, no-allocation overflow, nullable-slice, malformed-range, required-target, and ADBC materialization regressions.

Check List

  • Arrow normalizer and ADBC reader ASAN tests (25 tests)
  • clang-format 16

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

Automated review result: request changes

The review converged after two rounds, each with full-scope and risk-focused review coverage. Three blocking findings are covered by two inline comments:

  1. Arrow 24's FromListView misreads validity for nullable nonzero-offset slices, which can silently flip valid/null rows.
  2. Malformed list-view ranges are copied before Doris's default-on Arrow validation runs, allowing an out-of-bounds child-buffer read instead of a clean error.
  3. Canonicalization allocates a potentially amplified full logical-size intermediate from Arrow's untracked default pool, bypassing query memory limits while Doris copies the values again.

Critical checkpoint conclusions:

  • Goal and correctness: the offset-zero reordered/shared-range cases work for both widths, but the valid-slice corruption and pre-validation read mean the ADBC materialization goal is not safely complete.
  • Scope and parallel paths: the change is small and localized, and both LIST_VIEW and LARGE_LIST_VIEW are wired consistently. The same three mechanisms affect both widths and both query/partition streams that feed the shared ADBC materialization path.
  • Concurrency and lifecycle: normalization is synchronous and per-call, with no new shared mutable state, locks, threads, atomics, static initialization dependency, or ownership cycle. An explicit caller-supplied ExecEnv Arrow pool is lifecycle-safe here because the temporary is destroyed before the synchronous materialization call returns.
  • Configuration and error handling: no new configuration is added and both Arrow Results are checked, but the existing default-on enable_arrow_input_validation gate currently runs only after this conversion, which is too late for malformed ranges.
  • Compatibility, persistence, and writes: there is no FE-BE protocol, storage format, persisted metadata, transaction, data-write, failover, or rolling-upgrade change.
  • Performance and memory: conversion is O(rows + logical child values), but shared ranges can greatly amplify the intermediate. Keeping that buffer live through the second Doris copy makes tracked allocation, or direct SerDe materialization, necessary.
  • Tests and results: the new unit tests are registered and their expected reordered values are correct, but they omit nullable slices, malformed ranges, pool accounting, and a ListView-through-AdbcFileReader case. The review contract forbids local builds; the PR reports ASAN normalizer/ADBC unit runs. Clang Formatter passed, while compile and BE UT were still pending at review time. The external performance check failed without accessible evidence tying it to this patch.
  • Observability: the existing AdbcNormalizeTime profile timer covers the new CPU work; no additional log or metric issue remains once memory accounting is fixed.
  • Additional user focus: none was provided; the complete PR was reviewed.
  • Dismissed scope point: nested encoding-only children remain a documented pre-existing top-level-only limitation and still fail loudly, so no duplicate changed-code finding was submitted for that case.

Review completion: complete and converged. Every candidate is accepted in the inline set or dismissed with concrete evidence; no unresolved suspicious point remains.

// List views may share or reorder value ranges, so rebuild canonical offsets instead of
// exposing their buffers to a serde that requires contiguous list values.
if (type.id() == arrow::Type::LIST_VIEW) {
auto converted = arrow::ListArray::FromListView(

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] Avoid this unsafe Arrow 24 conversion path

This API is unsafe for two inputs ADBC can deliver. On a nullable slice, Arrow 24 calls GetValues<uint8_t>(0) (which already advances by ArrayData::offset bytes) and then GetBit(..., offset + i), so a nonzero offset reads the wrong validity bit and can silently flip null rows. On a malformed range, ListFromListViewImpl calls AppendArraySlice(values, offset, size) before Doris's default-on SerDe validation, and the numeric builder copies without checking offset + size <= values.length(), allowing an out-of-bounds read. C-stream import preserves child offsets and does not ValidateFull. Please validate/rebase the source or use a corrected conversion, and add nullable-slice and out-of-range tests for both widths.

if (type.id() == arrow::Type::LIST_VIEW) {
auto converted = arrow::ListArray::FromListView(
static_cast<const arrow::ListViewArray&>(*current),
arrow::default_memory_pool());

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] Route the canonicalization allocation through Doris tracking

FromListView computes SumOfLogicalListSizes, reserves that full child capacity, and copies each logical range from this pool. With N rows sharing an M-element range, a small M-element physical child creates an NM intermediate, which remains live while the Doris array SerDe copies NM values again. Arrow's default system pool is not Doris's ArrowMemoryPool, and no repository override connects it to the thread/query MemTracker, so a large batch bypasses query limits and can exhaust process memory. Please pass an explicit caller-owned tracked pool (for example, the ADBC runtime's ExecEnv pool, whose lifetime encloses out) for both widths; direct SerDe materialization could also remove the extra peak.

@Gabriel39
Gabriel39 requested a review from yiguolei as a code owner August 17, 2026 03:47
@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.

Automated review result: request changes

The review converged after two rounds of full-scope and risk-focused coverage. The current head fixes the two prior inline threads by validating ListView ranges before copying, preserving sliced validity correctly, and routing all conversion allocations through a caller-owned Doris pool. Two residual blocking findings are covered by the new inline comments:

  1. Aggregate logical length is not checked before Arrow 24 child builders copy shared ranges, allowing signed overflow before the parent list builder's late capacity check; nested view builders require the same protection.
  2. The manual conversion reserves only parent rows, omitting the exact child reserve used by Arrow's conversion and creating an avoidable geometric tracked-memory peak that can reject a valid batch.

Critical checkpoint conclusions:

  • Goal and correctness: valid ordinary ListView/LargeListView inputs now preserve logical order, nulls, slices, and malformed-range failure, but the unchecked aggregate and allocation-growth cases mean safe ADBC materialization is not complete.
  • Scope and focus: the production change is localized to the normalizer and its sole ADBC caller. The signature change reaches every caller, and no unrelated source change is present.
  • Concurrency and lifecycle: normalization state is per call. Concurrent readers share only atomic pool accounting; there is no new lock order or mutable reader state. The process pool outlives outputs, and the fallback pool is destroyed only after each normalized array is synchronously copied and released.
  • Error handling: validation, reserve, append, finish, cast, and SerDe statuses are consumed, and existing exception boundaries convert Doris allocation failures. The remaining signed overflow occurs before those status checks can protect the path.
  • Configuration and compatibility: no configuration, protocol, storage-format, persisted-state, transaction, data-write, FE/BE variable, failover, or rolling-upgrade surface changes.
  • Parallel paths: Remote Doris, Paimon/C Data, IPC, and generic Arrow materializers do not use this ADBC-specific helper and have distinct producer contracts; no parallel-path update is required.
  • Performance and memory: the supplied pool repairs untracked allocation, but exact checked child reservation is still needed to avoid the new manual loop's false memory-limit failure.
  • Tests and results: changed tests cover both offset widths, reordered/shared ranges, nullable slices, invalid individual ranges, caller-pool use, and 32-bit ADBC-to-Doris materialization. They omit the no-allocation aggregate-overflow and geometric-growth regressions in the inline findings. Per the review contract, no local build or test was run. Clang Formatter, CheckStyle, license, and related static checks pass. macOS BE UT fails during unrelated CMake configuration on a missing unity-skip source before reaching changed code; TeamCity BE UT exposes no accessible PR-specific evidence, while compile and performance checks remain pending.
  • Observability: the existing AdbcNormalizeTime profile timer covers the added CPU work; no extra log or metric issue was established.
  • Existing-thread and scope suppression: neither new comment repeats the fixed unsafe-FromListView or untracked-default-pool threads. The documented nested encoding-only SerDe limitation was previously dismissed and is not re-raised; nested builders are mentioned only where they reproduce the accepted pre-SerDe overflow mechanism.
  • Additional user focus: none was provided; the complete authoritative diff was reviewed.

Review completion: complete and converged. Every candidate is accepted in the two-comment inline set, merged into those findings, or dismissed with evidence; no unresolved suspicious point remains.

}
std::shared_ptr<arrow::ArrayBuilder> child_builder(child_builder_result.MoveValueUnsafe());
ListBuilder builder(pool, child_builder);
auto reserve_status = builder.Reserve(source.length());

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] Reserve the expanded child before the append loop

Only the parent slots are reserved, so the child grows geometrically while shared ListView ranges are expanded. For example, 1,025 rows viewing the same 100,000 Int32 values produce a roughly 391 MiB value buffer, but Arrow's final doubling reallocates it from about 391 MiB to 781 MiB; Doris charges the new allocation before releasing the old one, transiently accounting roughly 1.17 GiB for that buffer alone. An exact reserve keeps the canonical and Doris value buffers near 782 MiB combined, leaving roughly 391 MiB less required headroom (other common query allocations aside). After validation, please compute a checked non-null logical total and reserve it on child_builder before this loop.

if (!append_status.ok()) {
return append_status;
}
append_status = child_builder->AppendArraySlice(values, source.value_offset(i),

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] Check aggregate lengths before copying into builders

A valid LargeListView<Null> can reach signed overflow here without allocating a huge buffer: use a Null child of length INT64_MAX and two shared ranges of length INT64_MAX - 1. ValidateFull() accepts both ranges independently, but Arrow 24's NullBuilder::AppendArraySlice performs unchecked length_ += length, so the second append overflows before LargeListBuilder checks capacity. Checking only this source's top-level total is not enough either: an outer large_list_view<large_list_view<null>> can have total 2 while the nested builder overflows on the same ranges during this call. Please compute checked non-null logical totals before any append at every copied view level, or reject nested view children before building, and add a no-allocation regression test.

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: This content was flagged for possible cybersecurity risk. If this seems wrong, try rephrasing your request. To get authorized for security work, join the Trusted Access for Cyber program: https://chatgpt.com/cyber
Workflow run: https://github.com/apache/doris/actions/runs/31992276202

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

morningman added a commit that referenced this pull request Aug 17, 2026
…count probe (FE) (#66831)

### What

Two independent breakages that each make **current master fail to
build** — one in BE configure, one in FE compile.

They share a shape: a pair of PRs that never conflict textually, merge
cleanly, and only break once combined, so each PR's own pipeline was
green.

| | Breakage | Colliding PRs |
|---|---|---|
| BE | `cmake` configure aborts | #66052 moved a file, #66789 made a
dangling unity-skip entry fail loud |
| FE | `fe-connector-iceberg` does not compile | #66778 deleted
`getCountFromSnapshot()`, #66413 added a caller for it |

CI merges each PR into the latest master before building, so **every PR
pipeline that picks up current master is red** on one or both.

---

## 1. BE — stale `STORAGE_UNITY_SKIP` entry for a moved file

Remove the stale `STORAGE_UNITY_SKIP` entry (and its comment block) for
`compaction/collection_statistics.cpp`, which no longer exists.

### Why — master configure is currently broken

#66052 moved `storage/compaction/collection_statistics.{cpp,h}` to
`storage/index/inverted/similarity/` (rewritten), but left behind the
unity-skip entry that #66789 had added for the old path. The fail-loud
validation introduced by #66789 turns a dangling skip entry into a
configure-time error — which is exactly what it is designed to catch (a
skip list rotting after a file move), so BE configure on current master
fails immediately:

```
CMake Error at CMakeLists.txt:1002 (message):
  unity skip entry does not exist (renamed or moved?):
  .../be/src/storage/compaction/collection_statistics.cpp
```

#66826, #66824, #66819, #66820 were the first hits — same error on
multiple independent agents.

### Why deletion (not a path update) is correct

The old entry existed because the old `collection_statistics_test`
`#include`d the `.cpp` into a second TU (unity batching would then
produce a duplicate definition at link time). The rewritten file at the
new location is not `#include`d by any test (`grep -rn
'collection_statistics.cpp' be/test/` is empty on master), so the new
path needs no skip entry.

### Verification

- Full BE build from a clean tree at master + this change (clang20 /
macOS arm64, unity=ON, PCH=ON): configure passes the skip-list
validation and the build compiles. (The same tree without this change
fails configure with the error above.)
- Timeline note: #66052's last green CI round presumably predates
#66789's validation landing (2026-08-16), which is how the dangling
entry slipped through.

---

## 2. FE — the metadata-only COUNT(\*) probe calls a deleted method

#66778 replaced the snapshot-summary COUNT(\*) pushdown with a
manifest-derived count and deleted `getCountFromSnapshot()`.
`canServeMetadataOnlyCount()`, added by #66413, still calls it, so FE
compilation fails:

```
[ERROR] .../connector/iceberg/IcebergScanPlanProvider.java:[505,16] cannot find symbol
[ERROR]   symbol:   method getCountFromSnapshot(org.apache.iceberg.TableScan,org.apache.doris.connector.spi.ConnectorSession)
[ERROR]   location: class org.apache.doris.connector.iceberg.IcebergScanPlanProvider
```

### Why re-express the probe instead of restoring the method

Bringing `getCountFromSnapshot()` back would reintroduce precisely what
#66778 removed: a query result derived from optional, writer-provided
snapshot summary fields.

The probe is rebuilt in #66778's own terms instead. It reuses that PR's
delete gate and additionally requires the data manifests to carry
aggregate row counters, so the answer is proved from the manifest list
alone — `O(manifests)`, no data-file enumeration, which is what a probe
running before planning can afford.

Manifest lists that omit those aggregates now answer `false`, where
count planning still serves them through its bounded per-file fallback.
A capability probe should under-promise rather than over-promise; the
planner itself is untouched.

### Verification

- `mvn package` over the full FE reactor (74 modules): all green. This
also confirms no second semantic break is hiding behind the first — CI's
maven stops at `fe-connector-iceberg` and never reaches the rest.
- `IcebergScanPlanProviderTest`: 153 tests, 0 failures, including a new
case pinning that the probe follows the same delete gate as count
planning.
- FE checkstyle: 0 violations.

### Release note

None (both fixes only restore a buildable master; no user-visible
behavior change).

### Check List (For Author)

- Test
- [x] Regression test — not applicable: neither change alters query
behavior
  - [x] Unit test: `IcebergScanPlanProviderTest` (153 tests, 0 failures)
  - [x] Manual test: full BE configure/build and full FE reactor build
- Behavior changed: No
- Does this need documentation: No

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@Gabriel39
Gabriel39 force-pushed the fix/adbc-list-view-materialization branch from ab92556 to 1043bd1 Compare August 18, 2026 01:59
@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: one new, non-duplicate P1 correctness finding is attached inline. A nullable runtime ListView can silently become [] when the FE-planned ARRAY slot is required. Existing threads already cover malformed/sliced conversion, tracked allocation, expanded-child reservation, and aggregate capacity; I did not repeat them.

Critical checkpoint conclusions:

  • Goal, scope, and tests: The focused normalizer implements top-level ListView/LargeListView canonicalization, and the added BE unit tests correctly cover logical order, both offset widths, sliced validity, malformed ranges, pool routing, nested-view rejection, and one nullable ADBC happy path. They do not cover the required-root mismatch in the inline finding.
  • Correctness, nullability, and error handling: Supported nullable shapes preserve row count, order, child values, and parent validity, and Arrow/SerDe errors are propagated. The accepted finding is the remaining silent-error path: a valid runtime null loses its validity bit in a non-nullable ARRAY SerDe and becomes an empty value.
  • Memory, lifecycle, and performance: The process pool outlives readers; fallback-pool locals outlive every normalized array; SerDes retain no Arrow buffers; allocations remain tracker-charged and unwind on all exits. The two remaining builder capacity/reservation concerns are already covered by live inline threads and were duplicate-suppressed here.
  • Concurrency and parallel paths: Builders and normalized arrays are call-local, the shared pool's statistics are atomic, and no new locking or shared mutable state is introduced. Ordinary List/LargeList materialization exposes the same target-nullability boundary, so the fix belongs at the shared ADBC materialization check.
  • Compatibility, configuration, and persistence: No configuration, FE-BE protocol, storage format, transaction, persistence, or data-write behavior changes. This is a read-path encoding compatibility change only.
  • Observability: Existing ADBC normalization/materialization timers cover the new work; failures already propagate with Arrow type context, and the proposed guard should add the column context. No additional metric or log is needed for the accepted fix.
  • Test execution: Review-only instructions prohibited local builds and tests. The test code and expected assertions were reviewed; at submission time the macOS BE UT check is still in progress.
  • User focus: No additional user-provided review focus was supplied.

Completion: converged after two rounds. Every subagent returned NO_NEW_VALUABLE_FINDINGS; all candidates were independently verified and either accepted, duplicate-suppressed, or dismissed with code evidence. This review is complete.

{
SCOPED_TIMER(_normalize_time);
RETURN_IF_ERROR(normalize_arrow_array(batch.column(arrow_idx), &array));
RETURN_IF_ERROR(normalize_arrow_array(batch.column(arrow_idx), arrow_pool, &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.

[P1] Reject null ListView rows for required ARRAY slots

The file schema here is synthesized from cached FE slots, not the imported batch schema. If the source changes this field from required to nullable while the 10-minute ADBC schema cache is live, the runtime batch can validly contain a null ListView while target_type is still non-nullable. Normalization preserves that null, but _materialize_arrow_column then calls DataTypeArraySerDe directly; it consumes only offsets, so the repeated offset for the null row is silently materialized as []. Before this change the same ListView failed as unsupported. Please reject array->null_count() > 0 for non-nullable targets before SerDe (with column context), and add an end-to-end required ARRAY test containing a null ListView row (and LargeListView if both remain supported).

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

Automated review result: request changes

The review converged after three rounds of full-scope and risk-focused coverage. Three distinct P1 silent-result-corruption paths are attached inline:

  1. ADBC ignores the mapper's partial nested projection, so a requested Struct child can receive a different physical child.
  2. Cached and runtime recursive schemas are not reconciled, so same-width child-type drift can be reinterpreted as the cached type and reordered Struct fields can be swapped by ordinal.
  3. A dictionary-encoded numeric child survives ListView canonicalization and reaches the leaf SerDe as dictionary indices rather than logical values.

Critical checkpoint conclusions:

  • Goal and correctness: basic ListView/LargeListView ordering, nulls, slices, malformed ranges, tracked allocation, and required outer-null rejection are covered, but the three inline corruption paths mean safe ADBC materialization is not complete.
  • Scope and parallel paths: the five-file patch is focused and all internal signature callers are updated. Both ListView widths share the same builder and ADBC paths and the same findings.
  • Concurrency and lifecycle: no new shared mutable reader state, lock order, race, or deadlock was found. The fallback pool outlives loop-local normalized arrays, the ExecEnv pool has process lifetime, and EOF, cancellation, close, and per-batch cleanup remain intact.
  • Error handling and observability: Arrow and SerDe statuses propagate through the scanner exception boundary, and normalization has a dedicated timer. The accepted issues are silent value corruption before any useful error can be raised.
  • Configuration, compatibility, persistence, and writes: there is no configuration, FE-BE protocol, storage-format, persisted-state, transaction, data-write, failover, or rolling-upgrade change.
  • Performance and memory: conversion allocations are Doris-tracked. Exact capacity and recursive child-reservation residuals are already covered by existing live threads and were not duplicated.
  • Tests and results: changed tests cover ordinary and reordered ranges, nullable slices, invalid ranges, both offset widths, tracked allocation, direct expansion, nested-view rejection, and the required outer-null case. They omit TableReader nested projection, recursive cached/runtime schema drift, and dictionary-encoded children. Per the review contract, no local build or test was run. Clang Formatter, CheckStyle, license, and related static checks pass; BE UT, macOS BE UT, compile, and performance checks are pending.
  • Existing-thread suppression: the five live findings about unsafe conversion, pool tracking, expanded-child reservation, aggregate capacity, and required outer ARRAY nulls were not repeated. A proposed required nested Struct-null issue was dismissed after proving production ADBC metadata makes ARRAY elements and STRUCT fields nullable.
  • Additional user focus: none was supplied; the complete authoritative diff was reviewed.

Completion: complete and converged. Every candidate was independently rechecked and accepted, duplicate-suppressed, or dismissed with code evidence; the final round produced no new valuable finding.

{
SCOPED_TIMER(_normalize_time);
RETURN_IF_ERROR(normalize_arrow_array(batch.column(arrow_idx), &array));
RETURN_IF_ERROR(normalize_arrow_array(batch.column(arrow_idx), arrow_pool, &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.

[P1] Honor the mapper's nested projection

ADBC exposes synthesized complex children and inherits the default TableColumnMapper, so a query may legitimately produce a partial LocalColumnIndex. TableReader then builds the file-block type from that projected subtree, but this loop checks only the root id and passes the complete normalized Arrow root to SerDe. For list_view<struct<a,b>> projected to only b, the target is ARRAY<STRUCT<b>> while Arrow still contains {a,b}; in release builds DataTypeStructSerDe iterates the one target child and reads source field(0), placing a into b (the arity check is only a DCHECK). Either force full complex scan projection with MaterializedColumnMapper and let TableReader rematerialize, or project/reorder the Arrow value from the request. Please add a TableReader-level ListView Struct-subfield projection test.

auto& columns = columns_guard.mutable_columns();
const auto& target_type = columns_guard.get_datatype_by_position(block_position.value());

// The cached FE target remains authoritative when a source schema changes mid-cache-window;

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 the recursive runtime type before SerDe

The cached FE target and runtime Arrow batch are matched only by the top-level column name. If an ARRAY<INT> source changes to ListView<Float32> during the cache window, canonicalization produces List<Float32> and this guard passes; the target Int32 SerDe then takes its generic fixed-width path and reinterprets the float buffer (1.0f becomes 1065353216) instead of rejecting the mismatch. The same missing boundary lets reordered same-typed Struct children be mapped by ordinal. Please validate recursive child types and normalized field identity against the cached target before mutating the block, and add ListView/LargeListView schema-drift tests.

if (type.id() == arrow::Type::LIST_VIEW) {
auto converted = canonicalize_list_view<int32_t, arrow::ListViewArray,
arrow::ListBuilder, arrow::ListArray>(
static_cast<const arrow::ListViewArray&>(*current), pool);

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] Normalize encoding wrappers below the rebuilt list

For list_view<dictionary<int8,int8>>, MakeBuilder creates a dictionary child builder. Its AppendArraySlice inserts decoded values, but Finish still emits a DictionaryArray backed by indices. The next normalizer pass sees only the outer LIST as acceptable and returns it unchanged; ADBC maps the dictionary's logical value type to TINYINT, so Array/Nullable/Number SerDes copy the one-byte index buffer. Logical values [42,43] are therefore returned as [0,1], and default Arrow validation cannot distinguish the equally wide buffers. Before this change the outer ListView was rejected. Please recursively normalize or reject encoding-only descendants before accepting a complex outer type, with ListView/LargeListView dictionary-child tests.

@hello-stephen

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

------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	17572	3109	3048	3048
q2	q3	10870	851	498	498
q4	4676	257	205	205
q5	7668	576	389	389
q6	140	119	95	95
q7	525	503	395	395
q8	9261	904	919	904
q9	3505	2381	2376	2376
q10	6528	871	704	704
q11	467	259	240	240
q12	694	404	332	332
q13	17868	1516	1164	1164
q14	163	149	140	140
q15	q16	479	390	367	367
q17	826	827	725	725
q18	3085	2242	2253	2242
q19	1124	860	702	702
q20	693	514	481	481
q21	5347	1629	1819	1629
q22	325	269	233	233
Total cold run time: 91816 ms
Total hot run time: 16869 ms

----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	3452	3398	3358	3358
q2	q3	2201	2336	2137	2137
q4	1196	1166	887	887
q5	2191	2120	2094	2094
q6	175	122	93	93
q7	1013	904	887	887
q8	1612	1419	1420	1419
q9	3138	3077	3088	3077
q10	1849	1795	1581	1581
q11	365	274	258	258
q12	456	428	365	365
q13	1479	1528	1169	1169
q14	174	165	163	163
q15	q16	401	392	356	356
q17	1047	1038	1032	1032
q18	4960	4378	4708	4378
q19	858	809	841	809
q20	944	924	800	800
q21	3524	3241	3275	3241
q22	395	346	314	314
Total cold run time: 31430 ms
Total hot run time: 28418 ms

@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: 17245 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 9d238a4ee365f454e602bfc7a388f6cd511b49b3, data reload: false

------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	17606	3075	3045	3045
q2	q3	10864	877	509	509
q4	4669	257	210	210
q5	7659	590	391	391
q6	137	117	95	95
q7	530	506	384	384
q8	9260	856	978	856
q9	3473	2411	2393	2393
q10	6506	868	723	723
q11	441	257	250	250
q12	692	396	350	350
q13	17876	1535	1178	1178
q14	161	155	139	139
q15	q16	453	399	374	374
q17	808	772	763	763
q18	3160	2306	2282	2282
q19	1122	932	699	699
q20	712	517	466	466
q21	4863	1905	1946	1905
q22	321	262	233	233
Total cold run time: 91313 ms
Total hot run time: 17245 ms

----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	3392	3355	3344	3344
q2	q3	2222	2383	2175	2175
q4	1197	1174	892	892
q5	2181	2121	2110	2110
q6	169	120	91	91
q7	1055	942	883	883
q8	1620	1391	1405	1391
q9	3161	3141	3138	3138
q10	1888	1807	1616	1616
q11	360	273	258	258
q12	455	428	341	341
q13	1501	1563	1171	1171
q14	169	170	158	158
q15	q16	412	396	365	365
q17	1051	1041	1031	1031
q18	4972	4552	4828	4552
q19	833	842	884	842
q20	966	938	825	825
q21	3648	3225	3315	3225
q22	423	359	334	334
Total cold run time: 31675 ms
Total hot run time: 28742 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 81213 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 9d238a4ee365f454e602bfc7a388f6cd511b49b3, data reload: false

query5	4258	423	348	348
query6	396	162	161	161
query7	4861	464	274	274
query8	293	132	122	122
query9	8695	2917	2902	2902
query10	440	264	219	219
query11	5374	1061	947	947
query12	119	74	76	74
query13	1202	465	334	334
query14	6134	2235	2128	2128
query14_1	2008	1998	1997	1997
query15	174	120	115	115
query16	948	351	377	351
query17	814	470	397	397
query18	2349	336	248	248
query19	175	168	117	117
query20	76	70	69	69
query21	208	111	98	98
query22	5344	5274	5261	5261
query23	6910	6250	6182	6182
query23_1	6082	5988	6089	5988
query24	7259	1108	770	770
query24_1	785	782	769	769
query25	409	278	242	242
query26	1256	273	156	156
query27	2713	409	284	284
query28	4638	1496	1517	1496
query29	917	422	338	338
query30	275	182	154	154
query31	840	432	364	364
query32	97	49	48	48
query33	448	214	184	184
query34	977	840	482	482
query35	409	395	343	343
query36	571	546	548	546
query37	117	77	70	70
query38	1020	858	819	819
query39	497	476	495	476
query39_1	467	479	468	468
query40	216	122	107	107
query41	53	53	53	53
query42	80	79	76	76
query43	242	244	210	210
query44	
query45	109	106	98	98
query46	759	879	541	541
query47	765	752	720	720
query48	298	319	225	225
query49	523	236	190	190
query50	803	324	266	266
query51	8310	8319	8203	8203
query52	73	72	67	67
query53	202	212	161	161
query54	243	183	162	162
query55	75	58	57	57
query56	264	223	231	223
query57	684	641	642	641
query58	216	183	192	183
query59	1225	1235	1108	1108
query60	239	224	192	192
query61	136	118	152	118
query62	345	235	211	211
query63	184	157	151	151
query64	2718	716	609	609
query65	
query66	1867	313	261	261
query67	10117	10148	9889	9889
query68	
query69	416	227	213	213
query70	627	602	619	602
query71	299	257	251	251
query72	2376	1765	1602	1602
query73	679	549	356	356
query74	1977	1222	1137	1137
query75	1274	1163	1020	1020
query76	2404	690	532	532
query77	260	247	210	210
query78	3928	3524	3213	3213
query79	1188	786	551	551
query80	722	388	359	359
query81	441	199	177	177
query82	591	128	101	101
query83	349	258	231	231
query84	
query85	863	423	405	405
query86	344	172	169	169
query87	1021	1008	901	901
query88	2808	2173	2180	2173
query89	323	247	214	214
query90	1819	166	150	150
query91	180	158	166	158
query92	59	61	47	47
query93	1272	1180	784	784
query94	522	273	247	247
query95	646	465	356	356
query96	789	558	288	288
query97	1058	1103	1031	1031
query98	146	132	142	132
query99	447	344	321	321
Total cold run time: 170524 ms
Total hot run time: 81213 ms

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: This content was flagged for possible cybersecurity risk. If this seems wrong, try rephrasing your request. To get authorized for security work, join the Trusted Access for Cyber program: https://chatgpt.com/cyber
Workflow run: https://github.com/apache/doris/actions/runs/32099898953

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

@Gabriel39

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Aug 20th, 2026 3:35 AM.
Workflow run: https://github.com/apache/doris/actions/runs/32105680104

The selected account is excluded until 2026-08-20T03:35:00Z. Please trigger /review again; another configured account may be available.

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 60.26% (188/312) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 62.12% (28875/46481)
Line Coverage 47.13% (301766/640317)
Region Coverage 42.84% (243840/569130)
Branch Coverage 44.43% (113551/255575)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 60.26% (188/312) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 75.78% (34121/45025)
Line Coverage 60.70% (384240/632986)
Region Coverage 56.93% (322813/567003)
Branch Coverage 57.75% (147177/254848)

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