@rip-lang/db 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/duckdb.mjs CHANGED
@@ -1,325 +1,802 @@
1
1
  /**
2
- * DuckDB High-Performance Zig Bindings
2
+ * DuckDB Pure Bun FFI Wrapper
3
3
  *
4
- * Minimal JavaScript wrapper for the Zig implementation.
5
- * The Zig code handles all binary serialization directly - no JS overhead!
4
+ * Direct FFI bindings to DuckDB's C API using the modern chunk-based API.
5
+ * No deprecated per-value functions. No Zig, no npm package.
6
6
  *
7
7
  * Usage:
8
- * import { open, queryBinary, tokenize } from './duckdb.mjs';
8
+ * import { open } from './duckdb.mjs';
9
9
  *
10
10
  * const db = open(':memory:');
11
11
  * const conn = db.connect();
12
- *
13
- * // Returns binary ArrayBuffer - ready for HTTP response!
14
- * const result = queryBinary(conn.handle, 'SELECT 42', 10000);
15
- *
16
- * // Returns binary ArrayBuffer for tokenize endpoint
17
- * const tokens = tokenize('SELECT * FROM users');
12
+ * const rows = await conn.query('SELECT 42 as num');
13
+ * conn.close();
14
+ * db.close();
18
15
  */
19
16
 
20
- import { dlopen, ptr } from 'bun:ffi';
21
- import { fileURLToPath } from 'url';
22
- import { dirname, join } from 'path';
23
- import { platform, arch } from 'process';
17
+ import { dlopen, ptr, CString, read as ffiRead } from 'bun:ffi';
18
+ import { platform } from 'process';
19
+ import { existsSync } from 'fs';
20
+
21
+ // ==============================================================================
22
+ // Find DuckDB Library
23
+ // ==============================================================================
24
+
25
+ function findDuckDBLibrary() {
26
+ const candidates = [];
27
+
28
+ if (platform === 'darwin') {
29
+ candidates.push(
30
+ '/opt/homebrew/lib/libduckdb.dylib',
31
+ '/usr/local/lib/libduckdb.dylib',
32
+ '/usr/lib/libduckdb.dylib',
33
+ );
34
+ } else if (platform === 'linux') {
35
+ candidates.push(
36
+ '/usr/lib/libduckdb.so',
37
+ '/usr/local/lib/libduckdb.so',
38
+ '/usr/lib/x86_64-linux-gnu/libduckdb.so',
39
+ '/usr/lib/aarch64-linux-gnu/libduckdb.so',
40
+ );
41
+ } else if (platform === 'win32') {
42
+ candidates.push(
43
+ 'C:\\Program Files\\DuckDB\\duckdb.dll',
44
+ 'duckdb.dll',
45
+ );
46
+ }
47
+
48
+ if (process.env.DUCKDB_LIB_PATH) {
49
+ candidates.unshift(process.env.DUCKDB_LIB_PATH);
50
+ }
51
+
52
+ for (const path of candidates) {
53
+ if (existsSync(path)) return path;
54
+ }
55
+
56
+ throw new Error(
57
+ `Could not find DuckDB library. Tried:\n${candidates.join('\n')}\n\n` +
58
+ `Install DuckDB or set DUCKDB_LIB_PATH environment variable.`
59
+ );
60
+ }
24
61
 
25
- const __dirname = dirname(fileURLToPath(import.meta.url));
62
+ const libPath = findDuckDBLibrary();
26
63
 
27
- // Map platform/arch to library path
28
- const archMap = { 'arm64': 'arm64', 'x64': 'x64', 'x86_64': 'x64' };
29
- const target = `${platform}-${archMap[arch] || arch}`;
30
- const libPath = join(__dirname, `${target}/duckdb.node`);
64
+ // ==============================================================================
65
+ // Load DuckDB C API (modern chunk-based + lifecycle functions)
66
+ // ==============================================================================
31
67
 
