alitrack

我给 DuckDB 做了个 ORC 读取器

DuckDB 能读 Parquet、CSV、JSON、Iceberg、Delta……唯独没有 ORC。

Apache ORC 是 Hive 生态的标准列存格式。如果你跑 Spark 或者数仓导出的是 .orc 文件,到了 DuckDB 这边就是死路——没有官方扩展,社区也没有能用的。

我写了一个。alitrack/duckdb_orc,纯 Rust,187 行核心代码。

仓库:github.com/alitrack/duckdb_orc

● ● ●

怎么用

LOAD 'orc.duckdb_extension';

-- 读单个文件
SELECT * FROM read_orc('sales.orc');

-- glob 多文件
SELECT * FROM read_orc('data/2024/*.orc');

-- 跟普通表一样过滤聚合
SELECT region, SUM(amount)
FROM read_orc('sales.orc')
WHERE year = 2024
GROUP BY region;

跟 read_parquet 一样的体验。支持所有 ORC 数据类型(struct/list/map)、所有压缩编码(Zlib/Snappy/LZO/LZ4/ZSTD),大文件流式读取,不会 OOM。

● ● ●

为什么是 Rust

之前有人用 C 试过——quackmagic/duckdb-extension-orc,依赖 Apache ORC C 库,整个文件加载到内存,没有流式读取,没有测试,0 star。

Rust 生态里有个 orc-rust,DataFusion 团队维护的纯 Rust ORC 读取器,Arrow 输出,直接对接到 DuckDB 的 Arrow C Data Interface。类型全、编码全、压缩全,而且 DataFusion 已经在生产环境验证过了。

底层引擎选对了,上面就是胶水代码的事。

仓库:github.com/datafusion-contrib/orc-rust

● ● ●

架构

read_orc('data/*.orc')
  │
  ▼
glob 展开 → 文件列表 → schema 校验
  │
  ▼
orc-rust ArrowReader (batch_size=2048)
  │
  ▼
RecordBatch → DuckDB DataChunk (零拷贝)

整个过程没有 C++ 编译链,cargo build 一把梭。

● ● ●

做到了什么,卡在哪里

能力状态
全 ORC 类型、编码、压缩✅
流式读取(大文件)✅
glob 多文件✅
列裁剪(projection pushdown)✅
谓词下推(filter pushdown)❌
写入❌

列裁剪是 DuckDB 自动做的——SELECT name FROM read_orc(...) 只读 name 列。这部分开箱即用。

谓词下推卡在了 DuckDB 上游。DuckDB 的 C 扩展 API 只暴露了 projection pushdown,没有对应的 filter pushdown 接口。所有 Rust 扩展——包括社区里已经发布的 rusty-sheet——都面临同一个天花板。

不过 DuckDB 仍然会在扫描完成后应用 WHERE 过滤,数据不会漏。只是不能利用 ORC stripe 级别的 min/max 统计跳过不相关的数据块。

● ● ●

跨平台 CI

用 DuckDB 官方的 extension-ci-tools 搭了多平台构建流水线:

  • linux_amd64 ✅
  • linux_arm64 ✅
  • osx_amd64 ✅
  • osx_arm64 ✅

打 tag 自动发布 GitHub Release,所有平台的 .duckdb_extension 二进制文件都可以直接下载。

Release:github.com/alitrack/duckdb_orc/releases

● ● ●

下一步

三个方向:

  1. 01给 DuckDB 上游提 PR,加 filter pushdown 的 C API——这是唯一能打通谓词下推的路径
  2. 02提交到社区扩展仓库(duckdb/community-extensions),让用户 INSTALL orc FROM community 就能装
  3. 03写支持——orc-rust 只读,短期内不太可能

做完第二步,DuckDB + ORC 这件事就闭环了。