Skip to content

Commit f1f6ea4

Browse files
committed
feat: add DuckDB Xcode target and fix C API pointer handling
1 parent 5021c2d commit f1f6ea4

2 files changed

Lines changed: 210 additions & 37 deletions

File tree

Plugins/DuckDBDriverPlugin/DuckDBPlugin.swift

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,30 @@ private actor DuckDBConnectionActor {
3838

3939
func open(path: String) throws {
4040
var db: duckdb_database?
41-
let state = duckdb_open(path, &db)
41+
var errorPtr: UnsafeMutablePointer<CChar>?
42+
let state = duckdb_open_ext(path, &db, nil, &errorPtr)
4243

4344
if state == DuckDBError {
45+
let detail: String
46+
if let errPtr = errorPtr {
47+
detail = String(cString: errPtr)
48+
duckdb_free(errPtr)
49+
} else {
50+
detail = "unknown error"
51+
}
52+
throw DuckDBPluginError.connectionFailed(
53+
"Failed to open DuckDB database at '\(path)': \(detail)"
54+
)
55+
}
56+
57+
guard let openedDB = db else {
4458
throw DuckDBPluginError.connectionFailed(
4559
"Failed to open DuckDB database at '\(path)'"
4660
)
4761
}
4862

4963
var conn: duckdb_connection?
50-
let connState = duckdb_connect(db, &conn)
64+
let connState = duckdb_connect(openedDB, &conn)
5165

5266
if connState == DuckDBError {
5367
duckdb_close(&db)
@@ -59,12 +73,12 @@ private actor DuckDBConnectionActor {
5973
}
6074

6175
func close() {
62-
if var conn = connection {
63-
duckdb_disconnect(&conn)
76+
if connection != nil {
77+
duckdb_disconnect(&connection)
6478
connection = nil
6579
}
66-
if var db = database {
67-
duckdb_close(&db)
80+
if database != nil {
81+
duckdb_close(&database)
6882
database = nil
6983
}
7084
}
@@ -103,22 +117,26 @@ private actor DuckDBConnectionActor {
103117
}
104118

105119
let startTime = Date()
106-
var stmt: duckdb_prepared_statement?
120+
var stmtOpt: duckdb_prepared_statement?
107121

108-
let prepState = duckdb_prepare(conn, query, &stmt)
122+
let prepState = duckdb_prepare(conn, query, &stmtOpt)
109123
if prepState == DuckDBError {
110124
let errorMsg: String
111-
if let errPtr = duckdb_prepare_error(stmt) {
125+
if let s = stmtOpt, let errPtr = duckdb_prepare_error(s) {
112126
errorMsg = String(cString: errPtr)
113127
} else {
114128
errorMsg = "Failed to prepare statement"
115129
}
116-
duckdb_destroy_prepare(&stmt)
130+
duckdb_destroy_prepare(&stmtOpt)
117131
throw DuckDBPluginError.queryFailed(errorMsg)
118132
}
119133

134+
guard let stmt = stmtOpt else {
135+
throw DuckDBPluginError.queryFailed("Failed to prepare statement")
136+
}
137+
120138
defer {
121-
duckdb_destroy_prepare(&stmt)
139+
duckdb_destroy_prepare(&stmtOpt)
122140
}
123141

124142
for (index, param) in parameters.enumerated() {
@@ -298,6 +316,10 @@ final class DuckDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable {
298316

299317
try await connectionActor.open(path: path)
300318

319+
// Enable auto-install and auto-load of extensions (e.g. core_functions)
320+
try? await connectionActor.executeQuery("SET autoinstall_known_extensions=1")
321+
try? await connectionActor.executeQuery("SET autoload_known_extensions=1")
322+
301323
if let conn = await connectionActor.connectionHandleForInterrupt {
302324
setInterruptHandle(conn)
303325
}

0 commit comments

Comments
 (0)