32
- // Load the Zig library
33
68
  const lib = dlopen(libPath, {
34
69
  // Database lifecycle
35
- duck_open: { args: ['ptr'], returns: 'usize' },
36
- duck_close: { args: ['usize'], returns: 'void' },
37
- duck_connect: { args: ['usize'], returns: 'usize' },
38
- duck_disconnect: { args: ['usize'], returns: 'void' },
39
-
40
- // Binary serialization (for DuckDB UI protocol)
41
- duck_query_binary: { args: ['usize', 'ptr', 'ptr', 'usize', 'u64'], returns: 'usize' },
42
- duck_tokenize: { args: ['ptr', 'usize', 'ptr', 'usize'], returns: 'usize' },
43
- duck_empty_result: { args: ['ptr', 'usize'], returns: 'usize' },
44
- duck_error_result: { args: ['ptr', 'ptr', 'usize'], returns: 'usize' },
45
-
46
- // Basic query API (for JSON endpoints)
47
- duck_query: { args: ['usize', 'ptr'], returns: 'usize' },
48
- duck_free_result: { args: ['usize'], returns: 'void' },
49
- duck_result_error: { args: ['usize'], returns: 'ptr' },
50
- duck_row_count: { args: ['usize'], returns: 'u64' },
51
- duck_column_count: { args: ['usize'], returns: 'u64' },
52
- duck_column_name: { args: ['usize', 'u64'], returns: 'ptr' },
53
- duck_column_type: { args: ['usize', 'u64'], returns: 'u32' },
54
- duck_value_is_null: { args: ['usize', 'u64', 'u64'], returns: 'bool' },
55
- duck_value_varchar: { args: ['usize', 'u64', 'u64'], returns: 'ptr' },
56
- duck_value_int64: { args: ['usize', 'u64', 'u64'], returns: 'i64' },
57
- duck_value_double: { args: ['usize', 'u64', 'u64'], returns: 'f64' },
58
- duck_value_boolean: { args: ['usize', 'u64', 'u64'], returns: 'bool' },
59
- duck_free: { args: ['usize'], returns: 'void' },
70
+ duckdb_open: { args: ['ptr', 'ptr'], returns: 'i32' },
71
+ duckdb_close: { args: ['ptr'], returns: 'void' },
72
+
73
+ // Connection lifecycle
74
+ duckdb_connect: { args: ['ptr', 'ptr'], returns: 'i32' },
75
+ duckdb_disconnect: { args: ['ptr'], returns: 'void' },
76
+
77
+ // Query execution
78
+ duckdb_query: { args: ['ptr', 'ptr', 'ptr'], returns: 'i32' },
79
+ duckdb_destroy_result: { args: ['ptr'], returns: 'void' },
80
+
81
+ // Prepared statements
82
+ duckdb_prepare: { args: ['ptr', 'ptr', 'ptr'], returns: 'i32' },
83
+ duckdb_prepare_error: { args: ['ptr'], returns: 'ptr' },
84
+ duckdb_destroy_prepare: { args: ['ptr'], returns: 'void' },
85
+ duckdb_bind_null: { args: ['ptr', 'u64'], returns: 'i32' },
86
+ duckdb_bind_boolean: { args: ['ptr', 'u64', 'bool'], returns: 'i32' },
87
+ duckdb_bind_int32: { args: ['ptr', 'u64', 'i32'], returns: 'i32' },
88
+ duckdb_bind_int64: { args: ['ptr', 'u64', 'i64'], returns: 'i32' },
89
+ duckdb_bind_double: { args: ['ptr', 'u64', 'f64'], returns: 'i32' },
90
+ duckdb_bind_varchar: { args: ['ptr', 'u64', 'ptr'], returns: 'i32' },
91
+ duckdb_execute_prepared: { args: ['ptr', 'ptr'], returns: 'i32' },
92
+
93
+ // Result inspection
94
+ duckdb_column_count: { args: ['ptr'], returns: 'u64' },
95
+ duckdb_column_name: { args: ['ptr', 'u64'], returns: 'ptr' },
96
+ duckdb_column_type: { args: ['ptr', 'u64'], returns: 'i32' },
97
+ duckdb_result_error: { args: ['ptr'], returns: 'ptr' },
98
+
99
+ // Modern chunk-based API (non-deprecated)
100
+ duckdb_fetch_chunk: { args: ['ptr'], returns: 'ptr' },
101
+ duckdb_data_chunk_get_size: { args: ['ptr'], returns: 'u64' },
102
+ duckdb_data_chunk_get_vector: { args: ['ptr', 'u64'], returns: 'ptr' },
103
+ duckdb_vector_get_data: { args: ['ptr'], returns: 'ptr' },
104
+ duckdb_vector_get_validity: { args: ['ptr'], returns: 'ptr' },
105
+ duckdb_destroy_data_chunk: { args: ['ptr'], returns: 'void' },
106
+
107
+ // Logical type introspection (for DECIMAL, ENUM, LIST, STRUCT)
108
+ duckdb_column_logical_type: { args: ['ptr', 'u64'], returns: 'ptr' },
109
+ duckdb_destroy_logical_type: { args: ['ptr'], returns: 'void' },
110
+ duckdb_get_type_id: { args: ['ptr'], returns: 'i32' },
111
+ duckdb_decimal_width: { args: ['ptr'], returns: 'u8' },
112
+ duckdb_decimal_scale: { args: ['ptr'], returns: 'u8' },
113
+ duckdb_decimal_internal_type: { args: ['ptr'], returns: 'i32' },
114
+ duckdb_enum_internal_type: { args: ['ptr'], returns: 'i32' },
115
+ duckdb_enum_dictionary_size: { args: ['ptr'], returns: 'u32' },
116
+ duckdb_enum_dictionary_value: { args: ['ptr', 'u64'], returns: 'ptr' },
117
+
118
+ // Nested type vector access (LIST, STRUCT)
119
+ duckdb_list_vector_get_child: { args: ['ptr'], returns: 'ptr' },
120
+ duckdb_list_vector_get_size: { args: ['ptr'], returns: 'u64' }, // Not yet used; available for LIST size checks
121
+ duckdb_struct_vector_get_child: { args: ['ptr', 'u64'], returns: 'ptr' },
122
+ duckdb_struct_type_child_count: { args: ['ptr'], returns: 'u64' },
123
+ duckdb_struct_type_child_name: { args: ['ptr', 'u64'], returns: 'ptr' },
124
+ duckdb_struct_type_child_type: { args: ['ptr', 'u64'], returns: 'ptr' },
125
+ duckdb_list_type_child_type: { args: ['ptr'], returns: 'ptr' },
126
+
127
+ // Memory
128
+ duckdb_free: { args: ['ptr'], returns: 'void' },
129
+
130
+ // Library info
131
+ duckdb_library_version: { args: [], returns: 'ptr' },
60
132
  }).symbols;
61
133
 
62
- import { CString } from 'bun:ffi';
134
+ // ==============================================================================
135
+ // DuckDB Type Constants
136
+ // ==============================================================================
63
137
 
64
- // Pre-allocated buffer for query results (16MB default)
65
- const DEFAULT_BUFFER_SIZE = 16 * 1024 * 1024;
66
- let sharedBuffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
138
+ const DUCKDB_TYPE = {
139
+ INVALID: 0,
140
+ BOOLEAN: 1,
141
+ TINYINT: 2,
142
+ SMALLINT: 3,
143
+ INTEGER: 4,
144
+ BIGINT: 5,
145
+ UTINYINT: 6,
146
+ USMALLINT: 7,
147
+ UINTEGER: 8,
148
+ UBIGINT: 9,
149
+ FLOAT: 10,
150
+ DOUBLE: 11,
151
+ TIMESTAMP: 12,
152
+ DATE: 13,
153
+ TIME: 14,
154
+ INTERVAL: 15,
155
+ HUGEINT: 16,
156
+ VARCHAR: 17,
157
+ BLOB: 18,
158
+ DECIMAL: 19,
159
+ TIMESTAMP_S: 20,
160
+ TIMESTAMP_MS: 21,
161
+ TIMESTAMP_NS: 22,
162
+ ENUM: 23,
163
+ LIST: 24,
164
+ STRUCT: 25,
165
+ MAP: 26,
166
+ UUID: 27,
167
+ UNION: 28,
168
+ BIT: 29,
169
+ TIMESTAMP_TZ: 32,
170
+ };
67
171
 
68
- const utf8Encoder = new TextEncoder();
172
+ export { DUCKDB_TYPE };
173
+
174
+ // ==============================================================================
175
+ // Helper Functions
176
+ // ==============================================================================
177
+
178
+ // Async mutex to serialize FFI calls
179
+ let ffiLock = Promise.resolve();
180
+ function withLock(fn) {
181
+ const prev = ffiLock;
182
+ let resolve;
183
+ ffiLock = new Promise(r => resolve = r);
184
+ return prev.then(() => {
185
+ try { return fn(); }
186
+ finally { resolve(); }
187
+ });
188
+ }
69
189
 
