@duckdbfan/drizzle-duckdb 0.0.6 → 1.3.1

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 +344 -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 +142 -0
  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 +3071 -209
  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 +510 -1
  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 -216
  54. package/src/value-wrappers-core.ts +168 -0
  55. package/src/value-wrappers.ts +165 -0
package/dist/index.mjs CHANGED
@@ -1,42 +1,174 @@
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";
25
+ import {
26
+ PgCustomColumn,
27
+ PgDate,
28
+ PgDateString,
29
+ PgInterval,
30
+ PgTime,
31
+ PgTimestamp,
32
+ PgTimestampString
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
+ }
28
155
  function mapResultRow(columns, row, joinsNotNullableMap) {
29
156
  const nullifyMap = {};
30
- const result = columns.reduce((result2, { path, field }, columnIndex) => {
157
+ const result = columns.reduce((acc, { path, field }, columnIndex) => {
31
158
  let decoder;
32
159
  if (is(field, Column)) {
33
160
  decoder = field;
34
161
  } else if (is(field, SQL)) {
35
162
  decoder = field.decoder;
36
163
  } else {
37
- decoder = field.sql.decoder;
164
+ const col = field.sql.queryChunks.find((chunk) => is(chunk, Column));
165
+ if (is(col, PgCustomColumn)) {
166
+ decoder = col;
167
+ } else {
168
+ decoder = field.sql.decoder;
169
+ }
38
170
  }
39
- let node = result2;
171
+ let node = acc;
40
172
  for (const [pathChunkIndex, pathChunk] of path.entries()) {
41
173
  if (pathChunkIndex < path.length - 1) {
42
174
  if (!(pathChunk in node)) {
@@ -45,8 +177,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
45
177
  node = node[pathChunk];
46
178
  continue;
47
179
  }
48
- const rawValue = row[columnIndex];
49
- 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);
50
182
  if (joinsNotNullableMap && is(field, Column) && path.length === 2) {
51
183
  const objectName = path[0];
52
184
  if (!(objectName in nullifyMap)) {
@@ -73,7 +205,7 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
73
205
  continue;
74
206
  }
75
207
  }
76
- return result2;
208
+ return acc;
77
209
  }, {});
78
210
  if (joinsNotNullableMap && Object.keys(nullifyMap).length > 0) {
79
211
  for (const [objectName, tableName] of Object.entries(nullifyMap)) {
@@ -84,87 +216,493 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
84
216
  }
85
217
  return result;
86
218
  }
87
- function aliasFields(fields, fullJoin = false) {
88
- return Object.fromEntries(Object.entries(fields).filter(([key]) => key !== "enableRLS").map(([key, value]) => {
89
- if (fullJoin && is(value, Column)) {
90
- return [
91
- key,
92
- sql`${value}`.as(`${getTableName(value.table)}.${value.name}`)
93
- ];
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}`);
94
361
  }
95
- if (fullJoin && is(value, SQL)) {
96
- const col = value.getSQL().queryChunks.find((chunk) => is(chunk, Column));
97
- const tableName = col?.table && getTableName(col?.table);
98
- 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
+ }
99
398
  }
100
- if (is(value, SQL) || is(value, Column)) {
101
- 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;
102
429
  }
103
- if (is(value, SQL.Aliased)) {
104
- 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);
105
501
  }
106
- if (typeof value === "object") {
107
- const parentKey = key;
108
- return [
109
- key,
110
- Object.fromEntries(Object.entries(value).filter(([childKey]) => childKey !== "enableRLS").map(([childKey, childValue]) => [
111
- childKey,
112
- (is(childValue, SQL) ? childValue : sql`${childValue}`).as(`${parentKey}.${childKey}`)
113
- ]))
114
- ];
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;
115
521
  }
116
- return [key, value];
117
- }));
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();
118
644
  }
119
- var tableIdPropSelectionRegex = new RegExp([
120
- `("(.+)"\\."(.+)")`,
121
- `(\\s+as\\s+'?(.+?)'?\\.'?(.+?)'?)?`
122
- ].join(""), "i");
123
645
 
124
646
  // src/session.ts
125
- 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
+ }
126
653
 
127
654
  class DuckDBPreparedQuery extends PgPreparedQuery {
128
655
  client;
656
+ dialect;
129
657
  queryString;
130
658
  params;
131
659
  logger;
132
660
  fields;
133
661
  _isResponseInArrayMode;
134
662
  customResultMapper;
663
+ rejectStringArrayLiterals;
664
+ prepareCache;
665
+ warnOnStringArrayLiteral;
135
666
  static [entityKind] = "DuckDBPreparedQuery";
136
- constructor(client, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper) {
667
+ constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
137
668
  super({ sql: queryString, params });
138
669
  this.client = client;
670
+ this.dialect = dialect;
139
671
  this.queryString = queryString;
140
672
  this.params = params;
141
673
  this.logger = logger;
142
674
  this.fields = fields;
143
675
  this._isResponseInArrayMode = _isResponseInArrayMode;
144
676
  this.customResultMapper = customResultMapper;
677
+ this.rejectStringArrayLiterals = rejectStringArrayLiterals;
678
+ this.prepareCache = prepareCache;
679
+ this.warnOnStringArrayLiteral = warnOnStringArrayLiteral;
145
680
  }
146
681
  async execute(placeholderValues = {}) {
147
- 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
+ });
148
687
  this.logger.logQuery(this.queryString, params);
149
- const {
150
- fields,
151
- client,
152
- joinsNotNullableMap,
153
- customResultMapper,
154
- queryString
155
- } = this;
156
- const rows = await client.all(queryString, ...params) ?? [];
157
- if (rows.length === 0 || !fields) {
158
- return rows;
159
- }
160
- const rowValues = rows.map((row) => Object.values(row));
161
- 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;
162
700
  }
163
701
  all(placeholderValues = {}) {
164
702
  return this.execute(placeholderValues);
165
703
  }
166
704
  isResponseInArrayMode() {
167
- return false;
705
+ return this._isResponseInArrayMode;
168
706
  }
169
707
  }
170
708
 
@@ -173,32 +711,117 @@ class DuckDBSession extends PgSession {
173
711
  schema;
174
712
  options;
175
713
  static [entityKind] = "DuckDBSession";
714
+ dialect;
176
715
  logger;
716
+ rejectStringArrayLiterals;
717
+ prepareCache;
718
+ hasWarnedArrayLiteral = false;
719
+ rollbackOnly = false;
177
720
  constructor(client, dialect, schema, options = {}) {
178
721
  super(dialect);
179
722
  this.client = client;
180
723
  this.schema = schema;
181
724
  this.options = options;
725
+ this.dialect = dialect;
182
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
+ };
183
733
  }
184
734
  prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
185
- 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);
186
736
  }
187
- async transaction(transaction) {
188
- const connection = "connect" in this.client ? await this.client.connect() : this.client;
189
- 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);
190
755
  const tx = new DuckDBTransaction(this.dialect, session, this.schema);
191
- await tx.execute(sql2`BEGIN TRANSACTION;`);
192
756
  try {
193
- const result = await transaction(tx);
194
- await tx.execute(sql2`commit`);
195
- return result;
196
- } catch (error) {
197
- await tx.execute(sql2`rollback`);
198
- 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
+ }
199
773
  } finally {
200
- await connection.close();
774
+ if (pinnedConnection && pool) {
775
+ await pool.release(pinnedConnection);
776
+ }
777
+ }
778
+ }
779
+ warnOnStringArrayLiteral = (query) => {
780
+ if (this.hasWarnedArrayLiteral) {
781
+ return;
201
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;
202
825
  }
203
826
  }
204
827
 
@@ -218,117 +841,1299 @@ class DuckDBTransaction extends PgTransaction {
218
841
  if (typeof config.deferrable === "boolean") {
219
842
  chunks.push(config.deferrable ? "deferrable" : "not deferrable");
220
843
  }
221
- return sql2.raw(chunks.join(" "));
844
+ return sql.raw(chunks.join(" "));
222
845
  }
223
846
  setTransaction(config) {
224
- 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);
225
857
  }
226
858
  async transaction(transaction) {
227
- const savepointName = `sp${this.nestedIndex + 1}`;
228
- const tx = new DuckDBTransaction(this.dialect, this.session, this.schema, this.nestedIndex + 1);
229
- 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
+ }
230
880
  try {
231
- const result = await transaction(tx);
232
- await tx.execute(sql2.raw(`release savepoint ${savepointName}`));
881
+ const result = await transaction(nestedTx);
882
+ if (createdSavepoint) {
883
+ await internals.session.execute(releaseSql);
884
+ }
233
885
  return result;
234
- } catch (err) {
235
- await tx.execute(sql2.raw(`rollback to savepoint ${savepointName}`));
236
- throw err;
886
+ } catch (error) {
887
+ if (createdSavepoint) {
888
+ await internals.session.execute(rollbackSql);
889
+ }
890
+ internals.session.markRollbackOnly();
891
+ throw error;
237
892
  }
238
893
  }
894
+ runNestedWithoutSavepoint(transaction, nestedTx, internals) {
895
+ return transaction(nestedTx).catch((error) => {
896
+ internals.session.markRollbackOnly();
897
+ throw error;
898
+ });
899
+ }
239
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.";
240
902
 
241
903
  // src/dialect.ts
242
904
  import { entityKind as entityKind2, is as is2 } from "drizzle-orm/entity";
243
905
  import {
244
- PgDate,
245
- PgDateString,
906
+ PgDate as PgDate2,
907
+ PgDateString as PgDateString2,
246
908
  PgDialect,
247
909
  PgJson,
248
910
  PgJsonb,
249
911
  PgNumeric,
250
- PgTime,
251
- PgTimestamp,
252
- PgTimestampString,
912
+ PgTime as PgTime2,
913
+ PgTimestamp as PgTimestamp2,
914
+ PgTimestampString as PgTimestampString2,
253
915
  PgUUID
254
916
  } from "drizzle-orm/pg-core";
255
917
  import {
256
- sql as sql3
918
+ sql as sql2
257
919
  } from "drizzle-orm";
258
920
 
259
- class DuckDBDialect extends PgDialect {
260
- static [entityKind2] = "DuckDBPgDialect";
261
- async migrate(migrations, session, config) {
262
- const migrationsSchema = config.migrationsSchema ?? "drizzle";
263
- const migrationsTable = config.migrationsTable ?? "__drizzle_migrations";
264
- const migrationTableCreate = sql3`
265
- CREATE TABLE IF NOT EXISTS ${sql3.identifier(migrationsSchema)}.${sql3.identifier(migrationsTable)} (
266
- id integer PRIMARY KEY default nextval('migrations_pk_seq'),
267
- hash text NOT NULL,
268
- created_at bigint
269
- )
270
- `;
271
- await session.execute(sql3.raw("CREATE SEQUENCE IF NOT EXISTS migrations_pk_seq"));
272
- await session.execute(sql3`CREATE SCHEMA IF NOT EXISTS ${sql3.identifier(migrationsSchema)}`);
273
- await session.execute(migrationTableCreate);
274
- 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`);
275
- const lastDbMigration = dbMigrations[0];
276
- await session.transaction(async (tx) => {
277
- for await (const migration of migrations) {
278
- if (!lastDbMigration || Number(lastDbMigration.created_at) < migration.folderMillis) {
279
- for (const stmt of migration.sql) {
280
- await tx.execute(sql3.raw(stmt));
281
- }
282
- 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]
283
945
  }
946
+ };
947
+ if (parent && key) {
948
+ parent[key] = fnExpr;
284
949
  }
285
- });
286
- }
287
- prepareTyping(encoder) {
288
- if (is2(encoder, PgJsonb) || is2(encoder, PgJson)) {
289
- throw new Error("JSON and JSONB types are not supported in DuckDB");
290
- } else if (is2(encoder, PgNumeric)) {
291
- return "decimal";
292
- } else if (is2(encoder, PgTime)) {
293
- return "time";
294
- } else if (is2(encoder, PgTimestamp) || is2(encoder, PgTimestampString)) {
295
- return "timestamp";
296
- } else if (is2(encoder, PgDate) || is2(encoder, PgDateString)) {
297
- return "date";
298
- } else if (is2(encoder, PgUUID)) {
299
- return "uuid";
950
+ transformed = true;
300
951
  } else {
301
- return "none";
952
+ transformed = walkExpression(binary.left, binary, "left") || transformed;
953
+ transformed = walkExpression(binary.right, binary, "right") || transformed;
302
954
  }
303
955
  }
304
- }
305
-
306
- // src/driver.ts
307
- import {
308
- PgSelectBase,
309
- PgSelectBuilder
310
- } from "drizzle-orm/pg-core/query-builders";
311
- import { SQL as SQL3 } from "drizzle-orm/sql/sql";
312
- import { Subquery, ViewBaseConfig } from "drizzle-orm";
313
- import { PgViewBase } from "drizzle-orm/pg-core/view-base";
314
- class DuckDBDriver {
315
- client;
316
- dialect;
317
- options;
318
- static [entityKind3] = "DuckDBDriver";
319
- constructor(client, dialect, options = {}) {
320
- this.client = client;
321
- this.dialect = dialect;
322
- this.options = options;
956
+ if ("type" in expr && exprObj.type === "unary_expr") {
957
+ if ("expr" in exprObj) {
958
+ transformed = walkExpression(exprObj.expr, exprObj, "expr") || transformed;
959
+ }
323
960
  }
324
- createSession(schema) {
325
- return new DuckDBSession(this.client, this.dialect, schema, {
326
- logger: this.options.logger
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;
1001
+ }
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
+ }
327
1794
  });
328
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";
1826
+ import {
1827
+ PgSelectBase,
1828
+ PgSelectBuilder
1829
+ } from "drizzle-orm/pg-core/query-builders";
1830
+ import { Subquery, ViewBaseConfig } from "drizzle-orm";
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
2109
+ class DuckDBDriver {
2110
+ client;
2111
+ dialect;
2112
+ options;
2113
+ static [entityKind3] = "DuckDBDriver";
2114
+ constructor(client, dialect, options = {}) {
2115
+ this.client = client;
2116
+ this.dialect = dialect;
2117
+ this.options = options;
2118
+ }
2119
+ createSession(schema) {
2120
+ return new DuckDBSession(this.client, this.dialect, schema, {
2121
+ logger: this.options.logger,
2122
+ rejectStringArrayLiterals: this.options.rejectStringArrayLiterals,
2123
+ prepareCache: this.options.prepareCache
2124
+ });
2125
+ }
2126
+ }
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;
329
2133
  }
330
- function drizzle(client, config = {}) {
2134
+ function createFromClient(client, config = {}, instance) {
331
2135
  const dialect = new DuckDBDialect;
2136
+ const prepareCache = resolvePrepareCacheOption(config.prepareCache);
332
2137
  const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
333
2138
  let schema;
334
2139
  if (config.schema) {
@@ -339,98 +2144,1155 @@ function drizzle(client, config = {}) {
339
2144
  tableNamesMap: tablesConfig.tableNamesMap
340
2145
  };
341
2146
  }
342
- const driver = new DuckDBDriver(client, dialect, { logger });
2147
+ const driver = new DuckDBDriver(client, dialect, {
2148
+ logger,
2149
+ rejectStringArrayLiterals: config.rejectStringArrayLiterals,
2150
+ prepareCache
2151
+ });
343
2152
  const session = driver.createSession(schema);
344
- 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);
345
2188
  }
346
2189
 
347
2190
  class DuckDBDatabase extends PgDatabase {
348
2191
  dialect;
349
2192
  session;
350
2193
  static [entityKind3] = "DuckDBDatabase";
351
- constructor(dialect, session, schema) {
2194
+ $client;
2195
+ $instance;
2196
+ constructor(dialect, session, schema, client, instance) {
352
2197
  super(dialect, session, schema);
353
2198
  this.dialect = dialect;
354
2199
  this.session = session;
2200
+ this.$client = client;
2201
+ this.$instance = instance;
355
2202
  }
356
- select(fields) {
357
- if (!fields) {
358
- return new DuckDBSelectBuilder({
359
- fields: fields ?? undefined,
360
- session: this.session,
361
- dialect: this.dialect
362
- });
2203
+ async close() {
2204
+ if (isPool(this.$client) && this.$client.close) {
2205
+ await this.$client.close();
363
2206
  }
364
- 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;
365
2221
  return new DuckDBSelectBuilder({
366
- fields: aliasedFields,
2222
+ fields: selectedFields ?? undefined,
367
2223
  session: this.session,
368
2224
  dialect: this.dialect
369
2225
  });
370
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
+ }
371
2236
  async transaction(transaction) {
372
2237
  return await this.session.transaction(transaction);
373
2238
  }
374
2239
  }
375
-
376
- class DuckDBSelectBuilder extends PgSelectBuilder {
377
- _fields;
378
- _session;
379
- _dialect;
380
- _withList = [];
381
- _distinct;
382
- constructor(config) {
383
- super(config);
384
- this._fields = config.fields;
385
- this._session = config.session;
386
- this._dialect = config.dialect;
387
- if (config.withList) {
388
- 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;
389
2253
  }
390
- this._distinct = config.distinct;
391
2254
  }
392
- from(source) {
393
- const isPartialSelect = !!this._fields;
394
- const src = source;
395
- let fields;
396
- if (this._fields) {
397
- fields = this._fields;
398
- } else if (is3(src, Subquery)) {
399
- fields = Object.fromEntries(Object.keys(src._.selectedFields).map((key) => [
400
- key,
401
- src[key]
402
- ]));
403
- } else if (is3(src, PgViewBase)) {
404
- fields = src[ViewBaseConfig]?.selectedFields;
405
- } else if (is3(src, SQL3)) {
406
- fields = {};
407
- } else {
408
- fields = aliasFields(getTableColumns(src), !isPartialSelect);
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;
409
2261
  }
410
- return new PgSelectBase({
411
- table: src,
412
- fields,
413
- isPartialSelect,
414
- session: this._session,
415
- dialect: this._dialect,
416
- withList: this._withList,
417
- distinct: this._distinct
418
- });
419
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
+ }
2327
+ }
2328
+ return value;
2329
+ }
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;
2341
+ }
2342
+ if (typeof value === "string") {
2343
+ const parsed = coerceArrayString(value);
2344
+ if (parsed !== undefined) {
2345
+ return parsed;
2346
+ }
2347
+ }
2348
+ return value;
2349
+ }
2350
+ })(name);
2351
+ var duckDbMap = (name, valueType) => customType({
2352
+ dataType() {
2353
+ return `MAP (STRING, ${valueType})`;
2354
+ },
2355
+ toDriver(value) {
2356
+ if (Object.keys(value).length === 0) {
2357
+ return buildMapLiteral(value, valueType);
2358
+ }
2359
+ return wrapMap(value, valueType);
2360
+ },
2361
+ fromDriver(value) {
2362
+ return value;
2363
+ }
2364
+ })(name);
2365
+ var duckDbStruct = (name, schema) => customType({
2366
+ dataType() {
2367
+ const fields = Object.entries(schema).map(([key, type]) => `${key} ${type}`);
2368
+ return `STRUCT (${fields.join(", ")})`;
2369
+ },
2370
+ toDriver(value) {
2371
+ return buildStructLiteral(value, schema);
2372
+ },
2373
+ fromDriver(value) {
2374
+ if (typeof value === "string") {
2375
+ try {
2376
+ return JSON.parse(value);
2377
+ } catch {
2378
+ return value;
2379
+ }
2380
+ }
2381
+ return value;
2382
+ }
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);
2412
+ var duckDbBlob = customType({
2413
+ dataType() {
2414
+ return "BLOB";
2415
+ },
2416
+ toDriver(value) {
2417
+ return wrapBlob(value);
2418
+ }
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})`;
420
2530
  }
421
2531
  // src/migrator.ts
422
2532
  import { readMigrationFiles } from "drizzle-orm/migrator";
423
2533
  async function migrate(db, config) {
424
- const migrations = readMigrationFiles(config);
425
- 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})`;
426
3224
  }
427
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,
428
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,
3262
+ duckDbStruct,
3263
+ duckDbMap,
3264
+ duckDbList,
3265
+ duckDbJson,
3266
+ duckDbInterval,
3267
+ duckDbInet,
3268
+ duckDbDate,
3269
+ duckDbBlob,
3270
+ duckDbArrayOverlaps,
3271
+ duckDbArrayContains,
3272
+ duckDbArrayContained,
3273
+ duckDbArray,
429
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,
430
3291
  DuckDBTransaction,
431
3292
  DuckDBSession,
432
- DuckDBSelectBuilder,
433
3293
  DuckDBPreparedQuery,
434
3294
  DuckDBDriver,
435
- DuckDBDatabase
3295
+ DuckDBDatabase,
3296
+ DUCKDB_VALUE_MARKER,
3297
+ DEFAULT_IMPORT_BASE
436
3298
  };