@duckdbfan/drizzle-duckdb 0.0.7 → 1.3.2

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.
Files changed (55) hide show
  1. package/README.md +349 -62
  2. package/dist/bin/duckdb-introspect.d.ts +2 -0
  3. package/dist/client.d.ts +42 -0
  4. package/dist/columns.d.ts +100 -9
  5. package/dist/dialect.d.ts +27 -2
  6. package/dist/driver.d.ts +53 -37
  7. package/dist/duckdb-introspect.mjs +2890 -0
  8. package/dist/helpers.d.ts +1 -0
  9. package/dist/helpers.mjs +360 -0
  10. package/dist/index.d.ts +7 -0
  11. package/dist/index.mjs +3015 -228
  12. package/dist/introspect.d.ts +74 -0
  13. package/dist/migrator.d.ts +3 -2
  14. package/dist/olap.d.ts +46 -0
  15. package/dist/operators.d.ts +8 -0
  16. package/dist/options.d.ts +7 -0
  17. package/dist/pool.d.ts +30 -0
  18. package/dist/select-builder.d.ts +31 -0
  19. package/dist/session.d.ts +33 -8
  20. package/dist/sql/ast-transformer.d.ts +33 -0
  21. package/dist/sql/result-mapper.d.ts +9 -0
  22. package/dist/sql/selection.d.ts +2 -0
  23. package/dist/sql/visitors/array-operators.d.ts +5 -0
  24. package/dist/sql/visitors/column-qualifier.d.ts +10 -0
  25. package/dist/sql/visitors/generate-series-alias.d.ts +13 -0
  26. package/dist/sql/visitors/union-with-hoister.d.ts +11 -0
  27. package/dist/utils.d.ts +2 -5
  28. package/dist/value-wrappers-core.d.ts +42 -0
  29. package/dist/value-wrappers.d.ts +8 -0
  30. package/package.json +53 -16
  31. package/src/bin/duckdb-introspect.ts +181 -0
  32. package/src/client.ts +528 -0
  33. package/src/columns.ts +420 -65
  34. package/src/dialect.ts +111 -15
  35. package/src/driver.ts +266 -180
  36. package/src/helpers.ts +18 -0
  37. package/src/index.ts +8 -1
  38. package/src/introspect.ts +935 -0
  39. package/src/migrator.ts +10 -5
  40. package/src/olap.ts +190 -0
  41. package/src/operators.ts +27 -0
  42. package/src/options.ts +25 -0
  43. package/src/pool.ts +274 -0
  44. package/src/select-builder.ts +110 -0
  45. package/src/session.ts +306 -66
  46. package/src/sql/ast-transformer.ts +170 -0
  47. package/src/sql/result-mapper.ts +303 -0
  48. package/src/sql/selection.ts +60 -0
  49. package/src/sql/visitors/array-operators.ts +214 -0
  50. package/src/sql/visitors/column-qualifier.ts +586 -0
  51. package/src/sql/visitors/generate-series-alias.ts +291 -0
  52. package/src/sql/visitors/union-with-hoister.ts +106 -0
  53. package/src/utils.ts +2 -222
  54. package/src/value-wrappers-core.ts +168 -0
  55. package/src/value-wrappers.ts +165 -0