70
- /**
71
- * Open a DuckDB database
72
- * @param {string|null} path - Path to database file, or null/:memory: for in-memory
73
- * @returns {Database}
74
- */
75
- export function open(path) {
76
- return new Database(path);
190
+ const encoder = new TextEncoder();
191
+ const decoder = new TextDecoder();
192
+
193
+ function toCString(str) {
194
+ return encoder.encode(str + '\0');
77
195
  }
78
196
 
197
+ function fromCString(p) {
198
+ if (!p) return null;
199
+ return new CString(p).toString();
200
+ }
201
+
202
+ function allocPtr() {
203
+ return new Uint8Array(8);
204
+ }
205
+
206
+ function readPtr(buf) {
207
+ return ffiRead.ptr(ptr(buf), 0);
208
+ }
209
+
210
+ // Read a duckdb_string_t (16 bytes) from a data pointer at a given row offset
211
+ function readString(dataPtr, row) {
212
+ const offset = row * 16; // duckdb_string_t is 16 bytes
213
+ const length = ffiRead.u32(dataPtr, offset);
214
+
215
+ if (length <= 12) {
216
+ // Inlined: bytes 4-15 contain the string data
217
+ const bytes = new Uint8Array(length);
218
+ for (let i = 0; i < length; i++) {
219
+ bytes[i] = ffiRead.u8(dataPtr, offset + 4 + i);
220
+ }
221
+ return decoder.decode(bytes);
222
+ } else {
223
+ // Pointer: bytes 4-7 are prefix, bytes 8-15 are pointer to string data
224
+ const strPtr = ffiRead.ptr(dataPtr, offset + 8);
225
+ if (!strPtr) return null;
226
+ const bytes = new Uint8Array(length);
227
+ for (let i = 0; i < length; i++) {
228
+ bytes[i] = ffiRead.u8(strPtr, i);
229
+ }
230
+ return decoder.decode(bytes);
231
+ }
232
+ }
233
+
234
+ // Check if a row is valid (not NULL) in a validity mask
235
+ function isValid(validityPtr, row) {
236
+ if (!validityPtr) return true; // NULL validity = all valid
237
+ const entryIdx = Math.floor(row / 64);
238
+ const bitIdx = row % 64;
239
+ const entry = ffiRead.u64(validityPtr, entryIdx * 8);
240
+ return (entry & (1n << BigInt(bitIdx))) !== 0n;
241
+ }
242
+
243
+ // Format a hugeint (16 bytes: lower uint64 at offset 0, upper int64 at offset 8) as UUID
244
+ function readUUID(dataPtr, row) {
245
+ const offset = row * 16;
246
+ const lower = ffiRead.u64(dataPtr, offset);
247
+ const upper = ffiRead.i64(dataPtr, offset + 8);
248
+
249
+ // DuckDB stores UUID as hugeint with XOR on the upper bits
250
+ // Upper 64 bits have sign bit flipped for sorting
251
+ const hi = BigInt(upper) ^ (1n << 63n);
252
+ const lo = BigInt(lower);
253
+
254
+ const hex = ((hi << 64n) | lo).toString(16).padStart(32, '0');
255
+ return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20)}`;
256
+ }
257
+
258
+ // ==============================================================================
259
+ // Database Class
260
+ // ==============================================================================
261
+
79
262
  class Database {
80
- #handle;
263
+ #ptrBuf = null;
264
+ #handle = null;
81
265
 
82
266
  constructor(path) {
83
- const dbPath = path === null || path === ':memory:' ? null : path;
84
- const pathBytes = dbPath ? utf8Encoder.encode(dbPath + '\0') : null;
85
- this.#handle = lib.duck_open(pathBytes ? ptr(pathBytes) : null);
86
- if (this.#handle === 0n || this.#handle === 0) {
87
- throw new Error('Failed to open database');
88
- }
267
+ this.#ptrBuf = allocPtr();
268
+ const pathBytes = path && path !== ':memory:' ? toCString(path) : null;
269
+ const result = lib.duckdb_open(pathBytes ? ptr(pathBytes) : null, ptr(this.#ptrBuf));
270
+ if (result !== 0) throw new Error('Failed to open database');
271
+ this.#handle = readPtr(this.#ptrBuf);
89
272
  }
90
273
 
91
274
  get handle() { return this.#handle; }
275
+ get ptrBuf() { return this.#ptrBuf; }
92
276
 
93
- connect() {
94
- return new Connection(this.#handle);
95
- }
277
+ connect() { return new Connection(this); }
96
278
 
97
279
  close() {
98
- if (this.#handle) {
99
- lib.duck_close(this.#handle);
280
+ if (this.#ptrBuf) {
281
+ lib.duckdb_close(ptr(this.#ptrBuf));
282
+ this.#ptrBuf = null;
100
283
  this.#handle = null;
101
284
  }
102
285
  }
103
286
  }
104
287
 
105
- // DuckDB type constants
106
- const DUCKDB_TYPE = {
107
- INVALID: 0, BOOLEAN: 1, TINYINT: 2, SMALLINT: 3, INTEGER: 4, BIGINT: 5,
108
- UTINYINT: 6, USMALLINT: 7, UINTEGER: 8, UBIGINT: 9, FLOAT: 10, DOUBLE: 11,
109
- TIMESTAMP: 12, DATE: 13, TIME: 14, INTERVAL: 15, HUGEINT: 16, VARCHAR: 17,
110
- BLOB: 18, DECIMAL: 19, TIMESTAMP_S: 20, TIMESTAMP_MS: 21, TIMESTAMP_NS: 22,
111
- ENUM: 23, LIST: 24, STRUCT: 25, MAP: 26, UUID: 27,
112
- };
288
+ // ==============================================================================
289
+ // Connection Class
290
+ // ==============================================================================
113
291
 
114
292
  class Connection {
115
- #handle;
116
-
117
- constructor(dbHandle) {
118
- this.#handle = lib.duck_connect(dbHandle);
119
- if (this.#handle === 0n || this.#handle === 0) {
120
- throw new Error('Failed to create connection');
121
- }
293
+ #ptrBuf = null;
294
+ #handle = null;
295
+ #db = null;
296
+
297
+ constructor(db) {
298
+ this.#db = db;
299
+ this.#ptrBuf = allocPtr();
300
+ const result = lib.duckdb_connect(db.handle, ptr(this.#ptrBuf));
301
+ if (result !== 0) throw new Error('Failed to create connection');
302
+ this.#handle = readPtr(this.#ptrBuf);
122
303
  }
123
304
 
124
305
  get handle() { return this.#handle; }
306
+ get ptrBuf() { return this.#ptrBuf; }
125
307
 
126
308
  /**
127
- * Execute SQL and return rows as array of objects
309
+ * Execute a SQL query and return results as array of objects
128
310
  * @param {string} sql - SQL query
129
- * @returns {object[]} Array of row objects
311
+ * @param {any[]} params - Optional parameters for prepared statement
312
+ * @returns {Promise<object[]>} Array of row objects
130
313
  */
131
- query(sql) {
132
- const sqlBytes = utf8Encoder.encode(sql + '\0');
133
- const result = lib.duck_query(this.#handle, ptr(sqlBytes));
314
+ query(sql, params = []) {
315
+ return withLock(() => {
316
+ if (params.length > 0) return this.#queryPrepared(sql, params);
317
+ return this.#querySimple(sql);
318
+ });
319
+ }
320
+
321
+ #querySimple(sql) {
322
+ const resultPtr = new Uint8Array(64); // duckdb_result struct is ~48 bytes
323
+ const sqlBytes = toCString(sql);
324
+ lib.duckdb_query(this.#handle, ptr(sqlBytes), ptr(resultPtr));
325
+
326
+ const rp = ptr(resultPtr);
327
+ const errorPtr = lib.duckdb_result_error(rp);
328
+ if (errorPtr) {
329
+ const error = fromCString(errorPtr);
330
+ lib.duckdb_destroy_result(rp);
331
+ throw new Error(error);
332
+ }
134
333
 
135
334
  try {
136
- // Check for error
137
- const errorPtr = lib.duck_result_error(result);
335
+ return this.#extractChunks(resultPtr);
336
+ } finally {
337
+ lib.duckdb_destroy_result(rp);
338
+ }
339
+ }
340
+
341
+ #queryPrepared(sql, params) {
342
+ const stmtPtr = allocPtr();
343
+ const sqlBytes = toCString(sql);
344
+
345
+ const prepStatus = lib.duckdb_prepare(this.#handle, ptr(sqlBytes), ptr(stmtPtr));
346
+ if (prepStatus !== 0) {
347
+ const stmtHandle = readPtr(stmtPtr);
348
+ if (stmtHandle) {
349
+ const errPtr = lib.duckdb_prepare_error(stmtHandle);
350
+ const errMsg = errPtr ? fromCString(errPtr) : 'Failed to prepare statement';
351
+ lib.duckdb_destroy_prepare(ptr(stmtPtr));
352
+ throw new Error(errMsg);
353
+ }
354
+ throw new Error('Failed to prepare statement');
355
+ }
356
+
357
+ const stmtHandle = readPtr(stmtPtr);
358
+
359
+ try {
360
+ for (let i = 0; i < params.length; i++) {
361
+ const paramIdx = BigInt(i + 1);
362
+ const value = params[i];
363
+
364
+ if (value === null || value === undefined) {
365
+ lib.duckdb_bind_null(stmtHandle, paramIdx);
366
+ } else if (typeof value === 'boolean') {
367
+ lib.duckdb_bind_boolean(stmtHandle, paramIdx, value);
368
+ } else if (typeof value === 'number') {
369
+ if (Number.isInteger(value)) {
370
+ lib.duckdb_bind_int64(stmtHandle, paramIdx, BigInt(value));
371
+ } else {
372
+ lib.duckdb_bind_double(stmtHandle, paramIdx, value);
373
+ }
374
+ } else if (typeof value === 'bigint') {
375
+ lib.duckdb_bind_int64(stmtHandle, paramIdx, value);
376
+ } else if (value instanceof Date) {
377
+ const strBytes = toCString(value.toISOString());
378
+ lib.duckdb_bind_varchar(stmtHandle, paramIdx, ptr(strBytes));
379
+ } else {
380
+ const strBytes = toCString(String(value));
381
+ lib.duckdb_bind_varchar(stmtHandle, paramIdx, ptr(strBytes));
382
+ }
383
+ }
384
+
385
+ const resultPtr = new Uint8Array(64); // duckdb_result struct is ~48 bytes
386
+ lib.duckdb_execute_prepared(stmtHandle, ptr(resultPtr));
387
+
388
+ const rp = ptr(resultPtr);
389
+ const errorPtr = lib.duckdb_result_error(rp);
138
390
  if (errorPtr) {
139
- throw new Error(new CString(errorPtr).toString());
391
+ const error = fromCString(errorPtr);
392
+ lib.duckdb_destroy_result(rp);
393
+ throw new Error(error);
394
+ }
395
+
396
+ try {
397
+ return this.#extractChunks(resultPtr);
398
+ } finally {
399
+ lib.duckdb_destroy_result(rp);
400
+ }
401
+ } finally {
402
+ lib.duckdb_destroy_prepare(ptr(stmtPtr));
403
+ }
404
+ }
405
+
406
+ // ---------------------------------------------------------------------------
407
+ // Modern chunk-based result extraction
408
+ //
409
+ // Uses duckdb_fetch_chunk + duckdb_vector_get_data to read values directly
410
+ // from DuckDB's columnar memory. No deprecated duckdb_value_* functions.
411
+ //
412
+ // Contract:
413
+ // BIGINT/UBIGINT → number (lossy above 2^53, JSON-safe)
414
+ // DECIMAL/HUGEINT → string (preserves precision)
415
+ // All timestamps → Date (UTC)
416
+ // UUID → string (formatted)
417
+ // VARCHAR/BLOB → string
418
+ // ENUM → string (dictionary lookup)
419
+ // LIST → array, STRUCT → object, MAP → object
420
+ // ---------------------------------------------------------------------------
421
+
422
+ #extractChunks(resultPtr) {
423
+ const rp = ptr(resultPtr);
424
+ const colCount = Number(lib.duckdb_column_count(rp));
425
+
426
+ // Get column info + logical type metadata for complex types
427
+ const columns = [];
428
+ for (let c = 0; c < colCount; c++) {
429
+ const namePtr = lib.duckdb_column_name(rp, BigInt(c));
430
+ const type = lib.duckdb_column_type(rp, BigInt(c));
431
+ const col = {
432
+ name: fromCString(namePtr) || `col${c}`,
433
+ type,
434
+ typeName: this.#typeName(type)
435
+ };
436
+
437
+ // Get logical type metadata for complex types
438
+ if (type === DUCKDB_TYPE.DECIMAL || type === DUCKDB_TYPE.ENUM ||
439
+ type === DUCKDB_TYPE.LIST || type === DUCKDB_TYPE.STRUCT || type === DUCKDB_TYPE.MAP) {
440
+ const logType = lib.duckdb_column_logical_type(rp, BigInt(c));
441
+ if (logType) {
442
+ if (type === DUCKDB_TYPE.DECIMAL) {
443
+ col.decimalScale = lib.duckdb_decimal_scale(logType);
444
+ col.decimalInternalType = lib.duckdb_decimal_internal_type(logType);
445
+ } else if (type === DUCKDB_TYPE.ENUM) {
446
+ col.enumInternalType = lib.duckdb_enum_internal_type(logType);
447
+ const dictSize = lib.duckdb_enum_dictionary_size(logType);
448
+ col.enumDict = [];
449
+ for (let d = 0; d < dictSize; d++) {
450
+ const vp = lib.duckdb_enum_dictionary_value(logType, BigInt(d));
451
+ col.enumDict.push(fromCString(vp));
452
+ if (vp) lib.duckdb_free(vp);
453
+ }
454
+ } else if (type === DUCKDB_TYPE.LIST) {
455
+ const childLogType = lib.duckdb_list_type_child_type(logType);
456
+ if (childLogType) {
457
+ col.childType = lib.duckdb_get_type_id(childLogType);
458
+ const ltBuf2 = allocPtr();
459
+ new DataView(ltBuf2.buffer).setBigUint64(0, BigInt(childLogType), true);
460
+ lib.duckdb_destroy_logical_type(ptr(ltBuf2));
461
+ }
462
+ } else if (type === DUCKDB_TYPE.STRUCT) {
463
+ const childCount = Number(lib.duckdb_struct_type_child_count(logType));
464
+ col.structChildren = [];
465
+ for (let i = 0; i < childCount; i++) {
466
+ const np = lib.duckdb_struct_type_child_name(logType, BigInt(i));
467
+ const ct = lib.duckdb_struct_type_child_type(logType, BigInt(i));
468
+ const childType = ct ? lib.duckdb_get_type_id(ct) : DUCKDB_TYPE.VARCHAR;
469
+ col.structChildren.push({ name: fromCString(np) || `f${i}`, type: childType });
470
+ if (np) lib.duckdb_free(np);
471
+ if (ct) {
472
+ const ltBuf2 = allocPtr();
473
+ new DataView(ltBuf2.buffer).setBigUint64(0, BigInt(ct), true);
474
+ lib.duckdb_destroy_logical_type(ptr(ltBuf2));
475
+ }
476
+ }
477
+ } else if (type === DUCKDB_TYPE.MAP) {
478
+ const keyLogType = lib.duckdb_list_type_child_type(logType); // MAP child is STRUCT
479
+ if (keyLogType) {
480
+ // MAP's child is a STRUCT with key (0) and value (1)
481
+ const keyType = lib.duckdb_struct_type_child_type(keyLogType, 0n);
482
+ const valType = lib.duckdb_struct_type_child_type(keyLogType, 1n);
483
+ col.keyType = keyType ? lib.duckdb_get_type_id(keyType) : DUCKDB_TYPE.VARCHAR;
484
+ col.valueType = valType ? lib.duckdb_get_type_id(valType) : DUCKDB_TYPE.VARCHAR;
485
+ if (keyType) {
486
+ const b = allocPtr(); new DataView(b.buffer).setBigUint64(0, BigInt(keyType), true);
487
+ lib.duckdb_destroy_logical_type(ptr(b));
488
+ }
489
+ if (valType) {
490
+ const b = allocPtr(); new DataView(b.buffer).setBigUint64(0, BigInt(valType), true);
491
+ lib.duckdb_destroy_logical_type(ptr(b));
492
+ }
493
+ const b = allocPtr(); new DataView(b.buffer).setBigUint64(0, BigInt(keyLogType), true);
494
+ lib.duckdb_destroy_logical_type(ptr(b));
495
+ }
496
+ }
497
+ const ltBuf = allocPtr();
498
+ new DataView(ltBuf.buffer).setBigUint64(0, BigInt(logType), true);
499
+ lib.duckdb_destroy_logical_type(ptr(ltBuf));
500
+ }
140
501
  }
141
502
 
142
- const rowCount = Number(lib.duck_row_count(result));
143
- const colCount = Number(lib.duck_column_count(result));
503
+ columns.push(col);
504
+ }
505
+
506
+ // Fetch chunks and extract rows
507
+ const rows = [];
508
+ const chunkBuf = allocPtr();
509
+
510
+ while (true) {
511
+ const chunk = lib.duckdb_fetch_chunk(rp);
512
+ if (!chunk) break;
513
+
514
+ const chunkSize = Number(lib.duckdb_data_chunk_get_size(chunk));
515
+ if (chunkSize === 0) {
516
+ const dv = new DataView(chunkBuf.buffer);
517
+ dv.setBigUint64(0, BigInt(chunk), true);
518
+ lib.duckdb_destroy_data_chunk(ptr(chunkBuf));
519
+ break;
520
+ }
144
521
 
145
- // Get column names and types
146
- const columns = [];
147
- const types = [];
522
+ // Get vectors for each column (data + validity + handle for nested types)
523
+ const colVec = [];
524
+ const colData = [];
525
+ const colValidity = [];
148
526
  for (let c = 0; c < colCount; c++) {
149
- const namePtr = lib.duck_column_name(result, BigInt(c));
150
- columns.push(namePtr ? new CString(namePtr).toString() : `col${c}`);
151
- types.push(Number(lib.duck_column_type(result, BigInt(c))));
527
+ const vec = lib.duckdb_data_chunk_get_vector(chunk, BigInt(c));
528
+ colVec.push(vec);
529
+ colData.push(lib.duckdb_vector_get_data(vec));
530
+ colValidity.push(lib.duckdb_vector_get_validity(vec));
152
531
  }
153
532
 
154
- // Build row objects
155
- const rows = [];
156
- for (let r = 0; r < rowCount; r++) {
533
+ // Extract rows from this chunk
534
+ for (let r = 0; r < chunkSize; r++) {
157
535
  const row = {};
158
536
  for (let c = 0; c < colCount; c++) {
159
- const col = BigInt(c);
160
- const rowIdx = BigInt(r);
161
-
162
- if (lib.duck_value_is_null(result, col, rowIdx)) {
163
- row[columns[c]] = null;
537
+ const col = columns[c];
538
+ if (!isValid(colValidity[c], r)) {
539
+ row[col.name] = null;
164
540
  } else {
165
- const type = types[c];
166
- switch (type) {
167
- case DUCKDB_TYPE.BOOLEAN:
168
- row[columns[c]] = lib.duck_value_boolean(result, col, rowIdx);
169
- break;
170
- case DUCKDB_TYPE.TINYINT:
171
- case DUCKDB_TYPE.SMALLINT:
172
- case DUCKDB_TYPE.INTEGER:
173
- case DUCKDB_TYPE.BIGINT:
174
- case DUCKDB_TYPE.UTINYINT:
175
- case DUCKDB_TYPE.USMALLINT:
176
- case DUCKDB_TYPE.UINTEGER:
177
- case DUCKDB_TYPE.UBIGINT:
178
- case DUCKDB_TYPE.DATE:
179
- case DUCKDB_TYPE.TIME:
180
- case DUCKDB_TYPE.TIMESTAMP:
181
- case DUCKDB_TYPE.TIMESTAMP_S:
182
- case DUCKDB_TYPE.TIMESTAMP_MS:
183
- case DUCKDB_TYPE.TIMESTAMP_NS:
184
- row[columns[c]] = Number(lib.duck_value_int64(result, col, rowIdx));
185
- break;
186
- case DUCKDB_TYPE.FLOAT:
187
- case DUCKDB_TYPE.DOUBLE:
188
- case DUCKDB_TYPE.DECIMAL:
189
- row[columns[c]] = lib.duck_value_double(result, col, rowIdx);
190
- break;
191
- default:
192
- // VARCHAR, BLOB, UUID, etc - get as string
193
- const strPtr = lib.duck_value_varchar(result, col, rowIdx);
194
- if (strPtr) {
195
- row[columns[c]] = new CString(strPtr).toString();
196
- lib.duck_free(strPtr); // Free the allocated string
197
- } else {
198
- row[columns[c]] = null;
199
- }
200
- }
541
+ row[col.name] = this.#readValue(colData[c], r, col.type, col, colVec[c]);
201
542
  }
202
543
  }
203
544
  rows.push(row);
204
545
  }
205
546
 
206
- return rows;
207
- } finally {
208
- lib.duck_free_result(result);
547
+ // Destroy chunk
548
+ const dv = new DataView(chunkBuf.buffer);
549
+ dv.setBigUint64(0, BigInt(chunk), true);
550
+ lib.duckdb_destroy_data_chunk(ptr(chunkBuf));
209
551
  }
210
- }
211
552
 
212
- close() {
213
- if (this.#handle) {
214
- lib.duck_disconnect(this.#handle);
215
- this.#handle = null;
216
- }
553
+ rows.columns = columns;
554
+ return rows;
217
555
  }
218
- }
219
556
 
220
- /**
221
- * Execute SQL and return binary result (DuckDB UI protocol format)
222
- *
223
- * @param {bigint|number} connHandle - Connection handle from conn.handle
224
- * @param {string} sql - SQL query to execute
225
- * @param {number} rowLimit - Maximum rows to return (default 10000)
226
- * @returns {ArrayBuffer} - Binary result ready for HTTP response
227
- */
228
- export function queryBinary(connHandle, sql, rowLimit = 10000) {
229
- const sqlBytes = utf8Encoder.encode(sql + '\0');
230
- const bytesWritten = lib.duck_query_binary(
231
- connHandle,
232
- ptr(sqlBytes),
233
- ptr(sharedBuffer),
234
- sharedBuffer.length,
235
- BigInt(rowLimit)
236
- );
557
+ // ---------------------------------------------------------------------------
558
+ // Read a single value from raw vector memory at a given row index.
559
+ // This is the core type dispatch — reads directly from DuckDB's columnar
560
+ // memory layout without any deprecated per-value API calls.
561
+ // ---------------------------------------------------------------------------
562
+
563
+ // col = column metadata (includes decimalScale, enumDict, etc.)
564
+ // vec = vector handle (for nested type child access)
565
+ #readValue(dataPtr, row, type, col, vec) {
566
+ switch (type) {
567
+ case DUCKDB_TYPE.BOOLEAN:
568
+ return ffiRead.u8(dataPtr, row) !== 0;
569
+
570
+ case DUCKDB_TYPE.TINYINT:
571
+ return ffiRead.i8(dataPtr, row);
572
+ case DUCKDB_TYPE.SMALLINT:
573
+ return ffiRead.i16(dataPtr, row * 2);
574
+ case DUCKDB_TYPE.INTEGER:
575
+ return ffiRead.i32(dataPtr, row * 4);
576
+ case DUCKDB_TYPE.UTINYINT:
577
+ return ffiRead.u8(dataPtr, row);
578
+ case DUCKDB_TYPE.USMALLINT:
579
+ return ffiRead.u16(dataPtr, row * 2);
580
+ case DUCKDB_TYPE.UINTEGER:
581
+ return ffiRead.u32(dataPtr, row * 4);
582
+
583
+ case DUCKDB_TYPE.BIGINT:
584
+ return Number(ffiRead.i64(dataPtr, row * 8));
585
+ case DUCKDB_TYPE.UBIGINT:
586
+ return Number(ffiRead.u64(dataPtr, row * 8));
587
+
588
+ case DUCKDB_TYPE.FLOAT:
589
+ return ffiRead.f32(dataPtr, row * 4);
590
+ case DUCKDB_TYPE.DOUBLE:
591
+ return ffiRead.f64(dataPtr, row * 8);
592
+
593
+ case DUCKDB_TYPE.HUGEINT: {
594
+ const lo = ffiRead.u64(dataPtr, row * 16);
595
+ const hi = ffiRead.i64(dataPtr, row * 16 + 8);
596
+ const value = (BigInt(hi) << 64n) | BigInt(lo);
597
+ return value.toString();
598
+ }
237
599
 
238
- // Return a copy of the used portion
239
- return sharedBuffer.slice(0, Number(bytesWritten)).buffer;
240
- }
600
+ case DUCKDB_TYPE.DECIMAL: {
601
+ // Read based on internal type, divide by 10^scale, return as string
602
+ const scale = col?.decimalScale || 0;
603
+ const internalType = col?.decimalInternalType || DUCKDB_TYPE.DOUBLE;
604
+ let raw;
605
+ switch (internalType) {
606
+ case DUCKDB_TYPE.SMALLINT:
607
+ raw = BigInt(ffiRead.i16(dataPtr, row * 2)); break;
608
+ case DUCKDB_TYPE.INTEGER:
609
+ raw = BigInt(ffiRead.i32(dataPtr, row * 4)); break;
610
+ case DUCKDB_TYPE.BIGINT:
611
+ raw = ffiRead.i64(dataPtr, row * 8); break;
612
+ case DUCKDB_TYPE.HUGEINT: {
613
+ const lo = ffiRead.u64(dataPtr, row * 16);
614
+ const hi = ffiRead.i64(dataPtr, row * 16 + 8);
615
+ raw = (BigInt(hi) << 64n) | BigInt(lo);
616
+ break;
617
+ }
618
+ default:
619
+ return ffiRead.f64(dataPtr, row * 8);
620
+ }
621
+ if (scale === 0) return raw.toString();
622
+ const divisor = 10n ** BigInt(scale);
623
+ const sign = raw < 0n ? '-' : '';
624
+ const abs = raw < 0n ? -raw : raw;
625
+ const intPart = abs / divisor;
626
+ const fracPart = abs % divisor;
627
+ return `${sign}${intPart}.${fracPart.toString().padStart(scale, '0')}`;
628
+ }
241
629
 
242
- /**
243
- * Tokenize SQL and return binary result (DuckDB UI protocol format)
244
- *
245
- * @param {string} sql - SQL to tokenize
246
- * @returns {ArrayBuffer} - Binary result ready for HTTP response
247
- */
248
- export function tokenize(sql) {
249
- const sqlBytes = utf8Encoder.encode(sql);
250
- const bytesWritten = lib.duck_tokenize(
251
- ptr(sqlBytes),
252
- sqlBytes.length,
253
- ptr(sharedBuffer),
254
- sharedBuffer.length
255
- );
630
+ case DUCKDB_TYPE.DATE: {
631
+ const days = ffiRead.i32(dataPtr, row * 4);
632
+ const ms = days * 86400000;
633
+ const d = new Date(ms);
634
+ return `${d.getUTCFullYear()}-${String(d.getUTCMonth()+1).padStart(2,'0')}-${String(d.getUTCDate()).padStart(2,'0')}`;
635
+ }
256
636
 
257
- return sharedBuffer.slice(0, Number(bytesWritten)).buffer;
258
- }
637
+ case DUCKDB_TYPE.TIMESTAMP:
638
+ case DUCKDB_TYPE.TIMESTAMP_S:
639
+ case DUCKDB_TYPE.TIMESTAMP_MS:
640
+ case DUCKDB_TYPE.TIMESTAMP_NS:
641
+ case DUCKDB_TYPE.TIMESTAMP_TZ: {
642
+ const micros = ffiRead.i64(dataPtr, row * 8);
643
+ return new Date(Number(micros / 1000n));
644
+ }
259
645
 
260
- /**
261
- * Create an empty binary result (for /ddb/interrupt)
262
- * @returns {ArrayBuffer}
263
- */
264
- export function emptyResult() {
265
- const bytesWritten = lib.duck_empty_result(
266
- ptr(sharedBuffer),
267
- sharedBuffer.length
268
- );
269
- return sharedBuffer.slice(0, Number(bytesWritten)).buffer;
270
- }
646
+ case DUCKDB_TYPE.TIME: {
647
+ const us = Number(ffiRead.i64(dataPtr, row * 8));
648
+ const totalSec = Math.floor(us / 1000000);
649
+ const h = Math.floor(totalSec / 3600);
650
+ const m = Math.floor((totalSec % 3600) / 60);
651
+ const s = totalSec % 60;
652
+ const frac = us % 1000000;
653
+ return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}` +
654
+ (frac > 0 ? `.${String(frac).padStart(6,'0').replace(/0+$/, '')}` : '');
655
+ }
271
656
 
