Open3
DuckDBクライアントAPIをZig対応にしてみる
- DuckDB (Why DuckDB, Frequently Asked Questions)
- Announcing DuckDB 1.0.0
- DuckDB 1.0.0 "Nivis"
- API Reference / Client API - C
方針
- 使用する関数や構造体などは
extern
で明示し@cImport
と@cInclude
は使わない -
[*c]
やオプションはできるだけ使わない
リンクと実行(Linux環境の場合)
- libduckdb-linux-amd64.zip をDuckDB 1.0.0 "Nivis"からダウンロード
- libduckdb.soをリンクしてコンパイル
libduckdb.soのリンクと実行(デバッグモード)
$ zig run -L(ライブラリのディレクトリ) -lduckdb -lc (ソースファイル名)
Reference
Open/Connect構造体と関数をZig対応にする
// DuckDBの構造体(libduckdb.soより)
const duckdb_database = extern struct {
__db: *opaque{}
};
const duckdb_connection = extern struct {
__conn: *opaque{}
};
// DuckDBの関数(libduckdb.soより)
extern fn duckdb_open(path: ?[*:0]const u8, out_data: *duckdb_database) duckdb_state;
extern fn duckdb_close(database: *const duckdb_database) void;
extern fn duckdb_connect(database: duckdb_database, out_connection: *duckdb_connection) duckdb_state;
extern fn duckdb_disconnect(connection: *const duckdb_connection) void;
C APIをそのまま使う場合
C APIをそのまま使う場合
var db: duckdb_database = undefined;
var con: duckdb_connection = undefined;
// エラーチェックは割愛
_ = duckdb_open(null, &db);
_ = duckdb_connect(db, &con);
// .....
duckdb_disconnect(&con);
duckdb_close(&db);
structでまとめる
structでまとめる
const DuckDBState = enum (u8) {
Success = 0,
Error = 1
};
const DuckDB = struct {
db: duckdb_database,
con: duckdb_connection,
const Self = @This();
pub fn init(path: ?[*:0]const u8) !Self {
var db: duckdb_database = undefined;
var state: usize = duckdb_open(path, &db);
if (state != @intFromEnum(DuckDBState.Success)) return error.DuckDBNotOpen;
var con: duckdb_connection = undefined;
state = duckdb_connect(db, &con);
if (state != @intFromEnum(DuckDBState.Success)) return error.DuckDBNotConnect;
return .{ .db = db, .con = con };
}
pub fn deinit(self: Self) void {
duckdb_disconnect(&self.con);
duckdb_close(&self.db);
}
};
// 実行イメージ
const duckdb = try DuckDB.init(null);
defer duckdb.deinit();
Reference) / Fetch (Reference)
Query (-
公式のサンプル main.cを参考にして、必要な関数を
extern fn
で宣言し、SQLを実行し、結果を取得するプログラムを作成してみる
関数の宣言
// open, connect関連は省略
const duckdb_result = extern struct {
__internal_data: *opaque{}
};
extern fn duckdb_query(connection: duckdb_connection, query: [*:0]const u8, out_result: ?*duckdb_result) duckdb_state;
extern fn duckdb_destroy_result(result: *duckdb_result) void;
extern fn duckdb_column_count(result: *duckdb_result) idx_t;
extern fn duckdb_column_name(result: *duckdb_result, col: idx_t) [*:0]const u8;
extern fn duckdb_row_count(result: *duckdb_result) idx_t;
extern fn duckdb_value_is_null(result: *duckdb_result, col: idx_t, row: idx_t) bool;
extern fn duckdb_value_int32(result: *duckdb_result, col: idx_t, row: idx_t) i32;
SQLの実行と結果の取得
const std = @import("std");
var db: duckdb_database = undefined;
var con: duckdb_connection = undefined;
var result: duckdb_result = undefined;
// エラーチェックは省略
_ = duckdb_open(null, &db);
_ = duckdb_connect(db, &con);
_ = duckdb_query(con, "CREATE TABLE integers(i INTEGER, j INTEGER);", null);
_ = duckdb_query(con, "INSERT INTO integers VALUES (3, 4), (5, 6), (7, null);", null);
_ = duckdb_query(con, "SELECT * FROM integers", &result);
const row_count: idx_t = duckdb_row_count(&result);
const column_count: idx_t = duckdb_column_count(&result);
for(0..column_count) |i| {
try stdout.print("{s} ", .{duckdb_column_name(&result, i)});
}
try stdout.print("\n", .{});
for(0..row_count) |row_idx| {
for(0..column_count) |col_idx| {
if (duckdb_value_is_null(&result, col_idx, row_idx)) {
try stdout.print("NULL ", .{});
}
else {
const val: i32 = duckdb_value_int32(&result, col_idx, row_idx);
try stdout.print("{} ", .{val});
}
}
try stdout.print("\n", .{});
}
duckdb_destroy_result(&result);
duckdb_disconnect(&con);
duckdb_close(&db);
結果
i j
3 4
5 6
7 NULL