@@ -0,0 +1,2890 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/bin/duckdb-introspect.ts
4
+ import { DuckDBInstance as DuckDBInstance3 } from "@duckdb/node-api";
5
+ import { mkdir, writeFile } from "node:fs/promises";
6
+ import path from "node:path";
7
+ import process2 from "node:process";
8
+
9
+ // src/driver.ts
10
+ import { DuckDBInstance as DuckDBInstance2 } from "@duckdb/node-api";
11
+ import { entityKind as entityKind3 } from "drizzle-orm/entity";
12
+ import { DefaultLogger } from "drizzle-orm/logger";
13
+ import { PgDatabase } from "drizzle-orm/pg-core/db";
14
+ import {
15
+ createTableRelationsHelpers,
16
+ extractTablesRelationalConfig
17
+ } from "drizzle-orm/relations";
18
+
19
+ // src/session.ts
20
+ import { entityKind } from "drizzle-orm/entity";
21
+ import { NoopLogger } from "drizzle-orm/logger";
22
+ import { PgTransaction } from "drizzle-orm/pg-core";
23
+ import { PgPreparedQuery, PgSession } from "drizzle-orm/pg-core/session";
24
+ import { fillPlaceholders, sql } from "drizzle-orm/sql/sql";
25
+
26
+ // src/sql/result-mapper.ts
27
+ import {
28
+ Column,
29
+ SQL,
30
+ getTableName,
31
+ is
32
+ } from "drizzle-orm";
33
+ import {
34
+ PgCustomColumn,
35
+ PgDate,
36
+ PgDateString,
37
+ PgInterval,
38
+ PgTime,
39
+ PgTimestamp,
40
+ PgTimestampString
41
+ } from "drizzle-orm/pg-core";
42
+ function toDecoderInput(decoder, value) {
43
+ return value;
44
+ }
45
+ function normalizeInet(value) {
46
+ if (value && typeof value === "object" && "address" in value && typeof value.address !== "undefined") {
47
+ const { address, mask } = value;
48
+ if (typeof address === "bigint" || typeof address === "number") {
49
+ const inet = typeof address === "number" ? BigInt(address) : address;
50
+ const maxIpv4 = (1n << 32n) - 1n;
51
+ if (inet >= 0 && inet <= maxIpv4) {
52
+ const num = Number(inet);
53
+ const octets = [
54
+ num >>> 24 & 255,
55
+ num >>> 16 & 255,
56
+ num >>> 8 & 255,
57
+ num & 255
58
+ ];
59
+ const suffix = typeof mask === "number" && mask !== 32 ? `/${mask}` : "";
60
+ return `${octets.join(".")}${suffix}`;
61
+ }
62
+ }
63
+ const fallback = value.toString?.();
64
+ if (fallback && fallback !== "[object Object]") {
65
+ return fallback;
66
+ }
67
+ }
68
+ return value;
69
+ }
70
+ function normalizeTimestampString(value, withTimezone) {
71
+ if (value instanceof Date) {
72
+ const iso = value.toISOString().replace("T", " ");
73
+ return withTimezone ? iso.replace("Z", "+00") : iso.replace("Z", "");
74
+ }
75
+ if (typeof value === "string") {
76
+ const normalized = value.replace("T", " ");
77
+ if (withTimezone) {
78
+ return normalized.includes("+") ? normalized : `${normalized}+00`;
79
+ }
80
+ return normalized.replace(/\+00$/, "");
81
+ }
82
+ return value;
83
+ }
84
+ function normalizeTimestamp(value, withTimezone) {
85
+ if (value instanceof Date) {
86
+ return value;
87
+ }
88
+ if (typeof value === "string") {
89
+ const hasOffset = value.endsWith("Z") || /[+-]\d{2}:?\d{2}$/.test(value.trim());
90
+ const spaced = value.replace(" ", "T");
91
+ const normalized = withTimezone || hasOffset ? spaced : `${spaced}+00`;
92
+ return new Date(normalized);
93
+ }
94
+ return value;
95
+ }
96
+ function normalizeDateString(value) {
97
+ if (value instanceof Date) {
98
+ return value.toISOString().slice(0, 10);
99
+ }
100
+ if (typeof value === "string") {
101
+ return value.slice(0, 10);
102
+ }
103
+ return value;
104
+ }
105
+ function normalizeDateValue(value) {
106
+ if (value instanceof Date) {
107
+ return value;
108
+ }
109
+ if (typeof value === "string") {
110
+ return new Date(`${value.slice(0, 10)}T00:00:00Z`);
111
+ }
112
+ return value;
113
+ }
114
+ function normalizeTime(value) {
115
+ if (typeof value === "bigint") {
116
+ const totalMillis = Number(value) / 1000;
117
+ const date = new Date(totalMillis);
118
+ return date.toISOString().split("T")[1].replace("Z", "");
119
+ }
120
+ if (value instanceof Date) {
121
+ return value.toISOString().split("T")[1].replace("Z", "");
122
+ }
123
+ return value;
124
+ }
125
+ function normalizeInterval(value) {
126
+ if (value && typeof value === "object" && "days" in value && "months" in value) {
127
+ const { months, days, micros } = value;
128
+ if (months === 0 && days !== undefined) {
129
+ if (micros && Number(micros) !== 0) {
130
+ const seconds = Number(micros) / 1e6;
131
+ return `${days} day${days === 1 ? "" : "s"} ${seconds} seconds`.trim();
132
+ }
133
+ return `${days} day${days === 1 ? "" : "s"}`;
134
+ }
135
+ }
136
+ return value;
137
+ }
138
+ function mapDriverValue(decoder, rawValue) {
139
+ if (is(decoder, PgTimestampString)) {
140
+ return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeTimestampString(rawValue, decoder.withTimezone)));
141
+ }
142
+ if (is(decoder, PgTimestamp)) {
143
+ const normalized = normalizeTimestamp(rawValue, decoder.withTimezone);
144
+ if (normalized instanceof Date) {
145
+ return normalized;
146
+ }
147
+ return decoder.mapFromDriverValue(toDecoderInput(decoder, normalized));
148
+ }
149
+ if (is(decoder, PgDateString)) {
150
+ return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeDateString(rawValue)));
151
+ }
152
+ if (is(decoder, PgDate)) {
153
+ return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeDateValue(rawValue)));
154
+ }
155
+ if (is(decoder, PgTime)) {
156
+ return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeTime(rawValue)));
157
+ }
158
+ if (is(decoder, PgInterval)) {
159
+ return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeInterval(rawValue)));
160
+ }
161
+ return decoder.mapFromDriverValue(toDecoderInput(decoder, rawValue));
162
+ }
163
+ function mapResultRow(columns, row, joinsNotNullableMap) {
164
+ const nullifyMap = {};
165
+ const result = columns.reduce((acc, { path, field }, columnIndex) => {
166
+ let decoder;
167
+ if (is(field, Column)) {
168
+ decoder = field;
169
+ } else if (is(field, SQL)) {
170
+ decoder = field.decoder;
171
+ } else {
172
+ const col = field.sql.queryChunks.find((chunk) => is(chunk, Column));
173
+ if (is(col, PgCustomColumn)) {
174
+ decoder = col;
175
+ } else {
176
+ decoder = field.sql.decoder;
177
+ }
178
+ }
179
+ let node = acc;
180
+ for (const [pathChunkIndex, pathChunk] of path.entries()) {
181
+ if (pathChunkIndex < path.length - 1) {
182
+ if (!(pathChunk in node)) {
183
+ node[pathChunk] = {};
184
+ }
185
+ node = node[pathChunk];
186
+ continue;
187
+ }
188
+ const rawValue = normalizeInet(row[columnIndex]);
189
+ const value = node[pathChunk] = rawValue === null ? null : mapDriverValue(decoder, rawValue);
190
+ if (joinsNotNullableMap && is(field, Column) && path.length === 2) {
191
+ const objectName = path[0];
192
+ if (!(objectName in nullifyMap)) {
193
+ nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
194
+ } else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
195
+ nullifyMap[objectName] = false;
196
+ }
197
+ continue;
198
+ }
199
+ if (joinsNotNullableMap && is(field, SQL.Aliased) && path.length === 2) {
200
+ const col = field.sql.queryChunks.find((chunk) => is(chunk, Column));
201
+ const tableName = col?.table && getTableName(col?.table);
202
+ if (!tableName) {
203
+ continue;
204
+ }
205
+ const objectName = path[0];
206
+ if (!(objectName in nullifyMap)) {
207
+ nullifyMap[objectName] = value === null ? tableName : false;
208
+ continue;
209
+ }
210
+ if (nullifyMap[objectName] && nullifyMap[objectName] !== tableName) {
211
+ nullifyMap[objectName] = false;
212
+ }
213
+ continue;
214
+ }
215
+ }
216
+ return acc;
217
+ }, {});
218
+ if (joinsNotNullableMap && Object.keys(nullifyMap).length > 0) {
219
+ for (const [objectName, tableName] of Object.entries(nullifyMap)) {
220
+ if (typeof tableName === "string" && !joinsNotNullableMap[tableName]) {
221
+ result[objectName] = null;
222
+ }
223
+ }
224
+ }
225
+ return result;
226
+ }
227
+
228
+ // src/session.ts
229
+ import { TransactionRollbackError } from "drizzle-orm/errors";
230
+
231
+ // src/client.ts
232
+ import {
233
+ listValue as listValue2,
234
+ timestampValue as timestampValue2
235
+ } from "@duckdb/node-api";
236
+
237
+ // src/value-wrappers.ts
238
+ import {
239
+ listValue,
240
+ arrayValue,
241
+ structValue,
242
+ mapValue,
243
+ blobValue,
244
+ timestampValue,
245
+ timestampTZValue
246
+ } from "@duckdb/node-api";
247
+
248
+ // src/value-wrappers-core.ts
249
+ var DUCKDB_VALUE_MARKER = Symbol.for("drizzle-duckdb:value");
250
+
251
+ // src/value-wrappers.ts
252
+ function dateToMicros(value) {
253
+ if (value instanceof Date) {
254
+ return BigInt(value.getTime()) * 1000n;
255
+ }
256
+ if (typeof value === "bigint") {
257
+ return value;
258
+ }
259
+ if (typeof value === "number") {
260
+ return BigInt(Math.trunc(value)) * 1000n;
261
+ }
262
+ let normalized = value;
263
+ if (!value.includes("T") && value.includes(" ")) {
264
+ normalized = value.replace(" ", "T");
265
+ }
266
+ if (!normalized.endsWith("Z") && !/[+-]\d{2}:?\d{2}$/.test(normalized)) {
267
+ normalized += "Z";
268
+ }
269
+ const date = new Date(normalized);
270
+ if (isNaN(date.getTime())) {
271
+ throw new Error(`Invalid timestamp string: ${value}`);
272
+ }
273
+ return BigInt(date.getTime()) * 1000n;
274
+ }
275
+ function toUint8Array(data) {
276
+ return data instanceof Uint8Array && !(data instanceof Buffer) ? data : new Uint8Array(data);
277
+ }
278
+ function convertStructEntries(data, toValue) {
279
+ const entries = {};
280
+ for (const [key, val] of Object.entries(data)) {
281
+ entries[key] = toValue(val);
282
+ }
283
+ return entries;
284
+ }
285
+ function convertMapEntries(data, toValue) {
286
+ return Object.entries(data).map(([key, val]) => ({
287
+ key,
288
+ value: toValue(val)
289
+ }));
290
+ }
291
+ function wrapperToNodeApiValue(wrapper, toValue) {
292
+ switch (wrapper.kind) {
293
+ case "list":
294
+ return listValue(wrapper.data.map(toValue));
295
+ case "array":
296
+ return arrayValue(wrapper.data.map(toValue));
297
+ case "struct":
298
+ return structValue(convertStructEntries(wrapper.data, toValue));
299
+ case "map":
300
+ return mapValue(convertMapEntries(wrapper.data, toValue));
301
+ case "timestamp":
302
+ return wrapper.withTimezone ? timestampTZValue(dateToMicros(wrapper.data)) : timestampValue(dateToMicros(wrapper.data));
303
+ case "blob":
304
+ return blobValue(toUint8Array(wrapper.data));
305
+ case "json":
306
+ return JSON.stringify(wrapper.data);
307
+ default: {
308
+ const _exhaustive = wrapper;
309
+ throw new Error(`Unknown wrapper kind: ${_exhaustive.kind}`);
310
+ }
311
+ }
312
+ }
313
+
314
+ // src/client.ts
315
+ function isPool(client) {
316
+ return typeof client.acquire === "function";
317
+ }
318
+ var PREPARED_CACHE = Symbol.for("drizzle-duckdb:prepared-cache");
319
+ function isPgArrayLiteral(value) {
320
+ return value.startsWith("{") && value.endsWith("}");
321
+ }
322
+ function parsePgArrayLiteral(value) {
323
+ const json = value.replace(/{/g, "[").replace(/}/g, "]");
324
+ try {
325
+ return JSON.parse(json);
326
+ } catch {
327
+ return value;
328
+ }
329
+ }
330
+ function prepareParams(params, options = {}) {
331
+ return params.map((param) => {
332
+ if (typeof param === "string" && param.length > 0) {
333
+ const firstChar = param[0];
334
+ const maybeArrayLiteral = firstChar === "{" || firstChar === "[" || firstChar === " " || firstChar === "\t";
335
+ if (maybeArrayLiteral) {
336
+ const trimmed = firstChar === "{" || firstChar === "[" ? param : param.trim();
337
+ if (trimmed && isPgArrayLiteral(trimmed)) {
338
+ if (options.rejectStringArrayLiterals) {
339
+ throw new Error("Stringified array literals are not supported. Use duckDbList()/duckDbArray() or pass native arrays.");
340
+ }
341
+ if (options.warnOnStringArrayLiteral) {
342
+ options.warnOnStringArrayLiteral();
343
+ }
344
+ return parsePgArrayLiteral(trimmed);
345
+ }
346
+ }
347
+ }
348
+ return param;
349
+ });
350
+ }
351
+ function toNodeApiValue(value) {
352
+ if (value == null)
353
+ return null;
354
+ const t = typeof value;
355
+ if (t === "string" || t === "number" || t === "bigint" || t === "boolean") {
356
+ return value;
357
+ }
358
+ if (t === "object" && DUCKDB_VALUE_MARKER in value) {
359
+ return wrapperToNodeApiValue(value, toNodeApiValue);
360
+ }
361
+ if (Array.isArray(value)) {
362
+ return listValue2(value.map((inner) => toNodeApiValue(inner)));
363
+ }
364
+ if (value instanceof Date) {
365
+ return timestampValue2(BigInt(value.getTime()) * 1000n);
366
+ }
367
+ return value;
368
+ }
369
+ function deduplicateColumns(columns) {
370
+ const counts = new Map;
371
+ let hasDuplicates = false;
372
+ for (const column of columns) {
373
+ const next = (counts.get(column) ?? 0) + 1;
374
+ counts.set(column, next);
375
+ if (next > 1) {
376
+ hasDuplicates = true;
377
+ break;
378
+ }
379
+ }
380
+ if (!hasDuplicates) {
381
+ return columns;
382
+ }
383
+ counts.clear();
384
+ return columns.map((column) => {
385
+ const count = counts.get(column) ?? 0;
386
+ counts.set(column, count + 1);
387
+ return count === 0 ? column : `${column}_${count}`;
388
+ });
389
+ }
390
+ function destroyPreparedStatement(entry) {
391
+ if (!entry)
392
+ return;
393
+ try {
394
+ entry.statement.destroySync();
395
+ } catch {}
396
+ }
397
+ function getPreparedCache(connection, size) {
398
+ const store = connection;
399
+ const existing = store[PREPARED_CACHE];
400
+ if (existing) {
401
+ existing.size = size;
402
+ return existing;
403
+ }
404
+ const cache = { size, entries: new Map };
405
+ store[PREPARED_CACHE] = cache;
406
+ return cache;
407
+ }
408
+ function evictOldest(cache) {
409
+ const oldest = cache.entries.keys().next();
410
+ if (!oldest.done) {
411
+ const key = oldest.value;
412
+ const entry = cache.entries.get(key);
413
+ cache.entries.delete(key);
414
+ destroyPreparedStatement(entry);
415
+ }
416
+ }
417
+ function evictCacheEntry(cache, key) {
418
+ const entry = cache.entries.get(key);
419
+ cache.entries.delete(key);
420
+ destroyPreparedStatement(entry);
421
+ }
422
+ async function getOrPrepareStatement(connection, query, cacheConfig) {
423
+ const cache = getPreparedCache(connection, cacheConfig.size);
424
+ const cached = cache.entries.get(query);
425
+ if (cached) {
426
+ cache.entries.delete(query);
427
+ cache.entries.set(query, cached);
428
+ return cached.statement;
429
+ }
430
+ const statement = await connection.prepare(query);
431
+ cache.entries.set(query, { statement });
432
+ while (cache.entries.size > cache.size) {
433
+ evictOldest(cache);
434
+ }
435
+ return statement;
436
+ }
437
+ async function materializeResultRows(result) {
438
+ const rows = await result.getRowsJS() ?? [];
439
+ const baseColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
440
+ const columns = typeof result.deduplicatedColumnNames === "function" ? baseColumns : deduplicateColumns(baseColumns);
441
+ return { columns, rows };
442
+ }
443
+ async function materializeRows(client, query, params, options = {}) {
444
+ if (isPool(client)) {
445
+ const connection2 = await client.acquire();
446
+ try {
447
+ return await materializeRows(connection2, query, params, options);
448
+ } finally {
449
+ await client.release(connection2);
450
+ }
451
+ }
452
+ const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
453
+ const connection = client;
454
+ if (options.prepareCache && typeof connection.prepare === "function") {
455
+ const cache = getPreparedCache(connection, options.prepareCache.size);
456
+ try {
457
+ const statement = await getOrPrepareStatement(connection, query, options.prepareCache);
458
+ if (values) {
459
+ statement.bind(values);
460
+ } else {
461
+ statement.clearBindings?.();
462
+ }
463
+ const result2 = await statement.run();
464
+ cache.entries.delete(query);
465
+ cache.entries.set(query, { statement });
466
+ return await materializeResultRows(result2);
467
+ } catch (error) {
468
+ evictCacheEntry(cache, query);
469
+ throw error;
470
+ }
471
+ }
472
+ const result = await connection.run(query, values);
473
+ return await materializeResultRows(result);
474
+ }
475
+ function clearPreparedCache(connection) {
476
+ const store = connection;
477
+ const cache = store[PREPARED_CACHE];
478
+ if (!cache)
479
+ return;
480
+ for (const entry of cache.entries.values()) {
481
+ destroyPreparedStatement(entry);
482
+ }
483
+ cache.entries.clear();
484
+ }
485
+ function mapRowsToObjects(columns, rows) {
486
+ return rows.map((vals) => {
487
+ const obj = {};
488
+ columns.forEach((col, idx) => {
489
+ obj[col] = vals[idx];
490
+ });
491
+ return obj;
492
+ });
493
+ }
494
+ async function closeClientConnection(connection) {
495
+ clearPreparedCache(connection);
496
+ if ("close" in connection && typeof connection.close === "function") {
497
+ await connection.close();
498
+ return;
499
+ }
500
+ if ("closeSync" in connection && typeof connection.closeSync === "function") {
501
+ connection.closeSync();
502
+ return;
503
+ }
504
+ if ("disconnectSync" in connection && typeof connection.disconnectSync === "function") {
505
+ connection.disconnectSync();
506
+ }
507
+ }
508
+ async function executeOnClient(client, query, params, options = {}) {
509
+ const { columns, rows } = await materializeRows(client, query, params, options);
510
+ if (!rows || rows.length === 0) {
511
+ return [];
512
+ }
513
+ return mapRowsToObjects(columns, rows);
514
+ }
515
+ async function executeArraysOnClient(client, query, params, options = {}) {
516
+ return await materializeRows(client, query, params, options);
517
+ }
518
+ async function* executeInBatches(client, query, params, options = {}) {
519
+ if (isPool(client)) {
520
+ const connection = await client.acquire();
521
+ try {
522
+ yield* executeInBatches(connection, query, params, options);
523
+ return;
524
+ } finally {
525
+ await client.release(connection);
526
+ }
527
+ }
528
+ const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
529
+ const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
530
+ const result = await client.stream(query, values);
531
+ const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
532
+ const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
533
+ let buffer = [];
534
+ for await (const chunk of result.yieldRowsJs()) {
535
+ const objects = mapRowsToObjects(columns, chunk);
536
+ for (const row of objects) {
537
+ buffer.push(row);
538
+ if (buffer.length >= rowsPerChunk) {
539
+ yield buffer;
540
+ buffer = [];
541
+ }
542
+ }
543
+ }
544
+ if (buffer.length > 0) {
545
+ yield buffer;
546
+ }
547
+ }
548
+ async function* executeInBatchesRaw(client, query, params, options = {}) {
549
+ if (isPool(client)) {
550
+ const connection = await client.acquire();
551
+ try {
552
+ yield* executeInBatchesRaw(connection, query, params, options);
553
+ return;
554
+ } finally {
555
+ await client.release(connection);
556
+ }
557
+ }
558
+ const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
559
+ const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
560
+ const result = await client.stream(query, values);
561
+ const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
562
+ const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
563
+ let buffer = [];
564
+ for await (const chunk of result.yieldRowsJs()) {
565
+ for (const row of chunk) {
566
+ buffer.push(row);
567
+ if (buffer.length >= rowsPerChunk) {
568
+ yield { columns, rows: buffer };
569
+ buffer = [];
570
+ }
571
+ }
572
+ }
573
+ if (buffer.length > 0) {
574
+ yield { columns, rows: buffer };
575
+ }
576
+ }
577
+ async function executeArrowOnClient(client, query, params) {
578
+ if (isPool(client)) {
579
+ const connection = await client.acquire();
580
+ try {
581
+ return await executeArrowOnClient(connection, query, params);
582
+ } finally {
583
+ await client.release(connection);
584
+ }
585
+ }
586
+ const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
587
+ const result = await client.run(query, values);
588
+ const maybeArrow = result.toArrow ?? result.getArrowTable;
589
+ if (typeof maybeArrow === "function") {
590
+ return await maybeArrow.call(result);
591
+ }
592
+ return result.getColumnsObjectJS();
593
+ }
594
+
595
+ // src/session.ts
596
+ function isSavepointSyntaxError(error) {
597
+ if (!(error instanceof Error) || !error.message) {
598
+ return false;
599
+ }
600
+ return error.message.toLowerCase().includes("savepoint") && error.message.toLowerCase().includes("syntax error");
601
+ }
602
+
603
+ class DuckDBPreparedQuery extends PgPreparedQuery {
604
+ client;
605
+ dialect;
606
+ queryString;
607
+ params;
608
+ logger;
609
+ fields;
610
+ _isResponseInArrayMode;
611
+ customResultMapper;
612
+ rejectStringArrayLiterals;
613
+ prepareCache;
614
+ warnOnStringArrayLiteral;
615
+ static [entityKind] = "DuckDBPreparedQuery";
616
+ constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
617
+ super({ sql: queryString, params });
618
+ this.client = client;
619
+ this.dialect = dialect;
620
+ this.queryString = queryString;
621
+ this.params = params;
622
+ this.logger = logger;
623
+ this.fields = fields;
624
+ this._isResponseInArrayMode = _isResponseInArrayMode;
625
+ this.customResultMapper = customResultMapper;
626
+ this.rejectStringArrayLiterals = rejectStringArrayLiterals;
627
+ this.prepareCache = prepareCache;
628
+ this.warnOnStringArrayLiteral = warnOnStringArrayLiteral;
629
+ }
630
+ async execute(placeholderValues = {}) {
631
+ this.dialect.assertNoPgJsonColumns();
632
+ const params = prepareParams(fillPlaceholders(this.params, placeholderValues), {
633
+ rejectStringArrayLiterals: this.rejectStringArrayLiterals,
634
+ warnOnStringArrayLiteral: this.warnOnStringArrayLiteral ? () => this.warnOnStringArrayLiteral?.(this.queryString) : undefined
635
+ });
636
+ this.logger.logQuery(this.queryString, params);
637
+ const { fields, joinsNotNullableMap, customResultMapper } = this;
638
+ if (fields) {
639
+ const { rows: rows2 } = await executeArraysOnClient(this.client, this.queryString, params, { prepareCache: this.prepareCache });
640
+ if (rows2.length === 0) {
641
+ return [];
642
+ }
643
+ return customResultMapper ? customResultMapper(rows2) : rows2.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
644
+ }
645
+ const rows = await executeOnClient(this.client, this.queryString, params, {
646
+ prepareCache: this.prepareCache
647
+ });
648
+ return rows;
649
+ }
650
+ all(placeholderValues = {}) {
651
+ return this.execute(placeholderValues);
652
+ }
653
+ isResponseInArrayMode() {
654
+ return this._isResponseInArrayMode;
655
+ }
656
+ }
657
+
658
+ class DuckDBSession extends PgSession {
659
+ client;
660
+ schema;
661
+ options;
662
+ static [entityKind] = "DuckDBSession";
663
+ dialect;
664
+ logger;
665
+ rejectStringArrayLiterals;
666
+ prepareCache;
667
+ hasWarnedArrayLiteral = false;
668
+ rollbackOnly = false;
669
+ constructor(client, dialect, schema, options = {}) {
670
+ super(dialect);
671
+ this.client = client;
672
+ this.schema = schema;
673
+ this.options = options;
674
+ this.dialect = dialect;
675
+ this.logger = options.logger ?? new NoopLogger;
676
+ this.rejectStringArrayLiterals = options.rejectStringArrayLiterals ?? false;
677
+ this.prepareCache = options.prepareCache;
678
+ this.options = {
679
+ ...options,
680
+ prepareCache: this.prepareCache
681
+ };
682
+ }
683
+ prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
684
+ return new DuckDBPreparedQuery(this.client, this.dialect, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, this.rejectStringArrayLiterals, this.prepareCache, this.rejectStringArrayLiterals ? undefined : this.warnOnStringArrayLiteral);
685
+ }
686
+ execute(query) {
687
+ this.dialect.resetPgJsonFlag();
688
+ return super.execute(query);
689
+ }
690
+ all(query) {
691
+ this.dialect.resetPgJsonFlag();
692
+ return super.all(query);
693
+ }
694
+ async transaction(transaction, config) {
695
+ let pinnedConnection;
696
+ let pool;
697
+ let clientForTx = this.client;
698
+ if (isPool(this.client)) {
699
+ pool = this.client;
700
+ pinnedConnection = await pool.acquire();
701
+ clientForTx = pinnedConnection;
702
+ }
703
+ const session = new DuckDBSession(clientForTx, this.dialect, this.schema, this.options);
704
+ const tx = new DuckDBTransaction(this.dialect, session, this.schema);
705
+ try {
706
+ await tx.execute(sql`BEGIN TRANSACTION;`);
707
+ if (config) {
708
+ await tx.setTransaction(config);
709
+ }
710
+ try {
711
+ const result = await transaction(tx);
712
+ if (session.isRollbackOnly()) {
713
+ await tx.execute(sql`rollback`);
714
+ throw new TransactionRollbackError;
715
+ }
716
+ await tx.execute(sql`commit`);
717
+ return result;
718
+ } catch (error) {
719
+ await tx.execute(sql`rollback`);
720
+ throw error;
721
+ }
722
+ } finally {
723
+ if (pinnedConnection && pool) {
724
+ await pool.release(pinnedConnection);
725
+ }
726
+ }
727
+ }
728
+ warnOnStringArrayLiteral = (query) => {
729
+ if (this.hasWarnedArrayLiteral) {
730
+ return;
731
+ }
732
+ this.hasWarnedArrayLiteral = true;
733
+ this.logger.logQuery(`[duckdb] ${arrayLiteralWarning}
734
+ query: ${query}`, []);
735
+ };
736
+ executeBatches(query, options = {}) {
737
+ this.dialect.resetPgJsonFlag();
738
+ const builtQuery = this.dialect.sqlToQuery(query);
739
+ this.dialect.assertNoPgJsonColumns();
740
+ const params = prepareParams(builtQuery.params, {
741
+ rejectStringArrayLiterals: this.rejectStringArrayLiterals,
742
+ warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
743
+ });
744
+ this.logger.logQuery(builtQuery.sql, params);
745
+ return executeInBatches(this.client, builtQuery.sql, params, options);
746
+ }
747
+ executeBatchesRaw(query, options = {}) {
748
+ this.dialect.resetPgJsonFlag();
749
+ const builtQuery = this.dialect.sqlToQuery(query);
750
+ this.dialect.assertNoPgJsonColumns();
751
+ const params = prepareParams(builtQuery.params, {
752
+ rejectStringArrayLiterals: this.rejectStringArrayLiterals,
753
+ warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
754
+ });
755
+ this.logger.logQuery(builtQuery.sql, params);
756
+ return executeInBatchesRaw(this.client, builtQuery.sql, params, options);
757
+ }
758
+ async executeArrow(query) {
759
+ this.dialect.resetPgJsonFlag();
760
+ const builtQuery = this.dialect.sqlToQuery(query);
761
+ this.dialect.assertNoPgJsonColumns();
762
+ const params = prepareParams(builtQuery.params, {
763
+ rejectStringArrayLiterals: this.rejectStringArrayLiterals,
764
+ warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
765
+ });
766
+ this.logger.logQuery(builtQuery.sql, params);
767
+ return executeArrowOnClient(this.client, builtQuery.sql, params);
768
+ }
769
+ markRollbackOnly() {
770
+ this.rollbackOnly = true;
771
+ }
772
+ isRollbackOnly() {
773
+ return this.rollbackOnly;
774
+ }
775
+ }
776
+
777
+ class DuckDBTransaction extends PgTransaction {
778
+ static [entityKind] = "DuckDBTransaction";
779
+ rollback() {
780
+ throw new TransactionRollbackError;
781
+ }
782
+ getTransactionConfigSQL(config) {
783
+ const chunks = [];
784
+ if (config.isolationLevel) {
785
+ chunks.push(`isolation level ${config.isolationLevel}`);
786
+ }
787
+ if (config.accessMode) {
788
+ chunks.push(config.accessMode);
789
+ }
790
+ if (typeof config.deferrable === "boolean") {
791
+ chunks.push(config.deferrable ? "deferrable" : "not deferrable");
792
+ }
793
+ return sql.raw(chunks.join(" "));
794
+ }
795
+ setTransaction(config) {
796
+ return this.session.execute(sql`set transaction ${this.getTransactionConfigSQL(config)}`);
797
+ }
798
+ executeBatches(query, options = {}) {
799
+ return this.session.executeBatches(query, options);
800
+ }
801
+ executeBatchesRaw(query, options = {}) {
802
+ return this.session.executeBatchesRaw(query, options);
803
+ }
804
+ executeArrow(query) {
805
+ return this.session.executeArrow(query);
806
+ }
807
+ async transaction(transaction) {
808
+ const internals = this;
809
+ const savepoint = `drizzle_savepoint_${this.nestedIndex + 1}`;
810
+ const savepointSql = sql.raw(`savepoint ${savepoint}`);
811
+ const releaseSql = sql.raw(`release savepoint ${savepoint}`);
812
+ const rollbackSql = sql.raw(`rollback to savepoint ${savepoint}`);
813
+ const nestedTx = new DuckDBTransaction(internals.dialect, internals.session, this.schema, this.nestedIndex + 1);
814
+ if (internals.dialect.areSavepointsUnsupported()) {
815
+ return this.runNestedWithoutSavepoint(transaction, nestedTx, internals);
816
+ }
817
+ let createdSavepoint = false;
818
+ try {
819
+ await internals.session.execute(savepointSql);
820
+ internals.dialect.markSavepointsSupported();
821
+ createdSavepoint = true;
822
+ } catch (error) {
823
+ if (!isSavepointSyntaxError(error)) {
824
+ throw error;
825
+ }
826
+ internals.dialect.markSavepointsUnsupported();
827
+ return this.runNestedWithoutSavepoint(transaction, nestedTx, internals);
828
+ }
829
+ try {
830
+ const result = await transaction(nestedTx);
831
+ if (createdSavepoint) {
832
+ await internals.session.execute(releaseSql);
833
+ }
834
+ return result;
835
+ } catch (error) {
836
+ if (createdSavepoint) {
837
+ await internals.session.execute(rollbackSql);
838
+ }
839
+ internals.session.markRollbackOnly();
840
+ throw error;
841
+ }
842
+ }
843
+ runNestedWithoutSavepoint(transaction, nestedTx, internals) {
844
+ return transaction(nestedTx).catch((error) => {
845
+ internals.session.markRollbackOnly();
846
+ throw error;
847
+ });
848
+ }
849
+ }
850
+ var arrayLiteralWarning = "Received a stringified Postgres-style array literal. Use duckDbList()/duckDbArray() or pass native arrays instead. You can also set rejectStringArrayLiterals=true to throw.";
851
+
852
+ // src/dialect.ts
853
+ import { entityKind as entityKind2, is as is2 } from "drizzle-orm/entity";
854
+ import {
855
+ PgDate as PgDate2,
856
+ PgDateString as PgDateString2,
857
+ PgDialect,
858
+ PgJson,
859
+ PgJsonb,
860
+ PgNumeric,
861
+ PgTime as PgTime2,
862
+ PgTimestamp as PgTimestamp2,
863
+ PgTimestampString as PgTimestampString2,
864
+ PgUUID
865
+ } from "drizzle-orm/pg-core";
866
+ import {
867
+ sql as sql2
868
+ } from "drizzle-orm";
869
+
870
+ // src/sql/ast-transformer.ts
871
+ import nodeSqlParser from "node-sql-parser";
872
+
873
+ // src/sql/visitors/array-operators.ts
874
+ var OPERATOR_MAP = {
875
+ "@>": { fn: "array_has_all" },
876
+ "<@": { fn: "array_has_all", swap: true },
877
+ "&&": { fn: "array_has_any" }
878
+ };
879
+ function walkExpression(expr, parent, key) {
880
+ if (!expr || typeof expr !== "object")
881
+ return false;
882
+ let transformed = false;
883
+ const exprObj = expr;
884
+ if ("type" in expr && exprObj.type === "binary_expr") {
885
+ const binary = expr;
886
+ const mapping = OPERATOR_MAP[binary.operator];
887
+ if (mapping) {
888
+ const fnExpr = {
889
+ type: "function",
890
+ name: { name: [{ type: "default", value: mapping.fn }] },
891
+ args: {
892
+ type: "expr_list",
893
+ value: mapping.swap ? [binary.right, binary.left] : [binary.left, binary.right]
894
+ }
895
+ };
896
+ if (parent && key) {
897
+ parent[key] = fnExpr;
898
+ }
899
+ transformed = true;
900
+ } else {
901
+ transformed = walkExpression(binary.left, binary, "left") || transformed;
902
+ transformed = walkExpression(binary.right, binary, "right") || transformed;
903
+ }
904
+ }
905
+ if ("type" in expr && exprObj.type === "unary_expr") {
906
+ if ("expr" in exprObj) {
907
+ transformed = walkExpression(exprObj.expr, exprObj, "expr") || transformed;
908
+ }
909
+ }
910
+ if ("type" in expr && exprObj.type === "case") {
911
+ if ("expr" in exprObj && exprObj.expr) {
912
+ transformed = walkExpression(exprObj.expr, exprObj, "expr") || transformed;
913
+ }
914
+ if ("args" in exprObj && Array.isArray(exprObj.args)) {
915
+ for (let i = 0;i < exprObj.args.length; i++) {
916
+ const whenClause = exprObj.args[i];
917
+ if (whenClause.cond) {
918
+ transformed = walkExpression(whenClause.cond, whenClause, "cond") || transformed;
919
+ }
920
+ if (whenClause.result) {
921
+ transformed = walkExpression(whenClause.result, whenClause, "result") || transformed;
922
+ }
923
+ }
924
+ }
925
+ }
926
+ if ("args" in expr && exprObj.args) {
927
+ const args = exprObj.args;
928
+ if ("value" in args && Array.isArray(args.value)) {
929
+ for (let i = 0;i < args.value.length; i++) {
930
+ transformed = walkExpression(args.value[i], args.value, String(i)) || transformed;
931
+ }
932
+ } else if ("expr" in args) {
933
+ transformed = walkExpression(args.expr, args, "expr") || transformed;
934
+ }
935
+ }
936
+ if ("ast" in exprObj && exprObj.ast) {
937
+ const subAst = exprObj.ast;
938
+ if (subAst.type === "select") {
939
+ transformed = walkSelectImpl(subAst) || transformed;
940
+ }
941
+ }
942
+ if ("type" in expr && exprObj.type === "expr_list") {
943
+ if ("value" in exprObj && Array.isArray(exprObj.value)) {
944
+ for (let i = 0;i < exprObj.value.length; i++) {
945
+ transformed = walkExpression(exprObj.value[i], exprObj.value, String(i)) || transformed;
946
+ }
947
+ }
948
+ }
949
+ return transformed;
950
+ }
951
+ function walkFrom(from) {
952
+ if (!from || !Array.isArray(from))
953
+ return false;
954
+ let transformed = false;
955
+ for (const f of from) {
956
+ if ("join" in f) {
957
+ const join = f;
958
+ transformed = walkExpression(join.on, join, "on") || transformed;
959
+ }
960
+ if ("expr" in f && f.expr && "ast" in f.expr) {
961
+ transformed = walkSelectImpl(f.expr.ast) || transformed;
962
+ }
963
+ }
964
+ return transformed;
965
+ }
966
+ function walkSelectImpl(select) {
967
+ let transformed = false;
968
+ if (select.with) {
969
+ for (const cte of select.with) {
970
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
971
+ if (cteSelect && cteSelect.type === "select") {
972
+ transformed = walkSelectImpl(cteSelect) || transformed;
973
+ }
974
+ }
975
+ }
976
+ if (Array.isArray(select.from)) {
977
+ transformed = walkFrom(select.from) || transformed;
978
+ }
979
+ transformed = walkExpression(select.where, select, "where") || transformed;
980
+ if (select.having) {
981
+ if (Array.isArray(select.having)) {
982
+ for (let i = 0;i < select.having.length; i++) {
983
+ transformed = walkExpression(select.having[i], select.having, String(i)) || transformed;
984
+ }
985
+ } else {
986
+ transformed = walkExpression(select.having, select, "having") || transformed;
987
+ }
988
+ }
989
+ if (Array.isArray(select.columns)) {
990
+ for (const col of select.columns) {
991
+ if ("expr" in col) {
992
+ transformed = walkExpression(col.expr, col, "expr") || transformed;
993
+ }
994
+ }
995
+ }
996
+ if (select._next) {
997
+ transformed = walkSelectImpl(select._next) || transformed;
998
+ }
999
+ return transformed;
1000
+ }
1001
+ function transformArrayOperators(ast) {
1002
+ const statements = Array.isArray(ast) ? ast : [ast];
1003
+ let transformed = false;
1004
+ for (const stmt of statements) {
1005
+ if (stmt.type === "select") {
1006
+ transformed = walkSelectImpl(stmt) || transformed;
1007
+ }
1008
+ }
1009
+ return transformed;
1010
+ }
1011
+
1012
+ // src/sql/visitors/column-qualifier.ts
1013
+ function getTableSource(from) {
1014
+ if ("table" in from && from.table) {
1015
+ return {
1016
+ name: from.table,
1017
+ alias: from.as ?? null,
1018
+ schema: "db" in from ? from.db ?? null : null
1019
+ };
1020
+ }
1021
+ if ("expr" in from && from.as) {
1022
+ return {
1023
+ name: from.as,
1024
+ alias: from.as,
1025
+ schema: null
1026
+ };
1027
+ }
1028
+ return null;
1029
+ }
1030
+ function getQualifier(source) {
1031
+ return {
1032
+ table: source.alias ?? source.name,
1033
+ schema: source.schema
1034
+ };
1035
+ }
1036
+ function isUnqualifiedColumnRef(expr) {
1037
+ return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && (!("table" in expr) || !expr.table);
1038
+ }
1039
+ function isQualifiedColumnRef(expr) {
1040
+ return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && "table" in expr && !!expr.table;
1041
+ }
1042
+ function getColumnName(col) {
1043
+ if (typeof col.column === "string") {
1044
+ return col.column;
1045
+ }
1046
+ if (col.column && "expr" in col.column && col.column.expr?.value) {
1047
+ return String(col.column.expr.value);
1048
+ }
1049
+ return null;
1050
+ }
1051
+ function applyQualifier(col, qualifier) {
1052
+ col.table = qualifier.table;
1053
+ if (!("schema" in col) || !col.schema) {
1054
+ col.schema = qualifier.schema;
1055
+ }
1056
+ }
1057
+ function unwrapColumnRef(expr) {
1058
+ if (!expr || typeof expr !== "object")
1059
+ return null;
1060
+ if ("type" in expr && expr.type === "column_ref") {
1061
+ return expr;
1062
+ }
1063
+ if ("expr" in expr && expr.expr) {
1064
+ return unwrapColumnRef(expr.expr);
1065
+ }
1066
+ if ("ast" in expr && expr.ast && typeof expr.ast === "object") {
1067
+ return null;
1068
+ }
1069
+ if ("args" in expr && expr.args) {
1070
+ const args = expr.args;
1071
+ if (args.expr) {
1072
+ return unwrapColumnRef(args.expr);
1073
+ }
1074
+ if (args.value && args.value.length === 1) {
1075
+ return unwrapColumnRef(args.value[0]);
1076
+ }
1077
+ }
1078
+ return null;
1079
+ }
1080
+ function isBinaryExpr(expr) {
1081
+ return !!expr && typeof expr === "object" && "type" in expr && expr.type === "binary_expr";
1082
+ }
1083
+ function walkOnClause(expr, leftQualifier, rightQualifier, ambiguousColumns) {
1084
+ if (!expr || typeof expr !== "object")
1085
+ return false;
1086
+ let transformed = false;
1087
+ if (isBinaryExpr(expr)) {
1088
+ const left = expr.left;
1089
+ const right = expr.right;
1090
+ const leftCol = unwrapColumnRef(left);
1091
+ const rightCol = unwrapColumnRef(right);
1092
+ const leftUnqualified = leftCol ? isUnqualifiedColumnRef(leftCol) : false;
1093
+ const rightUnqualified = rightCol ? isUnqualifiedColumnRef(rightCol) : false;
1094
+ const leftQualified = leftCol ? isQualifiedColumnRef(leftCol) : false;
1095
+ const rightQualified = rightCol ? isQualifiedColumnRef(rightCol) : false;
1096
+ const leftColName = leftCol ? getColumnName(leftCol) : null;
1097
+ const rightColName = rightCol ? getColumnName(rightCol) : null;
1098
+ if (expr.operator === "=" && leftColName && rightColName && leftColName === rightColName) {
1099
+ if (leftUnqualified && rightUnqualified) {
1100
+ applyQualifier(leftCol, leftQualifier);
1101
+ applyQualifier(rightCol, rightQualifier);
1102
+ ambiguousColumns.add(leftColName);
1103
+ transformed = true;
1104
+ } else if (leftQualified && rightUnqualified) {
1105
+ applyQualifier(rightCol, rightQualifier);
1106
+ ambiguousColumns.add(rightColName);
1107
+ transformed = true;
1108
+ } else if (leftUnqualified && rightQualified) {
1109
+ applyQualifier(leftCol, leftQualifier);
1110
+ ambiguousColumns.add(leftColName);
1111
+ transformed = true;
1112
+ }
1113
+ }
1114
+ if (expr.operator === "=" && leftCol && rightCol && leftColName && rightColName && leftColName !== rightColName) {
1115
+ if (leftQualified && rightUnqualified && !rightColName.includes(".")) {
1116
+ applyQualifier(rightCol, rightQualifier);
1117
+ transformed = true;
1118
+ } else if (leftUnqualified && rightQualified && !leftColName.includes(".")) {
1119
+ applyQualifier(leftCol, leftQualifier);
1120
+ transformed = true;
1121
+ }
1122
+ }
1123
+ transformed = walkOnClause(isBinaryExpr(expr.left) ? expr.left : expr.left, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1124
+ transformed = walkOnClause(isBinaryExpr(expr.right) ? expr.right : expr.right, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1125
+ }
1126
+ return transformed;
1127
+ }
1128
+ function qualifyAmbiguousInExpression(expr, defaultQualifier, ambiguousColumns) {
1129
+ if (!expr || typeof expr !== "object")
1130
+ return false;
1131
+ let transformed = false;
1132
+ if (isUnqualifiedColumnRef(expr)) {
1133
+ const colName = getColumnName(expr);
1134
+ if (colName && ambiguousColumns.has(colName)) {
1135
+ applyQualifier(expr, defaultQualifier);
1136
+ transformed = true;
1137
+ }
1138
+ return transformed;
1139
+ }
1140
+ if (isBinaryExpr(expr)) {
1141
+ const binary = expr;
1142
+ transformed = qualifyAmbiguousInExpression(binary.left, defaultQualifier, ambiguousColumns) || transformed;
1143
+ transformed = qualifyAmbiguousInExpression(binary.right, defaultQualifier, ambiguousColumns) || transformed;
1144
+ return transformed;
1145
+ }
1146
+ if ("args" in expr && expr.args) {
1147
+ const args = expr.args;
1148
+ if (args.value && Array.isArray(args.value)) {
1149
+ for (const arg of args.value) {
1150
+ transformed = qualifyAmbiguousInExpression(arg, defaultQualifier, ambiguousColumns) || transformed;
1151
+ }
1152
+ }
1153
+ if (args.expr) {
1154
+ transformed = qualifyAmbiguousInExpression(args.expr, defaultQualifier, ambiguousColumns) || transformed;
1155
+ }
1156
+ }
1157
+ if ("over" in expr && expr.over && typeof expr.over === "object") {
1158
+ const over = expr.over;
1159
+ if (Array.isArray(over.partition)) {
1160
+ for (const part of over.partition) {
1161
+ transformed = qualifyAmbiguousInExpression(part, defaultQualifier, ambiguousColumns) || transformed;
1162
+ }
1163
+ }
1164
+ if (Array.isArray(over.orderby)) {
1165
+ for (const order of over.orderby) {
1166
+ transformed = qualifyAmbiguousInExpression(order, defaultQualifier, ambiguousColumns) || transformed;
1167
+ }
1168
+ }
1169
+ }
1170
+ return transformed;
1171
+ }
1172
+ function hasUnqualifiedColumns(expr) {
1173
+ if (!expr || typeof expr !== "object")
1174
+ return false;
1175
+ if ("type" in expr && expr.type === "binary_expr") {
1176
+ const left = expr.left;
1177
+ const right = expr.right;
1178
+ const leftCol = unwrapColumnRef(left);
1179
+ const rightCol = unwrapColumnRef(right);
1180
+ if (isUnqualifiedColumnRef(left) || isUnqualifiedColumnRef(right) || leftCol && isUnqualifiedColumnRef(leftCol) || rightCol && isUnqualifiedColumnRef(rightCol)) {
1181
+ return true;
1182
+ }
1183
+ if (isBinaryExpr(expr.left) && hasUnqualifiedColumns(expr.left))
1184
+ return true;
1185
+ if (isBinaryExpr(expr.right) && hasUnqualifiedColumns(expr.right))
1186
+ return true;
1187
+ }
1188
+ if ("args" in expr && expr.args) {
1189
+ const args = expr.args;
1190
+ if (args.expr && isUnqualifiedColumnRef(args.expr))
1191
+ return true;
1192
+ if (args.value) {
1193
+ for (const arg of args.value) {
1194
+ if (isUnqualifiedColumnRef(arg))
1195
+ return true;
1196
+ }
1197
+ }
1198
+ }
1199
+ return false;
1200
+ }
1201
+ function walkSelect(select) {
1202
+ let transformed = false;
1203
+ const ambiguousColumns = new Set;
1204
+ if (Array.isArray(select.from) && select.from.length >= 2) {
1205
+ const firstSource = getTableSource(select.from[0]);
1206
+ const defaultQualifier = firstSource ? getQualifier(firstSource) : null;
1207
+ let prevSource = firstSource;
1208
+ let hasAnyUnqualified = false;
1209
+ for (const from of select.from) {
1210
+ if ("join" in from) {
1211
+ const join = from;
1212
+ if (join.on && hasUnqualifiedColumns(join.on)) {
1213
+ hasAnyUnqualified = true;
1214
+ break;
1215
+ }
1216
+ }
1217
+ }
1218
+ if (!hasAnyUnqualified) {
1219
+ for (const from of select.from) {
1220
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1221
+ transformed = walkSelect(from.expr.ast) || transformed;
1222
+ }
1223
+ }
1224
+ } else {
1225
+ for (const from of select.from) {
1226
+ if ("join" in from) {
1227
+ const join = from;
1228
+ const currentSource = getTableSource(join);
1229
+ if (join.on && prevSource && currentSource) {
1230
+ const leftQualifier = getQualifier(prevSource);
1231
+ const rightQualifier = getQualifier(currentSource);
1232
+ transformed = walkOnClause(join.on, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1233
+ }
1234
+ if (join.using && prevSource && currentSource) {
1235
+ for (const usingCol of join.using) {
1236
+ if (typeof usingCol === "string") {
1237
+ ambiguousColumns.add(usingCol);
1238
+ } else if ("value" in usingCol) {
1239
+ ambiguousColumns.add(String(usingCol.value));
1240
+ }
1241
+ }
1242
+ }
1243
+ prevSource = currentSource;
1244
+ } else {
1245
+ const source = getTableSource(from);
1246
+ if (source) {
1247
+ prevSource = source;
1248
+ }
1249
+ }
1250
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1251
+ transformed = walkSelect(from.expr.ast) || transformed;
1252
+ }
1253
+ }
1254
+ if (ambiguousColumns.size > 0 && defaultQualifier) {
1255
+ if (Array.isArray(select.columns)) {
1256
+ for (const col of select.columns) {
1257
+ if ("expr" in col) {
1258
+ transformed = qualifyAmbiguousInExpression(col.expr, defaultQualifier, ambiguousColumns) || transformed;
1259
+ }
1260
+ }
1261
+ }
1262
+ transformed = qualifyAmbiguousInExpression(select.where, defaultQualifier, ambiguousColumns) || transformed;
1263
+ if (Array.isArray(select.orderby)) {
1264
+ for (const order of select.orderby) {
1265
+ if (order.expr) {
1266
+ transformed = qualifyAmbiguousInExpression(order.expr, defaultQualifier, ambiguousColumns) || transformed;
1267
+ }
1268
+ }
1269
+ }
1270
+ }
1271
+ }
1272
+ }
1273
+ if (select.with) {
1274
+ for (const cte of select.with) {
1275
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
1276
+ if (cteSelect && cteSelect.type === "select") {
1277
+ transformed = walkSelect(cteSelect) || transformed;
1278
+ }
1279
+ }
1280
+ }
1281
+ if (select._next) {
1282
+ transformed = walkSelect(select._next) || transformed;
1283
+ }
1284
+ return transformed;
1285
+ }
1286
+ function qualifyJoinColumns(ast) {
1287
+ const statements = Array.isArray(ast) ? ast : [ast];
1288
+ let transformed = false;
1289
+ for (const stmt of statements) {
1290
+ if (stmt.type === "select") {
1291
+ transformed = walkSelect(stmt) || transformed;
1292
+ } else if (stmt.type === "insert") {
1293
+ const insert = stmt;
1294
+ if (insert.values && typeof insert.values === "object" && "type" in insert.values && insert.values.type === "select") {
1295
+ transformed = walkSelect(insert.values) || transformed;
1296
+ }
1297
+ } else if (stmt.type === "update") {
1298
+ const update = stmt;
1299
+ const mainSource = update.table?.[0] ? getTableSource(update.table[0]) : null;
1300
+ const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
1301
+ const fromSources = update.from ?? [];
1302
+ const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
1303
+ if (update.where && defaultQualifier && firstFrom) {
1304
+ const ambiguous = new Set;
1305
+ transformed = walkOnClause(update.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
1306
+ transformed = qualifyAmbiguousInExpression(update.where, defaultQualifier, ambiguous) || transformed;
1307
+ }
1308
+ if (Array.isArray(update.returning) && defaultQualifier) {
1309
+ for (const ret of update.returning) {
1310
+ transformed = qualifyAmbiguousInExpression(ret, defaultQualifier, new Set) || transformed;
1311
+ }
1312
+ }
1313
+ } else if (stmt.type === "delete") {
1314
+ const del = stmt;
1315
+ const mainSource = del.table?.[0] ? getTableSource(del.table[0]) : null;
1316
+ const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
1317
+ const fromSources = del.from ?? [];
1318
+ const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
1319
+ if (del.where && defaultQualifier && firstFrom) {
1320
+ const ambiguous = new Set;
1321
+ transformed = walkOnClause(del.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
1322
+ transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, ambiguous) || transformed;
1323
+ } else if (del.where && defaultQualifier) {
1324
+ transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, new Set) || transformed;
1325
+ }
1326
+ }
1327
+ }
1328
+ return transformed;
1329
+ }
1330
+
1331
+ // src/sql/visitors/generate-series-alias.ts
1332
+ function getColumnName2(col) {
1333
+ if (typeof col.column === "string") {
1334
+ return col.column;
1335
+ }
1336
+ if (col.column && "expr" in col.column && col.column.expr?.value) {
1337
+ return String(col.column.expr.value);
1338
+ }
1339
+ return null;
1340
+ }
1341
+ function isColumnRef(expr) {
1342
+ return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref";
1343
+ }
1344
+ function isBinaryExpr2(expr) {
1345
+ return !!expr && typeof expr === "object" && "type" in expr && expr.type === "binary_expr";
1346
+ }
1347
+ function getGenerateSeriesAliases(from) {
1348
+ const aliases = new Set;
1349
+ if (!from || !Array.isArray(from))
1350
+ return aliases;
1351
+ for (const f of from) {
1352
+ if ("expr" in f && f.expr && typeof f.expr === "object") {
1353
+ const exprObj = f.expr;
1354
+ if (exprObj.type === "function" && "name" in exprObj) {
1355
+ const nameObj = exprObj.name;
1356
+ const nameParts = nameObj?.name;
1357
+ const fnName = nameParts?.[0]?.value;
1358
+ if (typeof fnName === "string" && fnName.toLowerCase() === "generate_series") {
1359
+ const alias = typeof f.as === "string" ? f.as : null;
1360
+ if (alias && !alias.includes("(")) {
1361
+ aliases.add(alias);
1362
+ }
1363
+ }
1364
+ }
1365
+ }
1366
+ }
1367
+ return aliases;
1368
+ }
1369
+ function rewriteAliasColumnRef(col, alias) {
1370
+ col.table = alias;
1371
+ col.column = { expr: { type: "default", value: "generate_series" } };
1372
+ }
1373
+ function walkExpression2(expr, aliases) {
1374
+ if (!expr || typeof expr !== "object")
1375
+ return false;
1376
+ let transformed = false;
1377
+ const exprObj = expr;
1378
+ if (isColumnRef(expr)) {
1379
+ if (!("table" in expr) || !expr.table) {
1380
+ const colName = getColumnName2(expr);
1381
+ if (colName && aliases.has(colName)) {
1382
+ rewriteAliasColumnRef(expr, colName);
1383
+ transformed = true;
1384
+ }
1385
+ }
1386
+ return transformed;
1387
+ }
1388
+ if (isBinaryExpr2(expr)) {
1389
+ const binary = expr;
1390
+ transformed = walkExpression2(binary.left, aliases) || transformed;
1391
+ transformed = walkExpression2(binary.right, aliases) || transformed;
1392
+ return transformed;
1393
+ }
1394
+ if (exprObj.type === "unary_expr" && exprObj.expr) {
1395
+ transformed = walkExpression2(exprObj.expr, aliases) || transformed;
1396
+ }
1397
+ if (exprObj.type === "cast" && exprObj.expr) {
1398
+ transformed = walkExpression2(exprObj.expr, aliases) || transformed;
1399
+ }
1400
+ if (exprObj.type === "case") {
1401
+ if (exprObj.expr) {
1402
+ transformed = walkExpression2(exprObj.expr, aliases) || transformed;
1403
+ }
1404
+ if (Array.isArray(exprObj.args)) {
1405
+ for (const whenClause of exprObj.args) {
1406
+ if (whenClause.cond) {
1407
+ transformed = walkExpression2(whenClause.cond, aliases) || transformed;
1408
+ }
1409
+ if (whenClause.result) {
1410
+ transformed = walkExpression2(whenClause.result, aliases) || transformed;
1411
+ }
1412
+ }
1413
+ }
1414
+ }
1415
+ if ("args" in exprObj && exprObj.args) {
1416
+ const args = exprObj.args;
1417
+ if (Array.isArray(args.value)) {
1418
+ for (const arg of args.value) {
1419
+ transformed = walkExpression2(arg, aliases) || transformed;
1420
+ }
1421
+ } else if (args.expr) {
1422
+ transformed = walkExpression2(args.expr, aliases) || transformed;
1423
+ }
1424
+ }
1425
+ if ("over" in exprObj && exprObj.over && typeof exprObj.over === "object") {
1426
+ const over = exprObj.over;
1427
+ if (Array.isArray(over.partition)) {
1428
+ for (const part of over.partition) {
1429
+ transformed = walkExpression2(part, aliases) || transformed;
1430
+ }
1431
+ }
1432
+ if (Array.isArray(over.orderby)) {
1433
+ for (const order of over.orderby) {
1434
+ transformed = walkExpression2(order, aliases) || transformed;
1435
+ }
1436
+ }
1437
+ }
1438
+ if ("ast" in exprObj && exprObj.ast) {
1439
+ const subAst = exprObj.ast;
1440
+ if (subAst.type === "select") {
1441
+ transformed = walkSelect2(subAst) || transformed;
1442
+ }
1443
+ }
1444
+ if (exprObj.type === "expr_list" && Array.isArray(exprObj.value)) {
1445
+ for (const item of exprObj.value) {
1446
+ transformed = walkExpression2(item, aliases) || transformed;
1447
+ }
1448
+ }
1449
+ return transformed;
1450
+ }
1451
+ function walkFrom2(from, aliases) {
1452
+ if (!from || !Array.isArray(from))
1453
+ return false;
1454
+ let transformed = false;
1455
+ for (const f of from) {
1456
+ if ("join" in f) {
1457
+ const join = f;
1458
+ transformed = walkExpression2(join.on, aliases) || transformed;
1459
+ }
1460
+ if ("expr" in f && f.expr && "ast" in f.expr) {
1461
+ transformed = walkSelect2(f.expr.ast) || transformed;
1462
+ }
1463
+ }
1464
+ return transformed;
1465
+ }
1466
+ function walkSelect2(select) {
1467
+ let transformed = false;
1468
+ const aliases = getGenerateSeriesAliases(select.from);
1469
+ if (select.with) {
1470
+ for (const cte of select.with) {
1471
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
1472
+ if (cteSelect && cteSelect.type === "select") {
1473
+ transformed = walkSelect2(cteSelect) || transformed;
1474
+ }
1475
+ }
1476
+ }
1477
+ transformed = walkFrom2(select.from, aliases) || transformed;
1478
+ transformed = walkExpression2(select.where, aliases) || transformed;
1479
+ if (select.having) {
1480
+ if (Array.isArray(select.having)) {
1481
+ for (const h of select.having) {
1482
+ transformed = walkExpression2(h, aliases) || transformed;
1483
+ }
1484
+ } else {
1485
+ transformed = walkExpression2(select.having, aliases) || transformed;
1486
+ }
1487
+ }
1488
+ if (Array.isArray(select.columns)) {
1489
+ for (const col of select.columns) {
1490
+ if ("expr" in col) {
1491
+ transformed = walkExpression2(col.expr, aliases) || transformed;
1492
+ }
1493
+ }
1494
+ }
1495
+ if (Array.isArray(select.groupby)) {
1496
+ for (const g of select.groupby) {
1497
+ transformed = walkExpression2(g, aliases) || transformed;
1498
+ }
1499
+ }
1500
+ if (Array.isArray(select.orderby)) {
1501
+ for (const order of select.orderby) {
1502
+ if (order.expr) {
1503
+ transformed = walkExpression2(order.expr, aliases) || transformed;
1504
+ }
1505
+ }
1506
+ }
1507
+ if (select._orderby) {
1508
+ for (const order of select._orderby) {
1509
+ if (order.expr) {
1510
+ transformed = walkExpression2(order.expr, aliases) || transformed;
1511
+ }
1512
+ }
1513
+ }
1514
+ if (select._next) {
1515
+ transformed = walkSelect2(select._next) || transformed;
1516
+ }
1517
+ return transformed;
1518
+ }
1519
+ function rewriteGenerateSeriesAliases(ast) {
1520
+ const statements = Array.isArray(ast) ? ast : [ast];
1521
+ let transformed = false;
1522
+ for (const stmt of statements) {
1523
+ if (stmt.type === "select") {
1524
+ transformed = walkSelect2(stmt) || transformed;
1525
+ }
1526
+ }
1527
+ return transformed;
1528
+ }
1529
+
1530
+ // src/sql/visitors/union-with-hoister.ts
1531
+ function getCteName(cte) {
1532
+ const nameObj = cte.name;
1533
+ if (!nameObj)
1534
+ return null;
1535
+ const value = nameObj.value;
1536
+ if (typeof value === "string")
1537
+ return value;
1538
+ return null;
1539
+ }
1540
+ function hoistWithInSelect(select) {
1541
+ if (!select.set_op || !select._next)
1542
+ return false;
1543
+ const arms = [];
1544
+ let current = select;
1545
+ while (current && current.type === "select") {
1546
+ arms.push(current);
1547
+ current = current._next;
1548
+ }
1549
+ const mergedWith = [];
1550
+ const seen = new Set;
1551
+ let hasWithBeyondFirst = false;
1552
+ for (const arm of arms) {
1553
+ if (arm.with && arm.with.length > 0) {
1554
+ if (arm !== arms[0]) {
1555
+ hasWithBeyondFirst = true;
1556
+ }
1557
+ for (const cte of arm.with) {
1558
+ const cteName = getCteName(cte);
1559
+ if (!cteName)
1560
+ return false;
1561
+ if (seen.has(cteName)) {
1562
+ return false;
1563
+ }
1564
+ seen.add(cteName);
1565
+ mergedWith.push(cte);
1566
+ }
1567
+ }
1568
+ }
1569
+ if (!hasWithBeyondFirst)
1570
+ return false;
1571
+ arms[0].with = mergedWith;
1572
+ if ("parentheses_symbol" in arms[0]) {
1573
+ arms[0].parentheses_symbol = false;
1574
+ }
1575
+ for (let i = 1;i < arms.length; i++) {
1576
+ arms[i].with = null;
1577
+ }
1578
+ return true;
1579
+ }
1580
+ function walkSelect3(select) {
1581
+ let transformed = false;
1582
+ if (select.with) {
1583
+ for (const cte of select.with) {
1584
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
1585
+ if (cteSelect && cteSelect.type === "select") {
1586
+ transformed = walkSelect3(cteSelect) || transformed;
1587
+ }
1588
+ }
1589
+ }
1590
+ if (Array.isArray(select.from)) {
1591
+ for (const from of select.from) {
1592
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1593
+ transformed = walkSelect3(from.expr.ast) || transformed;
1594
+ }
1595
+ }
1596
+ }
1597
+ transformed = hoistWithInSelect(select) || transformed;
1598
+ if (select._next) {
1599
+ transformed = walkSelect3(select._next) || transformed;
1600
+ }
1601
+ return transformed;
1602
+ }
1603
+ function hoistUnionWith(ast) {
1604
+ const statements = Array.isArray(ast) ? ast : [ast];
1605
+ let transformed = false;
1606
+ for (const stmt of statements) {
1607
+ if (stmt.type === "select") {
1608
+ transformed = walkSelect3(stmt) || transformed;
1609
+ }
1610
+ }
1611
+ return transformed;
1612
+ }
1613
+
1614
+ // src/sql/ast-transformer.ts
1615
+ var { Parser } = nodeSqlParser;
1616
+ var parser = new Parser;
1617
+ var CACHE_SIZE = 500;
1618
+ var transformCache = new Map;
1619
+ function getCachedOrTransform(query, transform) {
1620
+ const cached = transformCache.get(query);
1621
+ if (cached) {
1622
+ transformCache.delete(query);
1623
+ transformCache.set(query, cached);
1624
+ return cached;
1625
+ }
1626
+ const result = transform();
1627
+ if (transformCache.size >= CACHE_SIZE) {
1628
+ const oldestKey = transformCache.keys().next().value;
1629
+ if (oldestKey) {
1630
+ transformCache.delete(oldestKey);
1631
+ }
1632
+ }
1633
+ transformCache.set(query, result);
1634
+ return result;
1635
+ }
1636
+ var DEBUG_ENV = "DRIZZLE_DUCKDB_DEBUG_AST";
1637
+ function hasJoin(query) {
1638
+ return /\bjoin\b/i.test(query);
1639
+ }
1640
+ function debugLog(message, payload) {
1641
+ if (process?.env?.[DEBUG_ENV]) {
1642
+ console.debug("[duckdb-ast]", message, payload ?? "");
1643
+ }
1644
+ }
1645
+ function transformSQL(query) {
1646
+ const needsArrayTransform = query.includes("@>") || query.includes("<@") || query.includes("&&");
1647
+ const needsJoinTransform = hasJoin(query) || /\bupdate\b/i.test(query) || /\bdelete\b/i.test(query);
1648
+ const needsUnionTransform = /\bunion\b/i.test(query) || /\bintersect\b/i.test(query) || /\bexcept\b/i.test(query);
1649
+ const needsGenerateSeriesTransform = /\bgenerate_series\b/i.test(query);
1650
+ if (!needsArrayTransform && !needsJoinTransform && !needsUnionTransform && !needsGenerateSeriesTransform) {
1651
+ return { sql: query, transformed: false };
1652
+ }
1653
+ return getCachedOrTransform(query, () => {
1654
+ try {
1655
+ const ast = parser.astify(query, { database: "PostgreSQL" });
1656
+ let transformed = false;
1657
+ if (needsArrayTransform) {
1658
+ transformed = transformArrayOperators(ast) || transformed;
1659
+ }
1660
+ if (needsJoinTransform) {
1661
+ transformed = qualifyJoinColumns(ast) || transformed;
1662
+ }
1663
+ if (needsGenerateSeriesTransform) {
1664
+ transformed = rewriteGenerateSeriesAliases(ast) || transformed;
1665
+ }
1666
+ if (needsUnionTransform) {
1667
+ transformed = hoistUnionWith(ast) || transformed;
1668
+ }
1669
+ if (!transformed) {
1670
+ debugLog("AST parsed but no transformation applied", {
1671
+ join: needsJoinTransform
1672
+ });
1673
+ return { sql: query, transformed: false };
1674
+ }
1675
+ const transformedSql = parser.sqlify(ast, { database: "PostgreSQL" });
1676
+ return { sql: transformedSql, transformed: true };
1677
+ } catch (err) {
1678
+ debugLog("AST transform failed; returning original SQL", {
1679
+ error: err.message
1680
+ });
1681
+ return { sql: query, transformed: false };
1682
+ }
1683
+ });
1684
+ }
1685
+
1686
+ // src/dialect.ts
1687
+ class DuckDBDialect extends PgDialect {
1688
+ static [entityKind2] = "DuckDBPgDialect";
1689
+ hasPgJsonColumn = false;
1690
+ savepointsSupported = 0 /* Unknown */;
1691
+ resetPgJsonFlag() {
1692
+ this.hasPgJsonColumn = false;
1693
+ }
1694
+ markPgJsonDetected() {
1695
+ this.hasPgJsonColumn = true;
1696
+ }
1697
+ assertNoPgJsonColumns() {
1698
+ if (this.hasPgJsonColumn) {
1699
+ throw new Error("Pg JSON/JSONB columns are not supported in DuckDB. Replace them with duckDbJson() to use DuckDB's native JSON type.");
1700
+ }
1701
+ }
1702
+ areSavepointsUnsupported() {
1703
+ return this.savepointsSupported === 2 /* No */;
1704
+ }
1705
+ markSavepointsSupported() {
1706
+ this.savepointsSupported = 1 /* Yes */;
1707
+ }
1708
+ markSavepointsUnsupported() {
1709
+ this.savepointsSupported = 2 /* No */;
1710
+ }
1711
+ async migrate(migrations, session, config) {
1712
+ const migrationConfig = typeof config === "string" ? { migrationsFolder: config } : config;
1713
+ const migrationsSchema = migrationConfig.migrationsSchema ?? "drizzle";
1714
+ const migrationsTable = migrationConfig.migrationsTable ?? "__drizzle_migrations";
1715
+ const migrationsSequence = `${migrationsTable}_id_seq`;
1716
+ const legacySequence = "migrations_pk_seq";
1717
+ const escapeIdentifier = (value) => value.replace(/"/g, '""');
1718
+ const sequenceLiteral = `"${escapeIdentifier(migrationsSchema)}"."${escapeIdentifier(migrationsSequence)}"`;
1719
+ const migrationTableCreate = sql2`
1720
+ CREATE TABLE IF NOT EXISTS ${sql2.identifier(migrationsSchema)}.${sql2.identifier(migrationsTable)} (
1721
+ id integer PRIMARY KEY default nextval('${sql2.raw(sequenceLiteral)}'),
1722
+ hash text NOT NULL,
1723
+ created_at bigint
1724
+ )
1725
+ `;
1726
+ await session.execute(sql2`CREATE SCHEMA IF NOT EXISTS ${sql2.identifier(migrationsSchema)}`);
1727
+ await session.execute(sql2`CREATE SEQUENCE IF NOT EXISTS ${sql2.identifier(migrationsSchema)}.${sql2.identifier(migrationsSequence)}`);
1728
+ if (legacySequence !== migrationsSequence) {
1729
+ await session.execute(sql2`CREATE SEQUENCE IF NOT EXISTS ${sql2.identifier(migrationsSchema)}.${sql2.identifier(legacySequence)}`);
1730
+ }
1731
+ await session.execute(migrationTableCreate);
1732
+ const dbMigrations = await session.all(sql2`select id, hash, created_at from ${sql2.identifier(migrationsSchema)}.${sql2.identifier(migrationsTable)} order by created_at desc limit 1`);
1733
+ const lastDbMigration = dbMigrations[0];
1734
+ await session.transaction(async (tx) => {
1735
+ for await (const migration of migrations) {
1736
+ if (!lastDbMigration || Number(lastDbMigration.created_at) < migration.folderMillis) {
1737
+ for (const stmt of migration.sql) {
1738
+ await tx.execute(sql2.raw(stmt));
1739
+ }
1740
+ await tx.execute(sql2`insert into ${sql2.identifier(migrationsSchema)}.${sql2.identifier(migrationsTable)} ("hash", "created_at") values(${migration.hash}, ${migration.folderMillis})`);
1741
+ }
1742
+ }
1743
+ });
1744
+ }
1745
+ prepareTyping(encoder) {
1746
+ if (is2(encoder, PgJsonb) || is2(encoder, PgJson)) {
1747
+ this.markPgJsonDetected();
1748
+ throw new Error("Pg JSON/JSONB columns are not supported in DuckDB. Replace them with duckDbJson() to use DuckDB's native JSON type.");
1749
+ } else if (is2(encoder, PgNumeric)) {
1750
+ return "decimal";
1751
+ } else if (is2(encoder, PgTime2)) {
1752
+ return "time";
1753
+ } else if (is2(encoder, PgTimestamp2) || is2(encoder, PgTimestampString2)) {
1754
+ return "timestamp";
1755
+ } else if (is2(encoder, PgDate2) || is2(encoder, PgDateString2)) {
1756
+ return "date";
1757
+ } else if (is2(encoder, PgUUID)) {
1758
+ return "uuid";
1759
+ } else {
1760
+ return "none";
1761
+ }
1762
+ }
1763
+ sqlToQuery(sqlObj, invokeSource) {
1764
+ const result = super.sqlToQuery(sqlObj, invokeSource);
1765
+ const transformed = transformSQL(result.sql);
1766
+ return {
1767
+ ...result,
1768
+ sql: transformed.sql
1769
+ };
1770
+ }
1771
+ }
1772
+
1773
+ // src/select-builder.ts
1774
+ import { is as is4 } from "drizzle-orm/entity";
1775
+ import {
1776
+ PgSelectBase,
1777
+ PgSelectBuilder
1778
+ } from "drizzle-orm/pg-core/query-builders";
1779
+ import { Subquery, ViewBaseConfig } from "drizzle-orm";
1780
+ import { PgViewBase } from "drizzle-orm/pg-core/view-base";
1781
+ import { SQL as SQL5 } from "drizzle-orm/sql/sql";
1782
+
1783
+ // src/sql/selection.ts
1784
+ import { Column as Column2, SQL as SQL4, getTableName as getTableName2, is as is3, sql as sql3 } from "drizzle-orm";
1785
+ function mapEntries(obj, prefix, fullJoin = false) {
1786
+ return Object.fromEntries(Object.entries(obj).filter(([key]) => key !== "enableRLS").map(([key, value]) => {
1787
+ const qualified = prefix ? `${prefix}.${key}` : key;
1788
+ if (fullJoin && is3(value, Column2)) {
1789
+ return [
1790
+ key,
1791
+ sql3`${value}`.mapWith(value).as(`${getTableName2(value.table)}.${value.name}`)
1792
+ ];
1793
+ }
1794
+ if (fullJoin && is3(value, SQL4)) {
1795
+ const col = value.getSQL().queryChunks.find((chunk) => is3(chunk, Column2));
1796
+ const tableName = col?.table && getTableName2(col?.table);
1797
+ return [key, value.as(tableName ? `${tableName}.${key}` : key)];
1798
+ }
1799
+ if (is3(value, SQL4) || is3(value, Column2)) {
1800
+ const aliased = is3(value, SQL4) ? value : sql3`${value}`.mapWith(value);
1801
+ return [key, aliased.as(qualified)];
1802
+ }
1803
+ if (is3(value, SQL4.Aliased)) {
1804
+ return [key, value];
1805
+ }
1806
+ if (typeof value === "object" && value !== null) {
1807
+ return [
1808
+ key,
1809
+ mapEntries(value, qualified, fullJoin)
1810
+ ];
1811
+ }
1812
+ return [key, value];
1813
+ }));
1814
+ }
1815
+ function aliasFields(fields, fullJoin = false) {
1816
+ return mapEntries(fields, undefined, fullJoin);
1817
+ }
1818
+
1819
+ // src/select-builder.ts
1820
+ import { getTableColumns } from "drizzle-orm/utils";
1821
+
1822
+ class DuckDBSelectBuilder extends PgSelectBuilder {
1823
+ _fields;
1824
+ _session;
1825
+ _dialect;
1826
+ _withList = [];
1827
+ _distinct;
1828
+ constructor(config) {
1829
+ super(config);
1830
+ this._fields = config.fields;
1831
+ this._session = config.session;
1832
+ this._dialect = config.dialect;
1833
+ if (config.withList) {
1834
+ this._withList = config.withList;
1835
+ }
1836
+ this._distinct = config.distinct;
1837
+ }
1838
+ from(source) {
1839
+ const isPartialSelect = !!this._fields;
1840
+ const src = source;
1841
+ let fields;
1842
+ if (this._fields) {
1843
+ fields = this._fields;
1844
+ } else if (is4(src, Subquery)) {
1845
+ fields = Object.fromEntries(Object.keys(src._.selectedFields).map((key) => [
1846
+ key,
1847
+ src[key]
1848
+ ]));
1849
+ } else if (is4(src, PgViewBase)) {
1850
+ fields = src[ViewBaseConfig]?.selectedFields;
1851
+ } else if (is4(src, SQL5)) {
1852
+ fields = {};
1853
+ } else {
1854
+ fields = aliasFields(getTableColumns(src), !isPartialSelect);
1855
+ }
1856
+ return new PgSelectBase({
1857
+ table: src,
1858
+ fields,
1859
+ isPartialSelect,
1860
+ session: this._session,
1861
+ dialect: this._dialect,
1862
+ withList: this._withList,
1863
+ distinct: this._distinct
1864
+ });
1865
+ }
1866
+ }
1867
+
1868
+ // src/pool.ts
1869
+ import { DuckDBConnection } from "@duckdb/node-api";
1870
+ var POOL_PRESETS = {
1871
+ pulse: 4,
1872
+ standard: 6,
1873
+ jumbo: 8,
1874
+ mega: 12,
1875
+ giga: 16,
1876
+ local: 8,
1877
+ memory: 4
1878
+ };
1879
+ function resolvePoolSize(pool) {
1880
+ if (pool === false)
1881
+ return false;
1882
+ if (pool === undefined)
1883
+ return 4;
1884
+ if (typeof pool === "string")
1885
+ return POOL_PRESETS[pool];
1886
+ return pool.size ?? 4;
1887
+ }
1888
+ function createDuckDBConnectionPool(instance, options = {}) {
1889
+ const size = options.size && options.size > 0 ? options.size : 4;
1890
+ const acquireTimeout = options.acquireTimeout ?? 30000;
1891
+ const maxWaitingRequests = options.maxWaitingRequests ?? 100;
1892
+ const maxLifetimeMs = options.maxLifetimeMs;
1893
+ const idleTimeoutMs = options.idleTimeoutMs;
1894
+ const metadata = new WeakMap;
1895
+ const idle = [];
1896
+ const waiting = [];
1897
+ let total = 0;
1898
+ let closed = false;
1899
+ let pendingAcquires = 0;
1900
+ const shouldRecycle = (conn, now) => {
1901
+ if (maxLifetimeMs !== undefined && now - conn.createdAt >= maxLifetimeMs) {
1902
+ return true;
1903
+ }
1904
+ if (idleTimeoutMs !== undefined && now - conn.lastUsedAt >= idleTimeoutMs) {
1905
+ return true;
1906
+ }
1907
+ return false;
1908
+ };
1909
+ const acquire = async () => {
1910
+ if (closed) {
1911
+ throw new Error("DuckDB connection pool is closed");
1912
+ }
1913
+ while (idle.length > 0) {
1914
+ const pooled = idle.pop();
1915
+ const now = Date.now();
1916
+ if (shouldRecycle(pooled, now)) {
1917
+ await closeClientConnection(pooled.connection);
1918
+ total = Math.max(0, total - 1);
1919
+ metadata.delete(pooled.connection);
1920
+ continue;
1921
+ }
1922
+ pooled.lastUsedAt = now;
1923
+ metadata.set(pooled.connection, {
1924
+ createdAt: pooled.createdAt,
1925
+ lastUsedAt: pooled.lastUsedAt
1926
+ });
1927
+ return pooled.connection;
1928
+ }
1929
+ if (total < size) {
1930
+ pendingAcquires += 1;
1931
+ total += 1;
1932
+ try {
1933
+ const connection = await DuckDBConnection.create(instance);
1934
+ if (closed) {
1935
+ await closeClientConnection(connection);
1936
+ total -= 1;
1937
+ throw new Error("DuckDB connection pool is closed");
1938
+ }
1939
+ const now = Date.now();
1940
+ metadata.set(connection, { createdAt: now, lastUsedAt: now });
1941
+ return connection;
1942
+ } catch (error) {
1943
+ total -= 1;
1944
+ throw error;
1945
+ } finally {
1946
+ pendingAcquires -= 1;
1947
+ }
1948
+ }
1949
+ if (waiting.length >= maxWaitingRequests) {
1950
+ throw new Error(`DuckDB connection pool queue is full (max ${maxWaitingRequests} waiting requests)`);
1951
+ }
1952
+ return await new Promise((resolve, reject) => {
1953
+ const timeoutId = setTimeout(() => {
1954
+ const idx = waiting.findIndex((w) => w.timeoutId === timeoutId);
1955
+ if (idx !== -1) {
1956
+ waiting.splice(idx, 1);
1957
+ }
1958
+ reject(new Error(`DuckDB connection pool acquire timeout after ${acquireTimeout}ms`));
1959
+ }, acquireTimeout);
1960
+ waiting.push({ resolve, reject, timeoutId });
1961
+ });
1962
+ };
1963
+ const release = async (connection) => {
1964
+ const waiter = waiting.shift();
1965
+ if (waiter) {
1966
+ clearTimeout(waiter.timeoutId);
1967
+ const now2 = Date.now();
1968
+ const meta = metadata.get(connection) ?? { createdAt: now2, lastUsedAt: now2 };
1969
+ const expired = maxLifetimeMs !== undefined && now2 - meta.createdAt >= maxLifetimeMs;
1970
+ if (closed) {
1971
+ await closeClientConnection(connection);
1972
+ total = Math.max(0, total - 1);
1973
+ metadata.delete(connection);
1974
+ waiter.reject(new Error("DuckDB connection pool is closed"));
1975
+ return;
1976
+ }
1977
+ if (expired) {
1978
+ await closeClientConnection(connection);
1979
+ total = Math.max(0, total - 1);
1980
+ metadata.delete(connection);
1981
+ try {
1982
+ const replacement = await acquire();
1983
+ waiter.resolve(replacement);
1984
+ } catch (error) {
1985
+ waiter.reject(error);
1986
+ }
1987
+ return;
1988
+ }
1989
+ meta.lastUsedAt = now2;
1990
+ metadata.set(connection, meta);
1991
+ waiter.resolve(connection);
1992
+ return;
1993
+ }
1994
+ if (closed) {
1995
+ await closeClientConnection(connection);
1996
+ metadata.delete(connection);
1997
+ total = Math.max(0, total - 1);
1998
+ return;
1999
+ }
2000
+ const now = Date.now();
2001
+ const existingMeta = metadata.get(connection) ?? { createdAt: now, lastUsedAt: now };
2002
+ existingMeta.lastUsedAt = now;
2003
+ metadata.set(connection, existingMeta);
2004
+ if (maxLifetimeMs !== undefined && now - existingMeta.createdAt >= maxLifetimeMs) {
2005
+ await closeClientConnection(connection);
2006
+ total -= 1;
2007
+ metadata.delete(connection);
2008
+ return;
2009
+ }
2010
+ idle.push({
2011
+ connection,
2012
+ createdAt: existingMeta.createdAt,
2013
+ lastUsedAt: existingMeta.lastUsedAt
2014
+ });
2015
+ };
2016
+ const close = async () => {
2017
+ closed = true;
2018
+ const waiters = waiting.splice(0, waiting.length);
2019
+ for (const waiter of waiters) {
2020
+ clearTimeout(waiter.timeoutId);
2021
+ waiter.reject(new Error("DuckDB connection pool is closed"));
2022
+ }
2023
+ const toClose = idle.splice(0, idle.length);
2024
+ await Promise.allSettled(toClose.map((item) => closeClientConnection(item.connection)));
2025
+ total = Math.max(0, total - toClose.length);
2026
+ toClose.forEach((item) => metadata.delete(item.connection));
2027
+ const maxWait = 5000;
2028
+ const start = Date.now();
2029
+ while (pendingAcquires > 0 && Date.now() - start < maxWait) {
2030
+ await new Promise((r) => setTimeout(r, 10));
2031
+ }
2032
+ };
2033
+ return {
2034
+ acquire,
2035
+ release,
2036
+ close,
2037
+ size
2038
+ };
2039
+ }
2040
+
2041
+ // src/options.ts
2042
+ var DEFAULT_PREPARED_CACHE_SIZE = 32;
2043
+ function resolvePrepareCacheOption(option) {
2044
+ if (!option)
2045
+ return;
2046
+ if (option === true) {
2047
+ return { size: DEFAULT_PREPARED_CACHE_SIZE };
2048
+ }
2049
+ if (typeof option === "number") {
2050
+ const size2 = Math.max(1, Math.floor(option));
2051
+ return { size: size2 };
2052
+ }
2053
+ const size = option.size ?? DEFAULT_PREPARED_CACHE_SIZE;
2054
+ return { size: Math.max(1, Math.floor(size)) };
2055
+ }
2056
+
2057
+ // src/driver.ts
2058
+ class DuckDBDriver {
2059
+ client;
2060
+ dialect;
2061
+ options;
2062
+ static [entityKind3] = "DuckDBDriver";
2063
+ constructor(client, dialect, options = {}) {
2064
+ this.client = client;
2065
+ this.dialect = dialect;
2066
+ this.options = options;
2067
+ }
2068
+ createSession(schema) {
2069
+ return new DuckDBSession(this.client, this.dialect, schema, {
2070
+ logger: this.options.logger,
2071
+ rejectStringArrayLiterals: this.options.rejectStringArrayLiterals,
2072
+ prepareCache: this.options.prepareCache
2073
+ });
2074
+ }
2075
+ }
2076
+ function isConfigObject(data) {
2077
+ if (typeof data !== "object" || data === null)
2078
+ return false;
2079
+ if (data.constructor?.name !== "Object")
2080
+ return false;
2081
+ return "connection" in data || "client" in data || "pool" in data || "schema" in data || "logger" in data;
2082
+ }
2083
+ function createFromClient(client, config = {}, instance) {
2084
+ const dialect = new DuckDBDialect;
2085
+ const prepareCache = resolvePrepareCacheOption(config.prepareCache);
2086
+ const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
2087
+ let schema;
2088
+ if (config.schema) {
2089
+ const tablesConfig = extractTablesRelationalConfig(config.schema, createTableRelationsHelpers);
2090
+ schema = {
2091
+ fullSchema: config.schema,
2092
+ schema: tablesConfig.tables,
2093
+ tableNamesMap: tablesConfig.tableNamesMap
2094
+ };
2095
+ }
2096
+ const driver = new DuckDBDriver(client, dialect, {
2097
+ logger,
2098
+ rejectStringArrayLiterals: config.rejectStringArrayLiterals,
2099
+ prepareCache
2100
+ });
2101
+ const session = driver.createSession(schema);
2102
+ const db = new DuckDBDatabase(dialect, session, schema, client, instance);
2103
+ return db;
2104
+ }
2105
+ async function createFromConnectionString(path, instanceOptions, config = {}) {
2106
+ const instance = await DuckDBInstance2.create(path, instanceOptions);
2107
+ const poolSize = resolvePoolSize(config.pool);
2108
+ if (poolSize === false) {
2109
+ const connection = await instance.connect();
2110
+ return createFromClient(connection, config, instance);
2111
+ }
2112
+ const pool = createDuckDBConnectionPool(instance, { size: poolSize });
2113
+ return createFromClient(pool, config, instance);
2114
+ }
2115
+ function drizzle(clientOrConfigOrPath, config) {
2116
+ if (typeof clientOrConfigOrPath === "string") {
2117
+ return createFromConnectionString(clientOrConfigOrPath, undefined, config);
2118
+ }
2119
+ if (isConfigObject(clientOrConfigOrPath)) {
2120
+ const configObj = clientOrConfigOrPath;
2121
+ if ("connection" in configObj) {
2122
+ const connConfig = configObj;
2123
+ const { connection, ...restConfig } = connConfig;
2124
+ if (typeof connection === "string") {
2125
+ return createFromConnectionString(connection, undefined, restConfig);
2126
+ }
2127
+ return createFromConnectionString(connection.path, connection.options, restConfig);
2128
+ }
2129
+ if ("client" in configObj) {
2130
+ const clientConfig = configObj;
2131
+ const { client: clientValue, ...restConfig } = clientConfig;
2132
+ return createFromClient(clientValue, restConfig);
2133
+ }
2134
+ throw new Error("Invalid drizzle config: either connection or client must be provided");
2135
+ }
2136
+ return createFromClient(clientOrConfigOrPath, config);
2137
+ }
2138
+
2139
+ class DuckDBDatabase extends PgDatabase {
2140
+ dialect;
2141
+ session;
2142
+ static [entityKind3] = "DuckDBDatabase";
2143
+ $client;
2144
+ $instance;
2145
+ constructor(dialect, session, schema, client, instance) {
2146
+ super(dialect, session, schema);
2147
+ this.dialect = dialect;
2148
+ this.session = session;
2149
+ this.$client = client;
2150
+ this.$instance = instance;
2151
+ }
2152
+ async close() {
2153
+ if (isPool(this.$client) && this.$client.close) {
2154
+ await this.$client.close();
2155
+ }
2156
+ if (!isPool(this.$client)) {
2157
+ await closeClientConnection(this.$client);
2158
+ }
2159
+ if (this.$instance) {
2160
+ const maybeClosable = this.$instance;
2161
+ if (typeof maybeClosable.close === "function") {
2162
+ await maybeClosable.close();
2163
+ } else if (typeof maybeClosable.closeSync === "function") {
2164
+ maybeClosable.closeSync();
2165
+ }
2166
+ }
2167
+ }
2168
+ select(fields) {
2169
+ const selectedFields = fields ? aliasFields(fields) : undefined;
2170
+ return new DuckDBSelectBuilder({
2171
+ fields: selectedFields ?? undefined,
2172
+ session: this.session,
2173
+ dialect: this.dialect
2174
+ });
2175
+ }
2176
+ executeBatches(query, options = {}) {
2177
+ return this.session.executeBatches(query, options);
2178
+ }
2179
+ executeBatchesRaw(query, options = {}) {
2180
+ return this.session.executeBatchesRaw(query, options);
2181
+ }
2182
+ executeArrow(query) {
2183
+ return this.session.executeArrow(query);
2184
+ }
2185
+ async transaction(transaction) {
2186
+ return await this.session.transaction(transaction);
2187
+ }
2188
+ }
2189
+
2190
+ // src/introspect.ts
2191
+ import { sql as sql4 } from "drizzle-orm";
2192
+ var SYSTEM_SCHEMAS = new Set(["information_schema", "pg_catalog"]);
2193
+ var DEFAULT_IMPORT_BASE = "@leonardovida-md/drizzle-neo-duckdb/helpers";
2194
+ async function introspect(db, opts = {}) {
2195
+ const database = await resolveDatabase(db, opts.database, opts.allDatabases);
2196
+ const schemas = await resolveSchemas(db, database, opts.schemas);
2197
+ const includeViews = opts.includeViews ?? false;
2198
+ const tables = await loadTables(db, database, schemas, includeViews);
2199
+ const columns = await loadColumns(db, database, schemas);
2200
+ const constraints = await loadConstraints(db, database, schemas);
2201
+ const indexes = await loadIndexes(db, database, schemas);
2202
+ const grouped = buildTables(tables, columns, constraints, indexes);
2203
+ const schemaTs = emitSchema(grouped, {
2204
+ useCustomTimeTypes: opts.useCustomTimeTypes ?? true,
2205
+ mapJsonAsDuckDbJson: opts.mapJsonAsDuckDbJson ?? true,
2206
+ importBasePath: opts.importBasePath ?? DEFAULT_IMPORT_BASE
2207
+ });
2208
+ return {
2209
+ files: {
2210
+ schemaTs,
2211
+ metaJson: grouped
2212
+ }
2213
+ };
2214
+ }
2215
+ async function resolveDatabase(db, targetDatabase, allDatabases) {
2216
+ if (allDatabases) {
2217
+ return null;
2218
+ }
2219
+ if (targetDatabase) {
2220
+ return targetDatabase;
2221
+ }
2222
+ const rows = await db.execute(sql4`SELECT current_database() as current_database`);
2223
+ return rows[0]?.current_database ?? null;
2224
+ }
2225
+ async function resolveSchemas(db, database, targetSchemas) {
2226
+ if (targetSchemas?.length) {
2227
+ return targetSchemas;
2228
+ }
2229
+ const databaseFilter = database ? sql4`catalog_name = ${database}` : sql4`1 = 1`;
2230
+ const rows = await db.execute(sql4`SELECT schema_name FROM information_schema.schemata WHERE ${databaseFilter}`);
2231
+ return rows.map((row) => row.schema_name).filter((name) => !SYSTEM_SCHEMAS.has(name));
2232
+ }
2233
+ async function loadTables(db, database, schemas, includeViews) {
2234
+ const schemaFragments = schemas.map((schema) => sql4`${schema}`);
2235
+ const databaseFilter = database ? sql4`table_catalog = ${database}` : sql4`1 = 1`;
2236
+ return await db.execute(sql4`
2237
+ SELECT table_schema as schema_name, table_name, table_type
2238
+ FROM information_schema.tables
2239
+ WHERE ${databaseFilter}
2240
+ AND table_schema IN (${sql4.join(schemaFragments, sql4.raw(", "))})
2241
+ AND ${includeViews ? sql4`1 = 1` : sql4`table_type = 'BASE TABLE'`}
2242
+ ORDER BY table_schema, table_name
2243
+ `);
2244
+ }
2245
+ async function loadColumns(db, database, schemas) {
2246
+ const schemaFragments = schemas.map((schema) => sql4`${schema}`);
2247
+ const databaseFilter = database ? sql4`database_name = ${database}` : sql4`1 = 1`;
2248
+ return await db.execute(sql4`
2249
+ SELECT
2250
+ schema_name,
2251
+ table_name,
2252
+ column_name,
2253
+ column_index,
2254
+ column_default,
2255
+ is_nullable,
2256
+ data_type,
2257
+ character_maximum_length,
2258
+ numeric_precision,
2259
+ numeric_scale,
2260
+ internal
2261
+ FROM duckdb_columns()
2262
+ WHERE ${databaseFilter}
2263
+ AND schema_name IN (${sql4.join(schemaFragments, sql4.raw(", "))})
2264
+ ORDER BY schema_name, table_name, column_index
2265
+ `);
2266
+ }
2267
+ async function loadConstraints(db, database, schemas) {
2268
+ const schemaFragments = schemas.map((schema) => sql4`${schema}`);
2269
+ const databaseFilter = database ? sql4`database_name = ${database}` : sql4`1 = 1`;
2270
+ return await db.execute(sql4`
2271
+ SELECT
2272
+ schema_name,
2273
+ table_name,
2274
+ constraint_name,
2275
+ constraint_type,
2276
+ constraint_text,
2277
+ constraint_column_names,
2278
+ referenced_table,
2279
+ referenced_column_names
2280
+ FROM duckdb_constraints()
2281
+ WHERE ${databaseFilter}
2282
+ AND schema_name IN (${sql4.join(schemaFragments, sql4.raw(", "))})
2283
+ ORDER BY schema_name, table_name, constraint_index
2284
+ `);
2285
+ }
2286
+ async function loadIndexes(db, database, schemas) {
2287
+ const schemaFragments = schemas.map((schema) => sql4`${schema}`);
2288
+ const databaseFilter = database ? sql4`database_name = ${database}` : sql4`1 = 1`;
2289
+ return await db.execute(sql4`
2290
+ SELECT
2291
+ schema_name,
2292
+ table_name,
2293
+ index_name,
2294
+ is_unique,
2295
+ expressions
2296
+ FROM duckdb_indexes()
2297
+ WHERE ${databaseFilter}
2298
+ AND schema_name IN (${sql4.join(schemaFragments, sql4.raw(", "))})
2299
+ ORDER BY schema_name, table_name, index_name
2300
+ `);
2301
+ }
2302
+ function buildTables(tables, columns, constraints, indexes) {
2303
+ const byTable = {};
2304
+ for (const table of tables) {
2305
+ const key = tableKey(table.schema_name, table.table_name);
2306
+ byTable[key] = {
2307
+ schema: table.schema_name,
2308
+ name: table.table_name,
2309
+ kind: table.table_type === "VIEW" ? "view" : "table",
2310
+ columns: [],
2311
+ constraints: [],
2312
+ indexes: []
2313
+ };
2314
+ }
2315
+ for (const column of columns) {
2316
+ if (column.internal) {
2317
+ continue;
2318
+ }
2319
+ const key = tableKey(column.schema_name, column.table_name);
2320
+ const table = byTable[key];
2321
+ if (!table) {
2322
+ continue;
2323
+ }
2324
+ table.columns.push({
2325
+ name: column.column_name,
2326
+ dataType: column.data_type,
2327
+ columnDefault: column.column_default,
2328
+ nullable: column.is_nullable,
2329
+ characterLength: column.character_maximum_length,
2330
+ numericPrecision: column.numeric_precision,
2331
+ numericScale: column.numeric_scale
2332
+ });
2333
+ }
2334
+ for (const constraint of constraints) {
2335
+ const key = tableKey(constraint.schema_name, constraint.table_name);
2336
+ const table = byTable[key];
2337
+ if (!table) {
2338
+ continue;
2339
+ }
2340
+ if (!constraint.constraint_column_names?.length) {
2341
+ continue;
2342
+ }
2343
+ table.constraints.push({
2344
+ name: constraint.constraint_name,
2345
+ type: constraint.constraint_type,
2346
+ columns: constraint.constraint_column_names ?? [],
2347
+ referencedTable: constraint.referenced_table && constraint.referenced_column_names ? {
2348
+ schema: constraint.schema_name,
2349
+ name: constraint.referenced_table,
2350
+ columns: constraint.referenced_column_names
2351
+ } : undefined,
2352
+ rawExpression: constraint.constraint_text
2353
+ });
2354
+ }
2355
+ for (const index of indexes) {
2356
+ const key = tableKey(index.schema_name, index.table_name);
2357
+ const table = byTable[key];
2358
+ if (!table) {
2359
+ continue;
2360
+ }
2361
+ table.indexes.push(index);
2362
+ }
2363
+ return Object.values(byTable);
2364
+ }
2365
+ function emitSchema(catalog, options) {
2366
+ const imports = {
2367
+ drizzle: new Set,
2368
+ pgCore: new Set,
2369
+ local: new Set
2370
+ };
2371
+ imports.pgCore.add("pgSchema");
2372
+ const sorted = [...catalog].sort((a, b) => a.schema === b.schema ? a.name.localeCompare(b.name) : a.schema.localeCompare(b.schema));
2373
+ const lines = [];
2374
+ for (const schema of uniqueSchemas(sorted)) {
2375
+ imports.pgCore.add("pgSchema");
2376
+ const schemaVar = toSchemaIdentifier(schema);
2377
+ lines.push(`export const ${schemaVar} = pgSchema(${JSON.stringify(schema)});`, "");
2378
+ const tables = sorted.filter((table) => table.schema === schema);
2379
+ for (const table of tables) {
2380
+ lines.push(...emitTable(schemaVar, table, imports, options));
2381
+ lines.push("");
2382
+ }
2383
+ }
2384
+ const importsBlock = renderImports(imports, options.importBasePath);
2385
+ return [importsBlock, ...lines].join(`
2386
+ `).trim() + `
2387
+ `;
2388
+ }
2389
+ function emitTable(schemaVar, table, imports, options) {
2390
+ const tableVar = toIdentifier(table.name);
2391
+ const columnLines = [];
2392
+ for (const column of table.columns) {
2393
+ columnLines.push(` ${columnProperty(column.name)}: ${emitColumn(column, imports, options)},`);
2394
+ }
2395
+ const constraintBlock = emitConstraints(table, imports);
2396
+ const tableLines = [];
2397
+ tableLines.push(`export const ${tableVar} = ${schemaVar}.table(${JSON.stringify(table.name)}, {`);
2398
+ tableLines.push(...columnLines);
2399
+ tableLines.push(`}${constraintBlock ? "," : ""}${constraintBlock ? ` ${constraintBlock}` : ""});`);
2400
+ return tableLines;
2401
+ }
2402
+ function emitConstraints(table, imports) {
2403
+ const constraints = table.constraints.filter((constraint) => ["PRIMARY KEY", "FOREIGN KEY", "UNIQUE"].includes(constraint.type));
2404
+ if (!constraints.length) {
2405
+ return "";
2406
+ }
2407
+ const entries = [];
2408
+ for (const constraint of constraints) {
2409
+ const key = toIdentifier(constraint.name || `${table.name}_constraint`);
2410
+ if (constraint.type === "PRIMARY KEY") {
2411
+ imports.pgCore.add("primaryKey");
2412
+ entries.push(`${key}: primaryKey({ columns: [${constraint.columns.map((col) => `t.${toIdentifier(col)}`).join(", ")}], name: ${JSON.stringify(constraint.name)} })`);
2413
+ } else if (constraint.type === "UNIQUE" && constraint.columns.length > 1) {
2414
+ imports.pgCore.add("unique");
2415
+ entries.push(`${key}: unique(${JSON.stringify(constraint.name)}).on(${constraint.columns.map((col) => `t.${toIdentifier(col)}`).join(", ")})`);
2416
+ } else if (constraint.type === "FOREIGN KEY" && constraint.referencedTable) {
2417
+ imports.pgCore.add("foreignKey");
2418
+ const targetTable = toIdentifier(constraint.referencedTable.name);
2419
+ entries.push(`${key}: foreignKey({ columns: [${constraint.columns.map((col) => `t.${toIdentifier(col)}`).join(", ")}], foreignColumns: [${constraint.referencedTable.columns.map((col) => `${targetTable}.${toIdentifier(col)}`).join(", ")}], name: ${JSON.stringify(constraint.name)} })`);
2420
+ } else if (constraint.type === "UNIQUE" && constraint.columns.length === 1) {
2421
+ const columnName = constraint.columns[0];
2422
+ entries.push(`${key}: t.${toIdentifier(columnName)}.unique(${JSON.stringify(constraint.name)})`);
2423
+ }
2424
+ }
2425
+ if (!entries.length) {
2426
+ return "";
2427
+ }
2428
+ const lines = ["(t) => ({"];
2429
+ for (const entry of entries) {
2430
+ lines.push(` ${entry},`);
2431
+ }
2432
+ lines.push("})");
2433
+ return lines.join(`
2434
+ `);
2435
+ }
2436
+ function emitColumn(column, imports, options) {
2437
+ const mapping = mapDuckDbType(column, imports, options);
2438
+ let builder = mapping.builder;
2439
+ if (!column.nullable) {
2440
+ builder += ".notNull()";
2441
+ }
2442
+ const defaultFragment = buildDefault(column.columnDefault);
2443
+ if (defaultFragment) {
2444
+ imports.drizzle.add("sql");
2445
+ builder += defaultFragment;
2446
+ }
2447
+ return builder;
2448
+ }
2449
+ function buildDefault(defaultValue) {
2450
+ if (!defaultValue) {
2451
+ return "";
2452
+ }
2453
+ const trimmed = defaultValue.trim();
2454
+ if (!trimmed || trimmed.toUpperCase() === "NULL") {
2455
+ return "";
2456
+ }
2457
+ if (/^nextval\(/i.test(trimmed)) {
2458
+ return `.default(sql\`${trimmed}\`)`;
2459
+ }
2460
+ if (/^current_timestamp(?:\(\))?$/i.test(trimmed) || /^now\(\)$/i.test(trimmed)) {
2461
+ return `.defaultNow()`;
2462
+ }
2463
+ if (trimmed === "true" || trimmed === "false") {
2464
+ return `.default(${trimmed})`;
2465
+ }
2466
+ const numberValue = Number(trimmed);
2467
+ if (!Number.isNaN(numberValue)) {
2468
+ return `.default(${trimmed})`;
2469
+ }
2470
+ const stringLiteralMatch = /^'(.*)'$/.exec(trimmed);
2471
+ if (stringLiteralMatch) {
2472
+ const value = stringLiteralMatch[1]?.replace(/''/g, "'");
2473
+ return `.default(${JSON.stringify(value)})`;
2474
+ }
2475
+ return "";
2476
+ }
2477
+ function mapDuckDbType(column, imports, options) {
2478
+ const raw = column.dataType.trim();
2479
+ const upper = raw.toUpperCase();
2480
+ if (upper === "BOOLEAN" || upper === "BOOL") {
2481
+ imports.pgCore.add("boolean");
2482
+ return { builder: `boolean(${columnName(column.name)})` };
2483
+ }
2484
+ if (upper === "SMALLINT" || upper === "INT2" || upper === "INT16" || upper === "TINYINT") {
2485
+ imports.pgCore.add("integer");
2486
+ return { builder: `integer(${columnName(column.name)})` };
2487
+ }
2488
+ if (upper === "INTEGER" || upper === "INT" || upper === "INT4" || upper === "SIGNED") {
2489
+ imports.pgCore.add("integer");
2490
+ return { builder: `integer(${columnName(column.name)})` };
2491
+ }
2492
+ if (upper === "BIGINT" || upper === "INT8" || upper === "UBIGINT") {
2493
+ imports.pgCore.add("bigint");
2494
+ return {
2495
+ builder: `bigint(${columnName(column.name)}, { mode: 'number' })`
2496
+ };
2497
+ }
2498
+ const decimalMatch = /^DECIMAL\((\d+),(\d+)\)/i.exec(upper);
2499
+ const numericMatch = /^NUMERIC\((\d+),(\d+)\)/i.exec(upper);
2500
+ if (decimalMatch || numericMatch) {
2501
+ imports.pgCore.add("numeric");
2502
+ const [, precision, scale] = decimalMatch ?? numericMatch;
2503
+ return {
2504
+ builder: `numeric(${columnName(column.name)}, { precision: ${precision}, scale: ${scale} })`
2505
+ };
2506
+ }
2507
+ if (upper.startsWith("DECIMAL") || upper.startsWith("NUMERIC")) {
2508
+ imports.pgCore.add("numeric");
2509
+ const precision = column.numericPrecision;
2510
+ const scale = column.numericScale;
2511
+ const options2 = [];
2512
+ if (precision !== null && precision !== undefined) {
2513
+ options2.push(`precision: ${precision}`);
2514
+ }
2515
+ if (scale !== null && scale !== undefined) {
2516
+ options2.push(`scale: ${scale}`);
2517
+ }
2518
+ const suffix = options2.length ? `, { ${options2.join(", ")} }` : "";
2519
+ return { builder: `numeric(${columnName(column.name)}${suffix})` };
2520
+ }
2521
+ if (upper === "REAL" || upper === "FLOAT4") {
2522
+ imports.pgCore.add("real");
2523
+ return { builder: `real(${columnName(column.name)})` };
2524
+ }
2525
+ if (upper === "DOUBLE" || upper === "DOUBLE PRECISION" || upper === "FLOAT") {
2526
+ imports.pgCore.add("doublePrecision");
2527
+ return { builder: `doublePrecision(${columnName(column.name)})` };
2528
+ }
2529
+ const arrayMatch = /^(.*)\[(\d+)\]$/.exec(upper);
2530
+ if (arrayMatch) {
2531
+ imports.local.add("duckDbArray");
2532
+ const [, base, length] = arrayMatch;
2533
+ return {
2534
+ builder: `duckDbArray(${columnName(column.name)}, ${JSON.stringify(base)}, ${Number(length)})`
2535
+ };
2536
+ }
2537
+ const listMatch = /^(.*)\[\]$/.exec(upper);
2538
+ if (listMatch) {
2539
+ imports.local.add("duckDbList");
2540
+ const [, base] = listMatch;
2541
+ return {
2542
+ builder: `duckDbList(${columnName(column.name)}, ${JSON.stringify(base)})`
2543
+ };
2544
+ }
2545
+ if (upper.startsWith("CHAR(") || upper === "CHAR") {
2546
+ imports.pgCore.add("char");
2547
+ const length = column.characterLength;
2548
+ const lengthPart = typeof length === "number" ? `, { length: ${length} }` : "";
2549
+ return { builder: `char(${columnName(column.name)}${lengthPart})` };
2550
+ }
2551
+ if (upper.startsWith("VARCHAR")) {
2552
+ imports.pgCore.add("varchar");
2553
+ const length = column.characterLength;
2554
+ const lengthPart = typeof length === "number" ? `, { length: ${length} }` : "";
2555
+ return { builder: `varchar(${columnName(column.name)}${lengthPart})` };
2556
+ }
2557
+ if (upper === "TEXT" || upper === "STRING") {
2558
+ imports.pgCore.add("text");
2559
+ return { builder: `text(${columnName(column.name)})` };
2560
+ }
2561
+ if (upper === "UUID") {
2562
+ imports.pgCore.add("uuid");
2563
+ return { builder: `uuid(${columnName(column.name)})` };
2564
+ }
2565
+ if (upper === "JSON") {
2566
+ if (options.mapJsonAsDuckDbJson) {
2567
+ imports.local.add("duckDbJson");
2568
+ return { builder: `duckDbJson(${columnName(column.name)})` };
2569
+ }
2570
+ imports.pgCore.add("text");
2571
+ return { builder: `text(${columnName(column.name)}) /* JSON */` };
2572
+ }
2573
+ if (upper.startsWith("ENUM")) {
2574
+ imports.pgCore.add("text");
2575
+ const enumLiteral = raw.replace(/^ENUM\s*/i, "").trim();
2576
+ return {
2577
+ builder: `text(${columnName(column.name)}) /* ENUM ${enumLiteral} */`
2578
+ };
2579
+ }
2580
+ if (upper.startsWith("UNION")) {
2581
+ imports.pgCore.add("text");
2582
+ const unionLiteral = raw.replace(/^UNION\s*/i, "").trim();
2583
+ return {
2584
+ builder: `text(${columnName(column.name)}) /* UNION ${unionLiteral} */`
2585
+ };
2586
+ }
2587
+ if (upper === "INET") {
2588
+ imports.local.add("duckDbInet");
2589
+ return { builder: `duckDbInet(${columnName(column.name)})` };
2590
+ }
2591
+ if (upper === "INTERVAL") {
2592
+ imports.local.add("duckDbInterval");
2593
+ return { builder: `duckDbInterval(${columnName(column.name)})` };
2594
+ }
2595
+ if (upper === "BLOB" || upper === "BYTEA" || upper === "VARBINARY") {
2596
+ imports.local.add("duckDbBlob");
2597
+ return { builder: `duckDbBlob(${columnName(column.name)})` };
2598
+ }
2599
+ if (upper.startsWith("STRUCT")) {
2600
+ imports.local.add("duckDbStruct");
2601
+ const inner = upper.replace(/^STRUCT\s*\(/i, "").replace(/\)$/, "");
2602
+ const fields = parseStructFields(inner);
2603
+ const entries = fields.map(({ name, type }) => `${JSON.stringify(name)}: ${JSON.stringify(type)}`);
2604
+ return {
2605
+ builder: `duckDbStruct(${columnName(column.name)}, { ${entries.join(", ")} })`
2606
+ };
2607
+ }
2608
+ if (upper.startsWith("MAP(")) {
2609
+ imports.local.add("duckDbMap");
2610
+ const valueType = parseMapValue(upper);
2611
+ return {
2612
+ builder: `duckDbMap(${columnName(column.name)}, ${JSON.stringify(valueType)})`
2613
+ };
2614
+ }
2615
+ if (upper.startsWith("TIMESTAMP WITH TIME ZONE")) {
2616
+ if (options.useCustomTimeTypes) {
2617
+ imports.local.add("duckDbTimestamp");
2618
+ } else {
2619
+ imports.pgCore.add("timestamp");
2620
+ }
2621
+ const factory = options.useCustomTimeTypes ? `duckDbTimestamp(${columnName(column.name)}, { withTimezone: true })` : `timestamp(${columnName(column.name)}, { withTimezone: true })`;
2622
+ return { builder: factory };
2623
+ }
2624
+ if (upper.startsWith("TIMESTAMP")) {
2625
+ if (options.useCustomTimeTypes) {
2626
+ imports.local.add("duckDbTimestamp");
2627
+ return {
2628
+ builder: `duckDbTimestamp(${columnName(column.name)})`
2629
+ };
2630
+ }
2631
+ imports.pgCore.add("timestamp");
2632
+ return { builder: `timestamp(${columnName(column.name)})` };
2633
+ }
2634
+ if (upper === "TIME") {
2635
+ if (options.useCustomTimeTypes) {
2636
+ imports.local.add("duckDbTime");
2637
+ return { builder: `duckDbTime(${columnName(column.name)})` };
2638
+ }
2639
+ imports.pgCore.add("time");
2640
+ return { builder: `time(${columnName(column.name)})` };
2641
+ }
2642
+ if (upper === "DATE") {
2643
+ if (options.useCustomTimeTypes) {
2644
+ imports.local.add("duckDbDate");
2645
+ return { builder: `duckDbDate(${columnName(column.name)})` };
2646
+ }
2647
+ imports.pgCore.add("date");
2648
+ return { builder: `date(${columnName(column.name)})` };
2649
+ }
2650
+ imports.pgCore.add("text");
2651
+ return {
2652
+ builder: `text(${columnName(column.name)}) /* unsupported DuckDB type: ${upper} */`
2653
+ };
2654
+ }
2655
+ function parseStructFields(inner) {
2656
+ const result = [];
2657
+ for (const part of splitTopLevel(inner, ",")) {
2658
+ const trimmed = part.trim();
2659
+ if (!trimmed)
2660
+ continue;
2661
+ const match = /^"?([^"]+)"?\s+(.*)$/i.exec(trimmed);
2662
+ if (!match) {
2663
+ continue;
2664
+ }
2665
+ const [, name, type] = match;
2666
+ result.push({ name, type: type.trim() });
2667
+ }
2668
+ return result;
2669
+ }
2670
+ function parseMapValue(raw) {
2671
+ const inner = raw.replace(/^MAP\(/i, "").replace(/\)$/, "");
2672
+ const parts = splitTopLevel(inner, ",");
2673
+ if (parts.length < 2) {
2674
+ return "TEXT";
2675
+ }
2676
+ return parts[1]?.trim() ?? "TEXT";
2677
+ }
2678
+ function splitTopLevel(input, delimiter) {
2679
+ const parts = [];
2680
+ let depth = 0;
2681
+ let current = "";
2682
+ for (let i = 0;i < input.length; i += 1) {
2683
+ const char = input[i];
2684
+ if (char === "(")
2685
+ depth += 1;
2686
+ if (char === ")")
2687
+ depth = Math.max(0, depth - 1);
2688
+ if (char === delimiter && depth === 0) {
2689
+ parts.push(current);
2690
+ current = "";
2691
+ continue;
2692
+ }
2693
+ current += char;
2694
+ }
2695
+ if (current) {
2696
+ parts.push(current);
2697
+ }
2698
+ return parts;
2699
+ }
2700
+ function tableKey(schema, table) {
2701
+ return `${schema}.${table}`;
2702
+ }
2703
+ function toIdentifier(name) {
2704
+ const cleaned = name.replace(/[^A-Za-z0-9_]/g, "_");
2705
+ const parts = cleaned.split("_").filter(Boolean);
2706
+ const base = parts.map((part, index) => index === 0 ? part.toLowerCase() : capitalize(part.toLowerCase())).join("");
2707
+ const candidate = base || "item";
2708
+ return /^[A-Za-z_]/.test(candidate) ? candidate : `t${candidate}`;
2709
+ }
2710
+ function toSchemaIdentifier(schema) {
2711
+ const base = toIdentifier(schema);
2712
+ return base.endsWith("Schema") ? base : `${base}Schema`;
2713
+ }
2714
+ function columnProperty(column) {
2715
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(column)) {
2716
+ return toIdentifier(column);
2717
+ }
2718
+ return JSON.stringify(column);
2719
+ }
2720
+ function columnName(name) {
2721
+ return JSON.stringify(name);
2722
+ }
2723
+ function capitalize(value) {
2724
+ if (!value)
2725
+ return value;
2726
+ return value[0].toUpperCase() + value.slice(1);
2727
+ }
2728
+ function uniqueSchemas(tables) {
2729
+ const seen = new Set;
2730
+ const result = [];
2731
+ for (const table of tables) {
2732
+ if (!seen.has(table.schema)) {
2733
+ seen.add(table.schema);
2734
+ result.push(table.schema);
2735
+ }
2736
+ }
2737
+ return result;
2738
+ }
2739
+ function renderImports(imports, importBasePath) {
2740
+ const lines = [];
2741
+ const drizzle2 = [...imports.drizzle];
2742
+ if (drizzle2.length) {
2743
+ lines.push(`import { ${drizzle2.sort().join(", ")} } from 'drizzle-orm';`);
2744
+ }
2745
+ const pgCore = [...imports.pgCore];
2746
+ if (pgCore.length) {
2747
+ lines.push(`import { ${pgCore.sort().join(", ")} } from 'drizzle-orm/pg-core';`);
2748
+ }
2749
+ const local = [...imports.local];
2750
+ if (local.length) {
2751
+ lines.push(`import { ${local.sort().join(", ")} } from '${importBasePath}';`);
2752
+ }
2753
+ lines.push("");
2754
+ return lines.join(`
2755
+ `);
2756
+ }
2757
+
2758
+ // src/bin/duckdb-introspect.ts
2759
+ function parseArgs(argv) {
2760
+ const options = {
2761
+ outFile: path.resolve(process2.cwd(), "drizzle/schema.ts"),
2762
+ outMeta: undefined,
2763
+ allDatabases: false,
2764
+ includeViews: false,
2765
+ useCustomTimeTypes: true
2766
+ };
2767
+ for (let i = 0;i < argv.length; i += 1) {
2768
+ const arg = argv[i];
2769
+ switch (arg) {
2770
+ case "--url":
2771
+ options.url = argv[++i];
2772
+ break;
2773
+ case "--database":
2774
+ case "--db":
2775
+ options.database = argv[++i];
2776
+ break;
2777
+ case "--all-databases":
2778
+ options.allDatabases = true;
2779
+ break;
2780
+ case "--schema":
2781
+ case "--schemas":
2782
+ options.schemas = argv[++i]?.split(",").map((s) => s.trim()).filter(Boolean);
2783
+ break;
2784
+ case "--out":
2785
+ case "--outFile":
2786
+ options.outFile = path.resolve(process2.cwd(), argv[++i] ?? "drizzle/schema.ts");
2787
+ break;
2788
+ case "--out-json":
2789
+ case "--outJson":
2790
+ case "--json":
2791
+ options.outMeta = path.resolve(process2.cwd(), argv[++i] ?? "drizzle/schema.meta.json");
2792
+ break;
2793
+ case "--include-views":
2794
+ case "--includeViews":
2795
+ options.includeViews = true;
2796
+ break;
2797
+ case "--use-pg-time":
2798
+ options.useCustomTimeTypes = false;
2799
+ break;
2800
+ case "--import-base":
2801
+ options.importBasePath = argv[++i];
2802
+ break;
2803
+ case "--help":
2804
+ case "-h":
2805
+ printHelp();
2806
+ process2.exit(0);
2807
+ default:
2808
+ if (arg.startsWith("-")) {
2809
+ console.warn(`Unknown option ${arg}`);
2810
+ }
2811
+ }
2812
+ }
2813
+ return options;
2814
+ }
2815
+ function printHelp() {
2816
+ console.log(`duckdb-introspect
2817
+
2818
+ Usage:
2819
+ bun x duckdb-introspect --url <duckdb path|md:> [--schema my_schema] [--out ./drizzle/schema.ts]
2820
+
2821
+ Options:
2822
+ --url DuckDB database path (e.g. :memory:, ./local.duckdb, md:)
2823
+ --database, --db Database/catalog to introspect (default: current database)
2824
+ --all-databases Introspect all attached databases (not just current)
2825
+ --schema Comma separated schema list (defaults to all non-system schemas)
2826
+ --out Output file (default: ./drizzle/schema.ts)
2827
+ --json Optional JSON metadata output (default: ./drizzle/schema.meta.json)
2828
+ --include-views Include views in the generated schema
2829
+ --use-pg-time Use pg-core timestamp/date/time instead of DuckDB custom helpers
2830
+ --import-base Override import path for duckdb helpers (default: package name)
2831
+
2832
+ Database Filtering:
2833
+ By default, only tables from the current database are introspected. This prevents
2834
+ returning tables from all attached databases in MotherDuck workspaces.
2835
+
2836
+ Use --database to specify a different database, or --all-databases to introspect
2837
+ all attached databases.
2838
+
2839
+ Examples:
2840
+ # Local DuckDB file
2841
+ bun x duckdb-introspect --url ./my-database.duckdb --out ./schema.ts
2842
+
2843
+ # MotherDuck (requires MOTHERDUCK_TOKEN env var)
2844
+ MOTHERDUCK_TOKEN=xxx bun x duckdb-introspect --url md: --database my_cloud_db --out ./schema.ts
2845
+ `);
2846
+ }
2847
+ async function main() {
2848
+ const options = parseArgs(process2.argv.slice(2));
2849
+ if (!options.url) {
2850
+ printHelp();
2851
+ throw new Error("Missing required --url");
2852
+ }
2853
+ const instanceOptions = options.url.startsWith("md:") && process2.env.MOTHERDUCK_TOKEN ? { motherduck_token: process2.env.MOTHERDUCK_TOKEN } : undefined;
2854
+ const instance = await DuckDBInstance3.create(options.url, instanceOptions);
2855
+ const connection = await instance.connect();
2856
+ const db = drizzle(connection);
2857
+ try {
2858
+ const result = await introspect(db, {
2859
+ database: options.database,
2860
+ allDatabases: options.allDatabases,
2861
+ schemas: options.schemas,
2862
+ includeViews: options.includeViews,
2863
+ useCustomTimeTypes: options.useCustomTimeTypes,
2864
+ importBasePath: options.importBasePath
2865
+ });
2866
+ await mkdir(path.dirname(options.outFile), { recursive: true });
2867
+ await writeFile(options.outFile, result.files.schemaTs, "utf8");
2868
+ if (options.outMeta) {
2869
+ await mkdir(path.dirname(options.outMeta), { recursive: true });
2870
+ await writeFile(options.outMeta, JSON.stringify(result.files.metaJson, null, 2), "utf8");
2871
+ }
2872
+ console.log(`Wrote schema to ${options.outFile}`);
2873
+ if (options.outMeta) {
2874
+ console.log(`Wrote metadata to ${options.outMeta}`);
2875
+ }
2876
+ } finally {
2877
+ if ("closeSync" in connection && typeof connection.closeSync === "function") {
2878
+ connection.closeSync();
2879
+ }
2880
+ if ("closeSync" in instance && typeof instance.closeSync === "function") {
2881
+ instance.closeSync();
2882
+ } else if ("close" in instance && typeof instance.close === "function") {
2883
+ await instance.close();
2884
+ }
2885
+ }
2886
+ }
2887
+ main().catch((err) => {
2888
+ console.error(err instanceof Error ? err.message : err);
2889
+ process2.exit(1);
2890
+ });