272
- /**
273
- * Create an error binary result
274
- * @param {string} message - Error message
275
- * @returns {ArrayBuffer}
276
- */
277
- export function errorResult(message) {
278
- const msgBytes = utf8Encoder.encode(message + '\0');
279
- const bytesWritten = lib.duck_error_result(
280
- ptr(msgBytes),
281
- ptr(sharedBuffer),
282
- sharedBuffer.length
283
- );
284
- return sharedBuffer.slice(0, Number(bytesWritten)).buffer;
657
+ case DUCKDB_TYPE.UUID:
658
+ return readUUID(dataPtr, row);
659
+
660
+ case DUCKDB_TYPE.VARCHAR:
661
+ case DUCKDB_TYPE.BLOB:
662
+ return readString(dataPtr, row);
663
+
664
+ case DUCKDB_TYPE.INTERVAL: {
665
+ const months = ffiRead.i32(dataPtr, row * 16);
666
+ const days = ffiRead.i32(dataPtr, row * 16 + 4);
667
+ const micros = Number(ffiRead.i64(dataPtr, row * 16 + 8));
668
+ const parts = [];
669
+ if (months) parts.push(`${months} month${months !== 1 ? 's' : ''}`);
670
+ if (days) parts.push(`${days} day${days !== 1 ? 's' : ''}`);
671
+ if (micros) {
672
+ const secs = micros / 1000000;
673
+ parts.push(`${secs} second${secs !== 1 ? 's' : ''}`);
674
+ }
675
+ return parts.join(' ') || '0 seconds';
676
+ }
677
+
678
+ case DUCKDB_TYPE.ENUM: {
679
+ // Read integer index, look up string from pre-built dictionary
680
+ const dict = col?.enumDict;
681
+ if (!dict) return null;
682
+ const enumType = col?.enumInternalType || DUCKDB_TYPE.UTINYINT;
683
+ let idx;
684
+ switch (enumType) {
685
+ case DUCKDB_TYPE.UTINYINT: idx = ffiRead.u8(dataPtr, row); break;
686
+ case DUCKDB_TYPE.USMALLINT: idx = ffiRead.u16(dataPtr, row * 2); break;
687
+ case DUCKDB_TYPE.UINTEGER: idx = ffiRead.u32(dataPtr, row * 4); break;
688
+ default: idx = ffiRead.u32(dataPtr, row * 4); break;
689
+ }
690
+ return dict[idx] ?? null;
691
+ }
692
+
693
+ case DUCKDB_TYPE.LIST: {
694
+ // Read list entry (offset + length) from parent, then read child values
695
+ if (!vec) return null;
696
+ const entryOffset = row * 16; // duckdb_list_entry is 16 bytes (uint64 offset + uint64 length)
697
+ const listOffset = Number(ffiRead.u64(dataPtr, entryOffset));
698
+ const listLength = Number(ffiRead.u64(dataPtr, entryOffset + 8));
699
+ const childVec = lib.duckdb_list_vector_get_child(vec);
700
+ const childData = lib.duckdb_vector_get_data(childVec);
701
+ const childValidity = lib.duckdb_vector_get_validity(childVec);
702
+ // Determine child type from the vector's column type
703
+ const childType = col?.childType || DUCKDB_TYPE.VARCHAR;
704
+ const result = [];
705
+ for (let i = 0; i < listLength; i++) {
706
+ const childRow = listOffset + i;
707
+ if (!isValid(childValidity, childRow)) {
708
+ result.push(null);
709
+ } else {
710
+ result.push(this.#readValue(childData, childRow, childType, null, childVec));
711
+ }
712
+ }
713
+ return result;
714
+ }
715
+
716
+ case DUCKDB_TYPE.STRUCT: {
717
+ // Read each child vector at the row index
718
+ if (!vec) return null;
719
+ const obj = {};
720
+ const childCount = col?.structChildren?.length || 0;
721
+ for (let i = 0; i < childCount; i++) {
722
+ const child = col.structChildren[i];
723
+ const childVec = lib.duckdb_struct_vector_get_child(vec, BigInt(i));
724
+ const childData = lib.duckdb_vector_get_data(childVec);
725
+ const childValidity = lib.duckdb_vector_get_validity(childVec);
726
+ if (!isValid(childValidity, row)) {
727
+ obj[child.name] = null;
728
+ } else {
729
+ obj[child.name] = this.#readValue(childData, row, child.type, null, childVec);
730
+ }
731
+ }
732
+ return obj;
733
+ }
734
+
735
+ case DUCKDB_TYPE.MAP: {
736
+ // MAP is internally a LIST of STRUCTs with 'key' and 'value' fields
737
+ if (!vec) return null;
738
+ const entryOffset = row * 16;
739
+ const listOffset = Number(ffiRead.u64(dataPtr, entryOffset));
740
+ const listLength = Number(ffiRead.u64(dataPtr, entryOffset + 8));
741
+ const childVec = lib.duckdb_list_vector_get_child(vec);
742
+ // MAP child is a STRUCT with key (child 0) and value (child 1)
743
+ const keyVec = lib.duckdb_struct_vector_get_child(childVec, 0n);
744
+ const valVec = lib.duckdb_struct_vector_get_child(childVec, 1n);
745
+ const keyData = lib.duckdb_vector_get_data(keyVec);
746
+ const valData = lib.duckdb_vector_get_data(valVec);
747
+ const keyValidity = lib.duckdb_vector_get_validity(keyVec);
748
+ const valValidity = lib.duckdb_vector_get_validity(valVec);
749
+ const keyType = col?.keyType || DUCKDB_TYPE.VARCHAR;
750
+ const valType = col?.valueType || DUCKDB_TYPE.VARCHAR;
751
+ const obj = {};
752
+ for (let i = 0; i < listLength; i++) {
753
+ const childRow = listOffset + i;
754
+ const k = isValid(keyValidity, childRow)
755
+ ? this.#readValue(keyData, childRow, keyType, null, keyVec) : null;
756
+ const v = isValid(valValidity, childRow)
757
+ ? this.#readValue(valData, childRow, valType, null, valVec) : null;
758
+ if (k !== null) obj[String(k)] = v;
759
+ }
760
+ return obj;
761
+ }
762
+
763
+ case DUCKDB_TYPE.UNION:
764
+ case DUCKDB_TYPE.BIT:
765
+ return null; // Rarely used types
766
+
767
+ default:
768
+ try { return readString(dataPtr, row); }
769
+ catch { return null; }
770
+ }
771
+ }
772
+
773
+ #typeName(type) {
774
+ for (const [name, value] of Object.entries(DUCKDB_TYPE)) {
775
+ if (value === type) return name;
776
+ }
777
+ return 'UNKNOWN';
778
+ }
779
+
780
+ close() {
781
+ if (this.#ptrBuf) {
782
+ lib.duckdb_disconnect(ptr(this.#ptrBuf));
783
+ this.#ptrBuf = null;
784
+ this.#handle = null;
785
+ }
786
+ }
285
787
  }
286
788
 
287
- /**
288
- * Resize the shared buffer (for very large results)
289
- * @param {number} size - New buffer size in bytes
290
- */
291
- export function setBufferSize(size) {
292
- sharedBuffer = new Uint8Array(size);
789
+ // ==============================================================================
790
+ // Public API
791
+ // ==============================================================================
792
+
793
+ export function open(path) {
794
+ return new Database(path);
293
795
  }
294
796
 
295
- // Export types for compatibility
296
- export const Type = {
297
- BOOLEAN: 10,
298
- TINYINT: 11,
299
- SMALLINT: 12,
300
- INTEGER: 13,
301
- BIGINT: 14,
302
- DATE: 15,
303
- TIME: 16,
304
- TIMESTAMP_SEC: 17,
305
- TIMESTAMP_MS: 18,
306
- TIMESTAMP: 19,
307
- TIMESTAMP_NS: 20,
308
- DECIMAL: 21,
309
- FLOAT: 22,
310
- DOUBLE: 23,
311
- CHAR: 24,
312
- VARCHAR: 25,
313
- BLOB: 26,
314
- INTERVAL: 27,
315
- UTINYINT: 28,
316
- USMALLINT: 29,
317
- UINTEGER: 30,
318
- UBIGINT: 31,
319
- TIMESTAMP_TZ: 32,
320
- TIME_TZ: 34,
321
- HUGEINT: 50,
322
- UUID: 54,
323
- };
797
+ export function version() {
798
+ const versionPtr = lib.duckdb_library_version();
799
+ return fromCString(versionPtr);
800
+ }
324
801
 
325
- export default { open, queryBinary, tokenize, emptyResult, errorResult, setBufferSize, Type };
802
+ export { Database, Connection };