결과 처리
결과 처리 (Handle Results, Java/JDBC)
개요 (Overview)
표준 JDBC ResultSet 외에도, DuckDB 드라이버는 결과를 Apache Arrow로 넘기고, 큰 결과를 물리화하지 않고 스트리밍하며, 원시 열 지향 데이터 청크를 노출할 수 있어요. 이 페이지는 각 결과 처리 옵션을 다룬다.
출처: 문서
본문
Arrow 메서드
DuckDB 결과 집합은 Java Arrow 바인딩을 통해 Apache Arrow와 통합돼요. DuckDBResultSet은 그 행을 Arrow 기반 도구가 소비할 수 있는 Arrow 스트림으로 내보낼 수 있고, 다른 곳에서 만든 Arrow 스트림은 연결에 등록해 DuckDB 테이블로 쿼리할 수 있어요. 타입 시그니처는 API Reference를 참고해요.
Arrow 내보내기
다음 예시는 쿼리 결과에서 Arrow 스트림을 내보내고 Java Arrow 바인딩으로 소비해요. 각 배치가 차례로 로드되고, 그 벡터가 VectorSchemaRoot에서 읽혀요.
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.ipc.ArrowReader;
import org.duckdb.DuckDBResultSet;
try (var conn = DriverManager.getConnection("jdbc:duckdb:");
var stmt = conn.prepareStatement("SELECT * FROM generate_series(2000)");
var resultset = (DuckDBResultSet) stmt.executeQuery();
var allocator = new RootAllocator()) {
try (var reader = (ArrowReader) resultset.arrowExportStream(allocator, 256)) {
while (reader.loadNextBatch()) {
System.out.println(reader.getVectorSchemaRoot().getVector("generate_series"));
}
}
}
Arrow 가져오기
다음 예시는 Java Arrow 바인딩이 만든 Arrow 스트림을 소비하고 registerArrowStream()으로 DuckDB 연결에 등록해요. 등록되면 그 메서드에 전달된 이름으로 스트림이 주소가 지정되고 다른 테이블처럼 쿼리할 수 있어요.
import org.apache.arrow.c.ArrowArrayStream;
import org.apache.arrow.c.Data;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.ipc.ArrowStreamReader;
import org.duckdb.DuckDBConnection;
import org.duckdb.DuckDBResultSet;
// Arrow binding
try (var allocator = new RootAllocator();
ArrowStreamReader reader = null; // 물론 null이면 안 됨
var arrow_array_stream = ArrowArrayStream.allocateNew(allocator)) {
Data.exportArrayStream(allocator, reader, arrow_array_stream);
// DuckDB setup
try (var conn = (DuckDBConnection) DriverManager.getConnection("jdbc:duckdb:")) {
conn.registerArrowStream("asdf", arrow_array_stream);
// 쿼리 실행
try (var stmt = conn.createStatement();
var rs = (DuckDBResultSet) stmt.executeQuery("SELECT count(*) FROM asdf")) {
while (rs.next()) {
System.out.println(rs.getInt(1));
}
}
}
}
결과 스트리밍 (Streaming Results)
결과 스트리밍은 JDBC 드라이버에서 옵트인이에요. 쿼리를 실행하기 전에 jdbc_stream_results 설정을 true로 설정해 활성화해요. 가장 쉬운 방법은 Properties 객체에 전달하는 것이에요.
Properties props = new Properties();
props.setProperty(DuckDBDriver.JDBC_STREAM_RESULTS, String.valueOf(true));
Connection conn = DriverManager.getConnection("jdbc:duckdb:", props);
청크 결과 (Chunked Results)
JDBC 드라이버는 org.duckdb.DuckDBChunkedResult 클래스를 통해 쿼리 결과를 지연 fetch되는 열 지향 데이터 청크 시퀀스로 반환할 수 있어요.
이것은 C API의 duckdb_fetch_chunk 함수를 Java에 노출하고, JDBC ResultSet 인터페이스가 필요로 하는 행별 오버헤드를 피해요.
청크 내용은 [user-defined functions]({% link docs/current/clients/java/functions.md %})에서 사용하는 것과 같은 DuckDBDataChunkReader API로 읽어요.
DuckDBPreparedStatement에서 query()를 호출해 DuckDBChunkedResult를 얻은 다음, nextChunk()로 청크를 진행하고 chunk()로 현재 청크를 얻은 후, 각 컬럼 벡터를 인덱스로 읽어요.
예시:
import java.sql.DriverManager;
import org.duckdb.DuckDBConnection;
import org.duckdb.DuckDBPreparedStatement;
import org.duckdb.DuckDBChunkedResult;
import org.duckdb.DuckDBDataChunkReader;
import org.duckdb.DuckDBReadableVector;
try (DuckDBConnection conn = DriverManager
.getConnection("jdbc:duckdb:")
.unwrap(DuckDBConnection.class);
DuckDBPreparedStatement ps = conn.prepare("SELECT ? AS col1")) {
// statement 파라미터는 1-based
ps.setInt(1, 42);
try (DuckDBChunkedResult res = ps.query()) {
// 다음 청크로 진행, 성공 시 true 반환
while (res.nextChunk()) {
// 결과에서 현재 청크 얻기
DuckDBDataChunkReader chunk = res.chunk();
// 청크 컬럼 순회, 모든 인덱스 0-based
for (long col = 0; col < chunk.columnCount(); col++) {
// 지정된 컬럼의 벡터 얻기
DuckDBReadableVector vector = chunk.vector(col);
// 벡터 행 순회
for (long row = 0; row < chunk.rowCount(); row++) {
int val = vector.getInt(row);
System.out.println(val);
}
}
}
}
}
Statement 파라미터는 JDBC 규칙에 따라 1-based로 유지되고, 청크 컬럼과 행은 C API와 user-defined function 인터페이스에 맞춰 0-based예요.
청크 결과 API는 현재 기본 데이터 타입만 지원해요.
LIST와STRUCT같은 복합 타입은 아직 이 인터페이스로 읽을 수 없어요.query()메서드는 prepared statements에서만 사용 가능하며query(String)오버로드는 없어요.
더 알아보기 (Learn more)
- [Run Queries]({% link docs/current/clients/java/querying.md %}) — 이 페이지가 읽는 결과를 내는 쿼리 전송 및 표준
ResultSet접근자. - [Define Functions]({% link docs/current/clients/java/functions.md %}) — 청크 결과와 공유하는
DuckDBDataChunkReaderAPI는 user-defined functions 작성에도 사용돼요. - [Define Connections]({% link docs/current/clients/java/connecting.md %}) — 결과 스트리밍을 활성화하는
jdbc_stream_results옵션. - [C API]({% link docs/current/clients/c/api.md %}) — 청크 결과 API가 Java에 노출하는
duckdb_fetch_chunk함수.