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