결과 처리
결과 처리 (Handle Results, Rust)
개요 (Overview)
결과 집합을 [행 단위로]({% link docs/current/clients/rust/querying.md %}#mapping-rows-to-rust-values) 읽는 것 외에도, Rust 클라이언트는 쿼리 결과를 Apache Arrow로 record batch 스트림으로 넘기고, Arrow 배치를 쿼리 가능한 테이블로 등록하고, 결과를 Polars 데이터 프레임으로 반환할 수 있어요. 아래에 열 지향 결과 처리 옵션 각각을 설명할게요.
출처: 문서
본문
Apache Arrow
DuckDB는 열 지향이고, 결과를 Rust에 대량으로 반환하는 네이티브 방식은 Apache Arrow예요. 크레이트는 arrow 크레이트를 재-export하므로, 별도의 Arrow 의존성이나 버전 정렬이 필요 없어요. Arrow 타입은 duckdb::arrow를 통해 접근할 수 있어요.
결과를 Arrow로 읽기
준비된 statement에서 query_arrow()를 호출해 RecordBatch 반복자를 얻어요. 수집하면 전체 결과가 물리화되고, 반복하면 한 번에 한 배치씩 읽어요:
use duckdb::{Connection, Result};
use duckdb::arrow::record_batch::RecordBatch;
use duckdb::arrow::util::pretty::print_batches;
let conn = Connection::open_in_memory()?;
let mut stmt = conn.prepare("SELECT * FROM generate_series(1, 5)")?;
let batches: Vec<RecordBatch> = stmt.query_arrow([])?.collect();
print_batches(&batches).unwrap();
반환된 핸들에서 get_schema()는 DuckDB가 결과에 대해 추론한 Arrow 스키마를 보고해요.
Arrow 결과 스트리밍
query_arrow()는 statement를 완료까지 실행하고 클라이언트 측에서 전체 결과를 버퍼링하므로, 반복자를 한 배치씩 진행하더라도 큰 결과가 메모리에 보관돼요. 반복자가 진행될 때만 청크를 가져오는 지연 소비 결과에는 stream_arrow()를 사용해요. 그 외에는 동일해요:
let mut stmt = conn.prepare("SELECT * FROM big_table")?;
for batch in stmt.stream_arrow([])? {
// 한 번에 하나의 RecordBatch 처리
println!("{} rows", batch.num_rows());
}
DuckDB는 일부 statement에 대해 내부적으로 결과를 여전히 물리화할 수 있어요. 스트리밍 반복자는 실행 시작 후 fetch 또는 Arrow 변환이 실패하면 패닉해요.
Arrow 배치 쿼리
Rust 프로그램의 다른 곳에서 만든 Arrow RecordBatch는 DuckDB 테이블 함수로 등록해 SQL에서 쿼리할 수 있어요. vtab-arrow 기능을 활성화하고, 연결에 내장 ArrowVTab 테이블 함수를 등록하고, arrow_recordbatch_to_query_params()로 배치를 쿼리 파라미터로 전달해요. 다음은 크레이트의 arrow_vtab 예시에서 가져온 것이에요:
use duckdb::{Connection, arrow::record_batch::RecordBatch};
use duckdb::vtab::arrow::{arrow_recordbatch_to_query_params, ArrowVTab};
let conn = Connection::open_in_memory()?;
conn.register_table_function::<ArrowVTab>("arrow")?;
let params = arrow_recordbatch_to_query_params(cities_batch);
let batches: Vec<RecordBatch> = conn
.prepare(
"SELECT city, population
FROM arrow(?, ?)
WHERE coastal AND population >= 500000
ORDER BY population DESC",
)?
.query_arrow(params)?
.collect();
배치는 register_table_function()에 주어진 이름(여기서는 arrow)으로 주소가 지정되고, DuckDB는 다른 테이블처럼 필터링·정렬·집계해요. arrow_recordbatch_to_query_params()는 배치를 arrow(?, ?) 함수가 기대하는 두 파라미터로 확장해요.
Polars 데이터 프레임
polars 기능을 활성화하면 쿼리 결과를 Polars DataFrame으로 반환할 수 있어요. 준비된 statement에서 query_polars()를 호출해 결과 청크당 하나씩 데이터 프레임 반복자를 얻어요:
use duckdb::{Connection, Result};
use polars::prelude::DataFrame;
let conn = Connection::open_in_memory()?;
let mut stmt = conn.prepare("SELECT * FROM test")?;
let dfs: Vec<DataFrame> = stmt.query_polars([])?.collect();
청크를 단일 DataFrame으로 결합하려면 polars_core의 accumulate_dataframes_vertical_unchecked를 사용해요. 크레이트는 polars를 재-export하므로 그 타입들도 duckdb::polars로 접근할 수 있어요.
더 알아보기 (Learn more)
- [Run Queries]({% link docs/current/clients/rust/querying.md %}) — 이 페이지가 읽는 결과를 내는 쿼리 전송 및 행 단위 읽기.
- [Write User Defined Functions]({% link docs/current/clients/rust/functions.md %}) — 내장
ArrowVTab이 그중 하나인 테이블 함수 작성. - [Import Data]({% link docs/current/clients/rust/data_import.md %}) —
appender-arrow기능으로 Arrow record batch를 테이블에 추가하기.