This is an automated email from the ASF dual-hosted git repository. suxiaogang223 pushed a commit to branch refact_reader_branch in repository https://gitbox.apache.org/repos/asf/doris.git
commit 23697072651f3875d65813f496f7f32b2fc3f03f Author: Socrates <[email protected]> AuthorDate: Thu May 28 17:36:36 2026 +0800 [doc](be) Update parquet complex reader status ### What problem does this PR solve? Issue Number: close #xxx Related PR: #xxx Problem Summary: Update the new parquet reader implementation document with the current complex type support status, validation results, remaining gaps, and next implementation priorities. ### Release note None ### Check List (For Author) - Test: No need to test - Documentation-only change. - Behavior changed: No - Does this need documentation: No --- docs/doris-arrow-parquet-reader-implementation.md | 85 +++++++++++++++++------ 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/docs/doris-arrow-parquet-reader-implementation.md b/docs/doris-arrow-parquet-reader-implementation.md index d191229e445..c3acb5d8f1e 100644 --- a/docs/doris-arrow-parquet-reader-implementation.md +++ b/docs/doris-arrow-parquet-reader-implementation.md @@ -142,7 +142,9 @@ select(selection, selected_rows, batch_rows, column) 当前实现: - `ScalarColumnReader`:基于 Arrow internal `RecordReader` 读取 flat primitive/string/decimal/time/timestamp。 -- `StructColumnReader`:递归读取 children,支持非常基础的 struct 组装。 +- `StructColumnReader`:支持 top-level struct 的 scalar child 组装,包含 nullable parent struct、nullable scalar child 和 struct child projection。 +- `ListColumnReader`:支持 scalar element 的 LIST level 组装,包含 null list、empty list、nullable element 和 overflow state。 +- `MapColumnReader`:支持 scalar key/value 的 MAP level 组装,包含 null map、empty map、nullable scalar value 和 overflow state。 `select()` 在基类中统一实现:把 `SelectionVector` 合并成连续 row ranges,然后交替调用 `skip()` 和 `read()`。当前不实现整批 read 后再 filter 的 fallback。 @@ -218,7 +220,8 @@ DataTypeSerDe::read_column_from_decoded_values(...) - selection index 当前是 `uint16_t`,需要显式约束 batch size; - selected read 依赖 Arrow internal `RecordReader::SkipRecords` 和 `ReadRecords`,需要继续隔离在 `column_reader.*`; - 没有 page-level row range selection; -- 复杂列延时物化尚未实现。 +- LIST/MAP 的 `select()` 已经复用 `skip() + read()` range 策略,并通过 nested overflow state 保持 cursor 正确; +- Struct 的 complex child selected read 仍依赖 child reader 自身能力,后续需要补多 stream assembler。 ## Schema Change 当前状态 @@ -248,20 +251,44 @@ DataTypeSerDe::read_column_from_decoded_values(...) - schema builder 能识别 `STRUCT`、`LIST`、`MAP`。 - 可以把复杂 Parquet schema 组合成 Doris `DataTypeStruct`、`DataTypeArray`、`DataTypeMap`。 -- `StructColumnReader` 可以递归读取 children,支持非常基础的非 nullable struct。 +- `ParquetColumnSchema` 已记录 file path、field id path、name path、definition level、repetition level、nullable definition level 和 repeated repetition level,为后续 child-level mapping/schema change 留入口。 +- `TableColumnMapper` 可以为 struct child 生成 `FieldProjection`,`ParquetReader` 会把 projected file-local schema 暴露给上层。 +- `StructColumnReader` 支持 top-level struct 的 scalar children: + - required struct; + - nullable struct; + - required scalar child; + - nullable scalar child; + - projected scalar child,例如只读 `s.b` 时仍能根据该 leaf 的 definition level 还原 parent null map。 +- `LIST` 支持 scalar element: + - required / nullable list; + - null list; + - empty list; + - required / nullable scalar element; + - 小批量 read 下跨 batch 的 overflow; + - `skip()` / `select()` 通过同一个 level assembler 推进。 +- `MAP` 支持 scalar key/value: + - required / nullable map; + - null map; + - empty map; + - required key; + - required / nullable scalar value; + - key leaf 作为 shape driver,value leaf 校验 row count、level count 和 repetition level 对齐; + - `skip()` / `select()` 通过同一个 level assembler 推进。 +- `NestedScalarBatch` 在每次 `RecordReader::ReadRecords()` 后复制 def/rep levels,并把 defined values materialize 到 Doris-owned 临时列,避免保存 Arrow buffer 或 `StringRef`。 +- `NestedScalarOverflow` 保存未消费的 level tail 和 compact 后的 Doris-owned value column,LIST/MAP read-ahead 不再假设 child records 等于 output rows。 +- `RepeatedLevelAssembler` 统一折叠 repeated level stream,生成 parent row、entry count、parent null map,并由 sink 写入 list/map child column。 主要缺口: -- nullable struct 未实现。 -- list reader 未实现。 -- map reader 未实现。 -- repeated / nested definition level assembler 未实现。 -- primitive reader 当前只支持 `max_repetition_level == 0 && max_definition_level <= 1` 的 RecordReader 路径。 -- 复杂列裁剪未实现。 -- 复杂列延时物化未实现。 -- 复杂列 schema evolution / child remap 未实现。 +- `Array(Struct)`、`Map<K, Struct>` 还未实现。当前 Struct reader 可以组装 scalar child,但 LIST/MAP assembler 还没有接 complex child sink。 +- 嵌套 list/map 还未实现,例如 `Array(Array<T>)`、`Map<K, Array<T>>`。 +- nullable struct 如果包含 complex child,目前仍返回 `NotSupported`,避免在缺少多 stream assembler 时误读。 +- LIST/MAP 的 nested projection 还未实现。当前只支持完整读取 scalar element/value,不支持只投影 `array.element.x` 或 `map.value.y`。 +- 复杂类型 schema change 还未实现 child-level remap/default/cast。当前 schema/path/projection 结构按后续扩展预留,但缺失 child、rename、field id remap、default child、nested cast 都还没有接入。 +- primitive reader 的 flat scalar 路径仍只支持 `max_repetition_level == 0 && max_definition_level <= 1`;nested scalar 只能通过 complex reader 使用。 +- complex child 的 lazy materialization 还不完整,尤其是 Struct complex child 和未来多 leaf value 需要统一 cursor/overflow。 -结论:当前复杂列“schema 可见”,但“读取能力不完整”。真正可用还需要实现 Dremel assembler 或等价的 nested column assembler。 +结论:当前复杂列已经从“schema 可见”推进到“scalar-child LIST/MAP/STRUCT 可读”。下一阶段重点不是再补单个特殊 case,而是把 Struct child 接入 LIST/MAP assembler,并建立多 leaf stream 的统一 cursor/overflow 模型。 ## 当前可用能力总结 @@ -277,7 +304,10 @@ DataTypeSerDe::read_column_from_decoded_values(...) - 通过 `DataTypeSerDe::read_column_from_decoded_values()` 写入 Doris column; - 基础 predicate-first scan; - flat column selected read; -- 非 nullable struct 的初步读取框架。 +- non-nullable / nullable struct 的 scalar child 读取; +- struct scalar child projection; +- scalar LIST / MAP 读取; +- LIST / MAP 的 skip/select overflow 推进。 当前还不具备完整生产能力,尤其缺少: @@ -286,22 +316,31 @@ DataTypeSerDe::read_column_from_decoded_values(...) - batch 内 `ColumnPredicate` 执行; - `reader_expression_map`; - page index / bloom filter / dictionary pruning; -- list/map/nullable struct; -- nested column pruning; -- nested lazy materialization; +- `Array(Struct)` / `Map<K, Struct>`; +- nested list/map; +- LIST/MAP child projection; +- 复杂类型 schema change; +- complex child nested lazy materialization; - 充分单测覆盖。 +最近验证状态: + +- `git diff --check` 通过。 +- Fedora `/home/socrates/code/doris` 上 `BUILD_TYPE=DEBUG ./build.sh --be` 通过。 +- 本地 macOS 运行 `./run-be-ut.sh --run '--filter=ParquetColumnReaderTest.*'` 被环境阻断,CMake 检查 clang++ 时失败:`ld: library 'c++' not found`,未进入测试体。 + ## 下一步优先级 建议按以下顺序推进: -1. 收敛 `SchemaField` 和 `ColumnMapping` 的 id 语义,区分 Iceberg field id、Parquet leaf column id 和 file-local output position。 -2. 补齐 batch 内 `ColumnPredicate` 执行,让 row group pruning 之后仍有正确 residual filter。 -3. 实现 `reader_expression_map`,支撑 schema change 下无法安全下推的 filter fallback。 -4. 补 flat primitive/string/decimal/timestamp 的 selected read 单测。 -5. 实现 nullable struct,再实现 list/map assembler。 -6. 在复杂列 assembler 稳定后,再做 nested pruning 和 nested lazy materialization。 -7. 后续再接 page index、bloom filter、dictionary pruning。 +1. 抽象 Struct child sink,把 `Array(Struct)` 和 `Map<K, Struct>` 接到现有 LIST/MAP level assembler。 +2. 将 LIST/MAP projection 从 top-level projection 扩展到 child projection,先支持 `array.element.<field>` 和 `map.value.<field>` 这类 Struct child 裁剪。 +3. 为多 leaf stream 引入统一 cursor/overflow 状态,避免 Struct、Array、Map 各自维护不兼容的 read-ahead。 +4. 收敛 `SchemaField` 和 `ColumnMapping` 的 id 语义,区分 Iceberg field id、Parquet leaf column id 和 file-local output position。 +5. 设计复杂类型 schema change 的 child-level mapping 接口,先预留缺失 child/default/null/cast sink,不立即实现完整语义。 +6. 补齐 batch 内 `ColumnPredicate` 执行,让 row group pruning 之后仍有正确 residual filter。 +7. 实现 `reader_expression_map`,支撑 schema change 下无法安全下推的 filter fallback。 +8. 在复杂列 assembler 稳定后,再做 nested pruning、nested lazy materialization、page index、bloom filter、dictionary pruning。 ## 核心规则 --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
