Arrow result transfer: enabling it, configuration, and what gets faster¶
dosi-engine can move query results as Apache Arrow — columnar end to end for engines whose wire format is already columnar (DuckDB, ClickHouse, StarRocks/Doris, Databricks). This page is the user guide: what to configure, what to build, and which APIs speed up. The research, support matrix, and adoption plan behind it live in design/arrow-adbc.md; connector setup details are in connectors.md.
0. Locked scope (2026-07-12)¶
Arrow paths this round: DuckDB (first priority — the bundled-crate disk
constraint is resolved), ClickHouse, StarRocks, Doris, Databricks.
Snowflake + BigQuery are one deferred TODO: both ride a future generic
exec-adbc channel (their ADBC drivers are maintained by the same org — the
ADBC Driver Foundry — as Go .sos; Snowflake's is Stable today, BigQuery's is
still Experimental, so the wait is only on BigQuery). Trino/MySQL/TiDB/
Redshift: no Arrow path, dropped — their wire formats are row-oriented, a
client-side pivot gains nothing.
1. Do I need to change my configuration?¶
Mostly no. Existing connection files keep working unchanged; the row path stays the default everywhere and behaves identically.
| Engine | Config change to get Arrow |
|---|---|
| DuckDB | none — in-process Arrow becomes the engine's native path automatically |
| ClickHouse | none — same HTTP profile; the adapter requests FORMAT ArrowStream itself |
| Databricks | none beyond the normal profile (host, warehouse, password: = PAT) — the executor is Arrow-first from day one |
| StarRocks | add one key: arrow_flight_port: 9408 (the FE arrow_flight_port; needs SR ≥ 3.5.1). Key present = Flight SQL; key absent = MySQL-wire exactly as today |
| Doris | add one key: arrow_flight_port: 8070 (the FE arrow_flight_sql_port; needs Doris ≥ 2.1.5 recommended). Same opt-in rule |
Example:
datasources:
prod-sr:
type: starrocks
host: sr.internal
port: 9030 # MySQL-wire port, still used as fallback/seeding
arrow_flight_port: 9408 # presence opts this profile into Arrow Flight SQL
username: osi
password: ${SR_PASSWORD}
database: analytics
There is deliberately no result_format: knob: each adapter has one
native decode, and the egress format is chosen per request (Accept header /
CLI flag), not per datasource.
2. How do I enable it at build time?¶
| Feature | What it turns on | In default build? |
|---|---|---|
exec-duckdb |
bundled libduckdb, in-process, Arrow-native | yes (new default; --no-default-features = today's lean CLI-shell-out build) |
exec-http-arrow |
ClickHouse FORMAT ArrowStream |
no (implies exec-http) |
exec-flightsql |
StarRocks/Doris Arrow Flight SQL client (tonic/tokio, gated) | no |
exec-databricks |
Databricks Statement Execution API, INLINE JSON_ARRAY rows | no |
exec-databricks-arrow |
Databricks ARROW_STREAM + EXTERNAL_LINKS (arrow-ipc) |
no (implies exec-databricks) |
exec-all |
all of the above | — |
dosi-server arrow |
Arrow IPC responses on /v1/query/execute |
yes (server default) |
3. Which APIs get faster, and by how much?¶
POST /v1/query/executewithAccept: application/vnd.apache.arrow.stream— the headline win. Batches stream from the warehouse adapter straight into the HTTP body as Arrow IPC: no per-value JSON encoding on the server, no JSON parsing in the client, and columnar consumers (Polars/pandas/DataFusion/ another dosi) read it zero-parse. The JSON response stays byte-identical for existing clients. Benefit scales with result size: negligible for a 10-row aggregate, decisive for high-cardinality group-bys and detail/export queries (10⁵+ rows).dosi query --execute --format arrow— Arrow IPC on stdout for piping into DuckDB/Polars/anything, skipping the table/JSON rendering entirely.- StarRocks/Doris large reads — Flight SQL reads bypass FE row forwarding (client pulls columnar results from BEs in parallel); vendors measure 20–160x vs the MySQL protocol on large result sets. Small aggregates: ~no change.
- ClickHouse — server-side Arrow serialization (lz4-framed) replaces JSON encode/decode on both ends; also removes float/decimal text round-trips.
- DuckDB (default engine) — in-process zero-copy replaces
spawn-a-CLI-per-query + JSON parse: lower latency on every
--execute, plus in-memory databases finally persist across statements in one session. - What does NOT get faster: compile-only APIs (
/v1/query/compile,explain) — they never touch results; small metric queries on row-wire engines (MySQL/TiDB/Postgres/Trino) — unchanged by design.
Every Arrow path passes corpus parity before it ships: the same 45 cases, Arrow results ≡ row results ≡ the DuckDB reference.