@leonardovida-md/drizzle-neo-duckdb 1.1.4 → 1.2.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.
@@ -4,7 +4,7 @@
4
4
  import { DuckDBInstance as DuckDBInstance3 } from "@duckdb/node-api";
5
5
  import { mkdir, writeFile } from "node:fs/promises";
6
6
  import path from "node:path";
7
- import process from "node:process";
7
+ import process2 from "node:process";
8
8
 
9
9
  // src/driver.ts
10
10
  import { DuckDBInstance as DuckDBInstance2 } from "@duckdb/node-api";
@@ -23,765 +23,6 @@ import { PgTransaction } from "drizzle-orm/pg-core";
23
23
  import { PgPreparedQuery, PgSession } from "drizzle-orm/pg-core/session";
24
24
  import { fillPlaceholders, sql } from "drizzle-orm/sql/sql";
25
25
 
26
- // src/sql/query-rewriters.ts
27
- var OPERATORS = [
28
- { token: "@>", fn: "array_has_all" },
29
- { token: "<@", fn: "array_has_all", swap: true },
30
- { token: "&&", fn: "array_has_any" }
31
- ];
32
- var isWhitespace = (char) => char !== undefined && /\s/.test(char);
33
- function scrubForRewrite(query) {
34
- let scrubbed = "";
35
- let state = "code";
36
- for (let i = 0;i < query.length; i += 1) {
37
- const char = query[i];
38
- const next = query[i + 1];
39
- if (state === "code") {
40
- if (char === "'") {
41
- scrubbed += "'";
42
- state = "single";
43
- continue;
44
- }
45
- if (char === '"') {
46
- scrubbed += '"';
47
- state = "double";
48
- continue;
49
- }
50
- if (char === "-" && next === "-") {
51
- scrubbed += " ";
52
- i += 1;
53
- state = "lineComment";
54
- continue;
55
- }
56
- if (char === "/" && next === "*") {
57
- scrubbed += " ";
58
- i += 1;
59
- state = "blockComment";
60
- continue;
61
- }
62
- scrubbed += char;
63
- continue;
64
- }
65
- if (state === "single") {
66
- if (char === "'" && next === "'") {
67
- scrubbed += "''";
68
- i += 1;
69
- continue;
70
- }
71
- scrubbed += char === "'" ? "'" : ".";
72
- if (char === "'") {
73
- state = "code";
74
- }
75
- continue;
76
- }
77
- if (state === "double") {
78
- if (char === '"' && next === '"') {
79
- scrubbed += '""';
80
- i += 1;
81
- continue;
82
- }
83
- scrubbed += char === '"' ? '"' : ".";
84
- if (char === '"') {
85
- state = "code";
86
- }
87
- continue;
88
- }
89
- if (state === "lineComment") {
90
- scrubbed += char === `
91
- ` ? `
92
- ` : " ";
93
- if (char === `
94
- `) {
95
- state = "code";
96
- }
97
- continue;
98
- }
99
- if (state === "blockComment") {
100
- if (char === "*" && next === "/") {
101
- scrubbed += " ";
102
- i += 1;
103
- state = "code";
104
- } else {
105
- scrubbed += " ";
106
- }
107
- }
108
- }
109
- return scrubbed;
110
- }
111
- function findNextOperator(scrubbed, start) {
112
- for (let idx = start;idx < scrubbed.length; idx += 1) {
113
- for (const operator of OPERATORS) {
114
- if (scrubbed.startsWith(operator.token, idx)) {
115
- return { index: idx, operator };
116
- }
117
- }
118
- }
119
- return null;
120
- }
121
- function walkLeft(source, scrubbed, start) {
122
- let idx = start;
123
- while (idx >= 0 && isWhitespace(scrubbed[idx])) {
124
- idx -= 1;
125
- }
126
- let depth = 0;
127
- for (;idx >= 0; idx -= 1) {
128
- const ch = scrubbed[idx];
129
- if (ch === ")" || ch === "]") {
130
- depth += 1;
131
- } else if (ch === "(" || ch === "[") {
132
- if (depth === 0) {
133
- return [idx + 1, source.slice(idx + 1, start + 1)];
134
- }
135
- depth = Math.max(0, depth - 1);
136
- } else if (depth === 0 && isWhitespace(ch)) {
137
- return [idx + 1, source.slice(idx + 1, start + 1)];
138
- }
139
- }
140
- return [0, source.slice(0, start + 1)];
141
- }
142
- function walkRight(source, scrubbed, start) {
143
- let idx = start;
144
- while (idx < scrubbed.length && isWhitespace(scrubbed[idx])) {
145
- idx += 1;
146
- }
147
- let depth = 0;
148
- for (;idx < scrubbed.length; idx += 1) {
149
- const ch = scrubbed[idx];
150
- if (ch === "(" || ch === "[") {
151
- depth += 1;
152
- } else if (ch === ")" || ch === "]") {
153
- if (depth === 0) {
154
- return [idx, source.slice(start, idx)];
155
- }
156
- depth = Math.max(0, depth - 1);
157
- } else if (depth === 0 && isWhitespace(ch)) {
158
- return [idx, source.slice(start, idx)];
159
- }
160
- }
161
- return [scrubbed.length, source.slice(start)];
162
- }
163
- function adaptArrayOperators(query) {
164
- if (query.indexOf("@>") === -1 && query.indexOf("<@") === -1 && query.indexOf("&&") === -1) {
165
- return query;
166
- }
167
- let rewritten = query;
168
- let scrubbed = scrubForRewrite(query);
169
- let searchStart = 0;
170
- while (true) {
171
- const next = findNextOperator(scrubbed, searchStart);
172
- if (!next)
173
- break;
174
- const { index, operator } = next;
175
- const [leftStart, leftExpr] = walkLeft(rewritten, scrubbed, index - 1);
176
- const [rightEnd, rightExpr] = walkRight(rewritten, scrubbed, index + operator.token.length);
177
- const left = leftExpr.trim();
178
- const right = rightExpr.trim();
179
- const replacement = `${operator.fn}(${operator.swap ? right : left}, ${operator.swap ? left : right})`;
180
- rewritten = rewritten.slice(0, leftStart) + replacement + rewritten.slice(rightEnd);
181
- scrubbed = scrubForRewrite(rewritten);
182
- searchStart = leftStart + replacement.length;
183
- }
184
- return rewritten;
185
- }
186
- function extractQuotedIdentifier(original, start) {
187
- if (original[start] !== '"') {
188
- return null;
189
- }
190
- let pos = start + 1;
191
- while (pos < original.length) {
192
- if (original[pos] === '"') {
193
- if (original[pos + 1] === '"') {
194
- pos += 2;
195
- continue;
196
- }
197
- break;
198
- }
199
- pos++;
200
- }
201
- if (pos >= original.length) {
202
- return null;
203
- }
204
- return {
205
- name: original.slice(start + 1, pos),
206
- end: pos + 1
207
- };
208
- }
209
- function findMainFromClause(scrubbed) {
210
- const lowerScrubbed = scrubbed.toLowerCase();
211
- let searchStart = 0;
212
- const withMatch = /\bwith\s+/i.exec(lowerScrubbed);
213
- if (withMatch) {
214
- let depth = 0;
215
- let pos = withMatch.index + withMatch[0].length;
216
- while (pos < scrubbed.length) {
217
- const char = scrubbed[pos];
218
- if (char === "(") {
219
- depth++;
220
- } else if (char === ")") {
221
- depth--;
222
- } else if (depth === 0) {
223
- const remaining = lowerScrubbed.slice(pos);
224
- if (/^\s*select\s+/i.test(remaining)) {
225
- searchStart = pos;
226
- break;
227
- }
228
- }
229
- pos++;
230
- }
231
- }
232
- const fromPattern = /\bfrom\s+/gi;
233
- fromPattern.lastIndex = searchStart;
234
- const fromMatch = fromPattern.exec(lowerScrubbed);
235
- return fromMatch ? fromMatch.index + fromMatch[0].length : -1;
236
- }
237
- function parseTableSources(original, scrubbed) {
238
- const sources = [];
239
- const lowerScrubbed = scrubbed.toLowerCase();
240
- const fromPos = findMainFromClause(scrubbed);
241
- if (fromPos < 0) {
242
- return sources;
243
- }
244
- const fromTable = parseTableRef(original, scrubbed, fromPos);
245
- if (fromTable) {
246
- sources.push(fromTable);
247
- }
248
- const joinPattern = /\b(left\s+|right\s+|inner\s+|full\s+|cross\s+)?join\s+/gi;
249
- joinPattern.lastIndex = fromPos;
250
- let joinMatch;
251
- while ((joinMatch = joinPattern.exec(lowerScrubbed)) !== null) {
252
- const tableStart = joinMatch.index + joinMatch[0].length;
253
- const joinTable = parseTableRef(original, scrubbed, tableStart);
254
- if (joinTable) {
255
- sources.push(joinTable);
256
- }
257
- }
258
- return sources;
259
- }
260
- function parseTableRef(original, scrubbed, start) {
261
- let pos = start;
262
- while (pos < scrubbed.length && isWhitespace(scrubbed[pos])) {
263
- pos++;
264
- }
265
- if (scrubbed[pos] === "(") {
266
- const nameStart2 = pos;
267
- let depth = 1;
268
- pos++;
269
- while (pos < scrubbed.length && depth > 0) {
270
- if (scrubbed[pos] === "(")
271
- depth++;
272
- else if (scrubbed[pos] === ")")
273
- depth--;
274
- pos++;
275
- }
276
- while (pos < scrubbed.length && isWhitespace(scrubbed[pos])) {
277
- pos++;
278
- }
279
- const afterSubquery = scrubbed.slice(pos).toLowerCase();
280
- if (afterSubquery.startsWith("as ")) {
281
- pos += 3;
282
- while (pos < scrubbed.length && isWhitespace(scrubbed[pos])) {
283
- pos++;
284
- }
285
- }
286
- if (original[pos] === '"') {
287
- const aliasIdent = extractQuotedIdentifier(original, pos);
288
- if (aliasIdent) {
289
- return {
290
- name: aliasIdent.name,
291
- alias: aliasIdent.name,
292
- position: nameStart2
293
- };
294
- }
295
- }
296
- return null;
297
- }
298
- if (original[pos] !== '"') {
299
- return null;
300
- }
301
- const nameStart = pos;
302
- const firstIdent = extractQuotedIdentifier(original, pos);
303
- if (!firstIdent) {
304
- return null;
305
- }
306
- let name = firstIdent.name;
307
- pos = firstIdent.end;
308
- let afterName = pos;
309
- while (afterName < scrubbed.length && isWhitespace(scrubbed[afterName])) {
310
- afterName++;
311
- }
312
- if (scrubbed[afterName] === ".") {
313
- afterName++;
314
- while (afterName < scrubbed.length && isWhitespace(scrubbed[afterName])) {
315
- afterName++;
316
- }
317
- if (original[afterName] === '"') {
318
- const tableIdent = extractQuotedIdentifier(original, afterName);
319
- if (tableIdent) {
320
- name = tableIdent.name;
321
- pos = tableIdent.end;
322
- }
323
- }
324
- }
325
- let alias;
326
- let aliasPos = pos;
327
- while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
328
- aliasPos++;
329
- }
330
- const afterTable = scrubbed.slice(aliasPos).toLowerCase();
331
- if (afterTable.startsWith("as ")) {
332
- aliasPos += 3;
333
- while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
334
- aliasPos++;
335
- }
336
- }
337
- const afterAlias = scrubbed.slice(aliasPos).toLowerCase();
338
- if (original[aliasPos] === '"' && !afterAlias.startsWith("on ") && !afterAlias.startsWith("left ") && !afterAlias.startsWith("right ") && !afterAlias.startsWith("inner ") && !afterAlias.startsWith("full ") && !afterAlias.startsWith("cross ") && !afterAlias.startsWith("join ") && !afterAlias.startsWith("where ") && !afterAlias.startsWith("group ") && !afterAlias.startsWith("order ") && !afterAlias.startsWith("limit ")) {
339
- const aliasIdent = extractQuotedIdentifier(original, aliasPos);
340
- if (aliasIdent) {
341
- alias = aliasIdent.name;
342
- }
343
- }
344
- return {
345
- name,
346
- alias,
347
- position: nameStart
348
- };
349
- }
350
- function findJoinClauses(original, scrubbed, sources, fromPos) {
351
- const clauses = [];
352
- const lowerScrubbed = scrubbed.toLowerCase();
353
- const joinPattern = /\b(left\s+|right\s+|inner\s+|full\s+|cross\s+)?join\s+"[^"]*"(\s*\.\s*"[^"]*")?(\s+as)?(\s+"[^"]*")?\s+on\s+/gi;
354
- joinPattern.lastIndex = fromPos;
355
- let match;
356
- let sourceIndex = 1;
357
- while ((match = joinPattern.exec(lowerScrubbed)) !== null) {
358
- const joinType = (match[1] || "").trim().toLowerCase();
359
- const joinKeywordEnd = match.index + (match[1] || "").length + "join".length;
360
- let tableStart = joinKeywordEnd;
361
- while (tableStart < original.length && isWhitespace(original[tableStart])) {
362
- tableStart++;
363
- }
364
- const tableIdent = extractQuotedIdentifier(original, tableStart);
365
- if (!tableIdent)
366
- continue;
367
- let tableName = tableIdent.name;
368
- let afterTable = tableIdent.end;
369
- let checkPos = afterTable;
370
- while (checkPos < scrubbed.length && isWhitespace(scrubbed[checkPos])) {
371
- checkPos++;
372
- }
373
- if (scrubbed[checkPos] === ".") {
374
- checkPos++;
375
- while (checkPos < scrubbed.length && isWhitespace(scrubbed[checkPos])) {
376
- checkPos++;
377
- }
378
- const realTableIdent = extractQuotedIdentifier(original, checkPos);
379
- if (realTableIdent) {
380
- tableName = realTableIdent.name;
381
- afterTable = realTableIdent.end;
382
- }
383
- }
384
- let tableAlias;
385
- let aliasPos = afterTable;
386
- while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
387
- aliasPos++;
388
- }
389
- const afterTableStr = scrubbed.slice(aliasPos).toLowerCase();
390
- if (afterTableStr.startsWith("as ")) {
391
- aliasPos += 3;
392
- while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
393
- aliasPos++;
394
- }
395
- }
396
- if (original[aliasPos] === '"' && !afterTableStr.startsWith("on ")) {
397
- const aliasIdent = extractQuotedIdentifier(original, aliasPos);
398
- if (aliasIdent) {
399
- tableAlias = aliasIdent.name;
400
- }
401
- }
402
- const onStart = match.index + match[0].length;
403
- const endPattern = /\b(left\s+join|right\s+join|inner\s+join|full\s+join|cross\s+join|join|where|group\s+by|order\s+by|limit|$)/i;
404
- const remaining = lowerScrubbed.slice(onStart);
405
- const endMatch = endPattern.exec(remaining);
406
- const onEnd = endMatch ? onStart + endMatch.index : scrubbed.length;
407
- let leftSource = "";
408
- if (sourceIndex > 0 && sourceIndex <= sources.length) {
409
- const prev = sources[sourceIndex - 1];
410
- leftSource = prev?.alias || prev?.name || "";
411
- }
412
- const rightSource = tableAlias || tableName;
413
- clauses.push({
414
- joinType,
415
- tableName,
416
- tableAlias,
417
- onStart,
418
- onEnd,
419
- leftSource,
420
- rightSource
421
- });
422
- sourceIndex++;
423
- }
424
- return clauses;
425
- }
426
- function findMainSelectClause(scrubbed) {
427
- const lowerScrubbed = scrubbed.toLowerCase();
428
- let searchStart = 0;
429
- const withMatch = /\bwith\s+/i.exec(lowerScrubbed);
430
- if (withMatch) {
431
- let depth2 = 0;
432
- let pos2 = withMatch.index + withMatch[0].length;
433
- while (pos2 < scrubbed.length) {
434
- const char = scrubbed[pos2];
435
- if (char === "(") {
436
- depth2++;
437
- } else if (char === ")") {
438
- depth2--;
439
- } else if (depth2 === 0) {
440
- const remaining = lowerScrubbed.slice(pos2);
441
- if (/^\s*select\s+/i.test(remaining)) {
442
- searchStart = pos2;
443
- break;
444
- }
445
- }
446
- pos2++;
447
- }
448
- }
449
- const selectPattern = /\bselect\s+/gi;
450
- selectPattern.lastIndex = searchStart;
451
- const selectMatch = selectPattern.exec(lowerScrubbed);
452
- if (!selectMatch)
453
- return null;
454
- const selectStart = selectMatch.index + selectMatch[0].length;
455
- let depth = 0;
456
- let pos = selectStart;
457
- while (pos < scrubbed.length) {
458
- const char = scrubbed[pos];
459
- if (char === "(") {
460
- depth++;
461
- } else if (char === ")") {
462
- depth--;
463
- } else if (depth === 0) {
464
- const remaining = lowerScrubbed.slice(pos);
465
- if (/^\s*from\s+/i.test(remaining)) {
466
- return { start: selectStart, end: pos };
467
- }
468
- }
469
- pos++;
470
- }
471
- return null;
472
- }
473
- function findWhereClause(scrubbed, fromPos) {
474
- const lowerScrubbed = scrubbed.toLowerCase();
475
- let depth = 0;
476
- let pos = fromPos;
477
- let whereStart = -1;
478
- while (pos < scrubbed.length) {
479
- const char = scrubbed[pos];
480
- if (char === "(") {
481
- depth++;
482
- } else if (char === ")") {
483
- depth--;
484
- } else if (depth === 0) {
485
- const remaining = lowerScrubbed.slice(pos);
486
- if (whereStart === -1 && /^\s*where\s+/i.test(remaining)) {
487
- const match = /^\s*where\s+/i.exec(remaining);
488
- if (match) {
489
- whereStart = pos + match[0].length;
490
- }
491
- } else if (whereStart !== -1 && /^\s*(group\s+by|order\s+by|limit|having|union|intersect|except|$)/i.test(remaining)) {
492
- return { start: whereStart, end: pos };
493
- }
494
- }
495
- pos++;
496
- }
497
- if (whereStart !== -1) {
498
- return { start: whereStart, end: scrubbed.length };
499
- }
500
- return null;
501
- }
502
- function findOrderByClause(scrubbed, fromPos) {
503
- const lowerScrubbed = scrubbed.toLowerCase();
504
- let depth = 0;
505
- let pos = fromPos;
506
- let orderStart = -1;
507
- while (pos < scrubbed.length) {
508
- const char = scrubbed[pos];
509
- if (char === "(") {
510
- depth++;
511
- } else if (char === ")") {
512
- depth--;
513
- } else if (depth === 0) {
514
- const remaining = lowerScrubbed.slice(pos);
515
- if (orderStart === -1 && /^\s*order\s+by\s+/i.test(remaining)) {
516
- const match = /^\s*order\s+by\s+/i.exec(remaining);
517
- if (match) {
518
- orderStart = pos + match[0].length;
519
- }
520
- } else if (orderStart !== -1 && /^\s*(limit|offset|fetch|for\s+update|$)/i.test(remaining)) {
521
- return { start: orderStart, end: pos };
522
- }
523
- }
524
- pos++;
525
- }
526
- if (orderStart !== -1) {
527
- return { start: orderStart, end: scrubbed.length };
528
- }
529
- return null;
530
- }
531
- function qualifyClauseColumnsSelective(original, scrubbed, clauseStart, clauseEnd, defaultSource, ambiguousColumns) {
532
- const clauseOriginal = original.slice(clauseStart, clauseEnd);
533
- const clauseScrubbed = scrubbed.slice(clauseStart, clauseEnd);
534
- let result = clauseOriginal;
535
- let offset = 0;
536
- let pos = 0;
537
- while (pos < clauseScrubbed.length) {
538
- const quotePos = clauseScrubbed.indexOf('"', pos);
539
- if (quotePos === -1)
540
- break;
541
- if (quotePos > 0 && clauseScrubbed[quotePos - 1] === ".") {
542
- const ident2 = extractQuotedIdentifier(clauseOriginal, quotePos);
543
- pos = ident2 ? ident2.end : quotePos + 1;
544
- continue;
545
- }
546
- const ident = extractQuotedIdentifier(clauseOriginal, quotePos);
547
- if (!ident) {
548
- pos = quotePos + 1;
549
- continue;
550
- }
551
- if (ident.end < clauseScrubbed.length && clauseScrubbed[ident.end] === ".") {
552
- pos = ident.end + 1;
553
- continue;
554
- }
555
- if (!ambiguousColumns.has(ident.name)) {
556
- pos = ident.end;
557
- continue;
558
- }
559
- let afterIdent = ident.end;
560
- while (afterIdent < clauseScrubbed.length && isWhitespace(clauseScrubbed[afterIdent])) {
561
- afterIdent++;
562
- }
563
- if (clauseScrubbed[afterIdent] === "(") {
564
- pos = ident.end;
565
- continue;
566
- }
567
- const beforeQuote = clauseScrubbed.slice(0, quotePos).toLowerCase();
568
- if (/\bas\s*$/i.test(beforeQuote)) {
569
- pos = ident.end;
570
- continue;
571
- }
572
- const qualified = `"${defaultSource}"."${ident.name}"`;
573
- const oldLength = ident.end - quotePos;
574
- result = result.slice(0, quotePos + offset) + qualified + result.slice(quotePos + oldLength + offset);
575
- offset += qualified.length - oldLength;
576
- pos = ident.end;
577
- }
578
- return { result, offset };
579
- }
580
- function qualifyJoinColumns(query) {
581
- const lowerQuery = query.toLowerCase();
582
- if (!lowerQuery.includes("join")) {
583
- return query;
584
- }
585
- const scrubbed = scrubForRewrite(query);
586
- const fromPos = findMainFromClause(scrubbed);
587
- if (fromPos < 0) {
588
- return query;
589
- }
590
- const sources = parseTableSources(query, scrubbed);
591
- if (sources.length < 2) {
592
- return query;
593
- }
594
- const joinClauses = findJoinClauses(query, scrubbed, sources, fromPos);
595
- if (joinClauses.length === 0) {
596
- return query;
597
- }
598
- const firstSource = sources[0];
599
- const defaultQualifier = firstSource.alias || firstSource.name;
600
- let result = query;
601
- let totalOffset = 0;
602
- for (const join of joinClauses) {
603
- const scrubbedOnClause = scrubbed.slice(join.onStart, join.onEnd);
604
- const originalOnClause = query.slice(join.onStart, join.onEnd);
605
- let clauseResult = originalOnClause;
606
- let clauseOffset = 0;
607
- let eqPos = -1;
608
- while ((eqPos = scrubbedOnClause.indexOf("=", eqPos + 1)) !== -1) {
609
- let lhsEnd = eqPos - 1;
610
- while (lhsEnd >= 0 && isWhitespace(scrubbedOnClause[lhsEnd])) {
611
- lhsEnd--;
612
- }
613
- if (scrubbedOnClause[lhsEnd] !== '"')
614
- continue;
615
- let lhsStartPos = lhsEnd - 1;
616
- while (lhsStartPos >= 0) {
617
- if (scrubbedOnClause[lhsStartPos] === '"') {
618
- if (lhsStartPos > 0 && scrubbedOnClause[lhsStartPos - 1] === '"') {
619
- lhsStartPos -= 2;
620
- continue;
621
- }
622
- break;
623
- }
624
- lhsStartPos--;
625
- }
626
- if (lhsStartPos < 0)
627
- continue;
628
- const lhsIsQualified = lhsStartPos > 0 && scrubbedOnClause[lhsStartPos - 1] === ".";
629
- let rhsStartPos = eqPos + 1;
630
- while (rhsStartPos < scrubbedOnClause.length && isWhitespace(scrubbedOnClause[rhsStartPos])) {
631
- rhsStartPos++;
632
- }
633
- const rhsChar = originalOnClause[rhsStartPos];
634
- const rhsIsParam = rhsChar === "$";
635
- const rhsIsStringLiteral = rhsChar === "'";
636
- const rhsIsColumn = rhsChar === '"';
637
- if (!rhsIsParam && !rhsIsStringLiteral && !rhsIsColumn)
638
- continue;
639
- const rhsIsQualified = !rhsIsColumn || rhsStartPos > 0 && scrubbedOnClause[rhsStartPos - 1] === ".";
640
- if (lhsIsQualified || rhsIsQualified)
641
- continue;
642
- const lhsIdent = extractQuotedIdentifier(originalOnClause, lhsStartPos);
643
- if (!lhsIdent)
644
- continue;
645
- let rhsIdent = null;
646
- let rhsValue = "";
647
- let rhsEnd = rhsStartPos;
648
- if (rhsIsParam) {
649
- let paramEnd = rhsStartPos + 1;
650
- while (paramEnd < originalOnClause.length && /\d/.test(originalOnClause[paramEnd])) {
651
- paramEnd++;
652
- }
653
- rhsValue = originalOnClause.slice(rhsStartPos, paramEnd);
654
- rhsEnd = paramEnd;
655
- } else if (rhsIsStringLiteral) {
656
- let literalEnd = rhsStartPos + 1;
657
- while (literalEnd < originalOnClause.length) {
658
- if (originalOnClause[literalEnd] === "'") {
659
- if (originalOnClause[literalEnd + 1] === "'") {
660
- literalEnd += 2;
661
- continue;
662
- }
663
- break;
664
- }
665
- literalEnd++;
666
- }
667
- rhsValue = originalOnClause.slice(rhsStartPos, literalEnd + 1);
668
- rhsEnd = literalEnd + 1;
669
- } else if (rhsIsColumn) {
670
- rhsIdent = extractQuotedIdentifier(originalOnClause, rhsStartPos);
671
- if (rhsIdent) {
672
- if (rhsIdent.end < scrubbedOnClause.length && scrubbedOnClause[rhsIdent.end] === ".") {
673
- continue;
674
- }
675
- rhsValue = `"${rhsIdent.name}"`;
676
- rhsEnd = rhsIdent.end;
677
- }
678
- }
679
- if (!rhsValue)
680
- continue;
681
- if (!rhsIsColumn || !rhsIdent || lhsIdent.name !== rhsIdent.name) {
682
- continue;
683
- }
684
- const lhsOriginal = `"${lhsIdent.name}"`;
685
- let newLhs = lhsOriginal;
686
- let newRhs = rhsValue;
687
- if (!lhsIsQualified && join.leftSource) {
688
- newLhs = `"${join.leftSource}"."${lhsIdent.name}"`;
689
- }
690
- if (!rhsIsQualified && rhsIsColumn && rhsIdent && join.rightSource) {
691
- newRhs = `"${join.rightSource}"."${rhsIdent.name}"`;
692
- }
693
- if (newLhs !== lhsOriginal || newRhs !== rhsValue) {
694
- const opStart = lhsIdent.end;
695
- let opEnd = opStart;
696
- while (opEnd < rhsEnd && (isWhitespace(originalOnClause[opEnd]) || originalOnClause[opEnd] === "=")) {
697
- opEnd++;
698
- }
699
- const operator = originalOnClause.slice(opStart, opEnd);
700
- const newExpr = `${newLhs}${operator}${newRhs}`;
701
- const oldExprLength = rhsEnd - lhsStartPos;
702
- clauseResult = clauseResult.slice(0, lhsStartPos + clauseOffset) + newExpr + clauseResult.slice(lhsStartPos + oldExprLength + clauseOffset);
703
- clauseOffset += newExpr.length - oldExprLength;
704
- }
705
- }
706
- if (clauseResult !== originalOnClause) {
707
- result = result.slice(0, join.onStart + totalOffset) + clauseResult + result.slice(join.onEnd + totalOffset);
708
- totalOffset += clauseResult.length - originalOnClause.length;
709
- }
710
- }
711
- const ambiguousColumns = new Set;
712
- for (const join of joinClauses) {
713
- const scrubbedOnClause = scrubbed.slice(join.onStart, join.onEnd);
714
- const originalOnClause = query.slice(join.onStart, join.onEnd);
715
- let eqPos = -1;
716
- while ((eqPos = scrubbedOnClause.indexOf("=", eqPos + 1)) !== -1) {
717
- let lhsEnd = eqPos - 1;
718
- while (lhsEnd >= 0 && isWhitespace(scrubbedOnClause[lhsEnd])) {
719
- lhsEnd--;
720
- }
721
- if (scrubbedOnClause[lhsEnd] !== '"')
722
- continue;
723
- let lhsStartPos = lhsEnd - 1;
724
- while (lhsStartPos >= 0) {
725
- if (scrubbedOnClause[lhsStartPos] === '"') {
726
- if (lhsStartPos > 0 && scrubbedOnClause[lhsStartPos - 1] === '"') {
727
- lhsStartPos -= 2;
728
- continue;
729
- }
730
- break;
731
- }
732
- lhsStartPos--;
733
- }
734
- if (lhsStartPos < 0)
735
- continue;
736
- let rhsStartPos = eqPos + 1;
737
- while (rhsStartPos < scrubbedOnClause.length && isWhitespace(scrubbedOnClause[rhsStartPos])) {
738
- rhsStartPos++;
739
- }
740
- if (originalOnClause[rhsStartPos] !== '"')
741
- continue;
742
- const lhsIdent = extractQuotedIdentifier(originalOnClause, lhsStartPos);
743
- const rhsIdent = extractQuotedIdentifier(originalOnClause, rhsStartPos);
744
- if (lhsIdent && rhsIdent && lhsIdent.name === rhsIdent.name) {
745
- ambiguousColumns.add(lhsIdent.name);
746
- }
747
- }
748
- }
749
- if (ambiguousColumns.size === 0) {
750
- return result;
751
- }
752
- const updatedScrubbed = scrubForRewrite(result);
753
- const selectClause = findMainSelectClause(updatedScrubbed);
754
- if (selectClause) {
755
- const { result: selectResult, offset: selectOffset } = qualifyClauseColumnsSelective(result, updatedScrubbed, selectClause.start, selectClause.end, defaultQualifier, ambiguousColumns);
756
- if (selectOffset !== 0) {
757
- result = result.slice(0, selectClause.start) + selectResult + result.slice(selectClause.end);
758
- }
759
- }
760
- const scrubbed2 = scrubForRewrite(result);
761
- const fromPos2 = findMainFromClause(scrubbed2);
762
- if (fromPos2 >= 0) {
763
- const whereClause = findWhereClause(scrubbed2, fromPos2);
764
- if (whereClause) {
765
- const { result: whereResult, offset: whereOffset } = qualifyClauseColumnsSelective(result, scrubbed2, whereClause.start, whereClause.end, defaultQualifier, ambiguousColumns);
766
- if (whereOffset !== 0) {
767
- result = result.slice(0, whereClause.start) + whereResult + result.slice(whereClause.end);
768
- }
769
- }
770
- }
771
- const scrubbed3 = scrubForRewrite(result);
772
- const fromPos3 = findMainFromClause(scrubbed3);
773
- if (fromPos3 >= 0) {
774
- const orderByClause = findOrderByClause(scrubbed3, fromPos3);
775
- if (orderByClause) {
776
- const { result: orderResult, offset: orderOffset } = qualifyClauseColumnsSelective(result, scrubbed3, orderByClause.start, orderByClause.end, defaultQualifier, ambiguousColumns);
777
- if (orderOffset !== 0) {
778
- result = result.slice(0, orderByClause.start) + orderResult + result.slice(orderByClause.end);
779
- }
780
- }
781
- }
782
- return result;
783
- }
784
-
785
26
  // src/sql/result-mapper.ts
786
27
  import {
787
28
  Column,
@@ -1358,24 +599,6 @@ function isSavepointSyntaxError(error) {
1358
599
  }
1359
600
  return error.message.toLowerCase().includes("savepoint") && error.message.toLowerCase().includes("syntax error");
1360
601
  }
1361
- function rewriteQuery(mode, query) {
1362
- if (mode === "never") {
1363
- return { sql: query, rewritten: false };
1364
- }
1365
- let result = query;
1366
- let wasRewritten = false;
1367
- const arrayRewritten = adaptArrayOperators(result);
1368
- if (arrayRewritten !== result) {
1369
- result = arrayRewritten;
1370
- wasRewritten = true;
1371
- }
1372
- const joinQualified = qualifyJoinColumns(result);
1373
- if (joinQualified !== result) {
1374
- result = joinQualified;
1375
- wasRewritten = true;
1376
- }
1377
- return { sql: result, rewritten: wasRewritten };
1378
- }
1379
602
 
1380
603
  class DuckDBPreparedQuery extends PgPreparedQuery {
1381
604
  client;
@@ -1386,12 +609,11 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
1386
609
  fields;
1387
610
  _isResponseInArrayMode;
1388
611
  customResultMapper;
1389
- rewriteArraysMode;
1390
612
  rejectStringArrayLiterals;
1391
613
  prepareCache;
1392
614
  warnOnStringArrayLiteral;
1393
615
  static [entityKind] = "DuckDBPreparedQuery";
1394
- constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rewriteArraysMode, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
616
+ constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
1395
617
  super({ sql: queryString, params });
1396
618
  this.client = client;
1397
619
  this.dialect = dialect;
@@ -1401,7 +623,6 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
1401
623
  this.fields = fields;
1402
624
  this._isResponseInArrayMode = _isResponseInArrayMode;
1403
625
  this.customResultMapper = customResultMapper;
1404
- this.rewriteArraysMode = rewriteArraysMode;
1405
626
  this.rejectStringArrayLiterals = rejectStringArrayLiterals;
1406
627
  this.prepareCache = prepareCache;
1407
628
  this.warnOnStringArrayLiteral = warnOnStringArrayLiteral;
@@ -1412,20 +633,16 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
1412
633
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
1413
634
  warnOnStringArrayLiteral: this.warnOnStringArrayLiteral ? () => this.warnOnStringArrayLiteral?.(this.queryString) : undefined
1414
635
  });
1415
- const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, this.queryString);
1416
- if (didRewrite) {
1417
- this.logger.logQuery(`[duckdb] original query before array rewrite: ${this.queryString}`, params);
1418
- }
1419
- this.logger.logQuery(rewrittenQuery, params);
636
+ this.logger.logQuery(this.queryString, params);
1420
637
  const { fields, joinsNotNullableMap, customResultMapper } = this;
1421
638
  if (fields) {
1422
- const { rows: rows2 } = await executeArraysOnClient(this.client, rewrittenQuery, params, { prepareCache: this.prepareCache });
639
+ const { rows: rows2 } = await executeArraysOnClient(this.client, this.queryString, params, { prepareCache: this.prepareCache });
1423
640
  if (rows2.length === 0) {
1424
641
  return [];
1425
642
  }
1426
643
  return customResultMapper ? customResultMapper(rows2) : rows2.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
1427
644
  }
1428
- const rows = await executeOnClient(this.client, rewrittenQuery, params, {
645
+ const rows = await executeOnClient(this.client, this.queryString, params, {
1429
646
  prepareCache: this.prepareCache
1430
647
  });
1431
648
  return rows;
@@ -1445,7 +662,6 @@ class DuckDBSession extends PgSession {
1445
662
  static [entityKind] = "DuckDBSession";
1446
663
  dialect;
1447
664
  logger;
1448
- rewriteArraysMode;
1449
665
  rejectStringArrayLiterals;
1450
666
  prepareCache;
1451
667
  hasWarnedArrayLiteral = false;
@@ -1457,17 +673,15 @@ class DuckDBSession extends PgSession {
1457
673
  this.options = options;
1458
674
  this.dialect = dialect;
1459
675
  this.logger = options.logger ?? new NoopLogger;
1460
- this.rewriteArraysMode = options.rewriteArrays ?? "auto";
1461
676
  this.rejectStringArrayLiterals = options.rejectStringArrayLiterals ?? false;
1462
677
  this.prepareCache = options.prepareCache;
1463
678
  this.options = {
1464
679
  ...options,
1465
- rewriteArrays: this.rewriteArraysMode,
1466
680
  prepareCache: this.prepareCache
1467
681
  };
1468
682
  }
1469
683
  prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
1470
- return new DuckDBPreparedQuery(this.client, this.dialect, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, this.rewriteArraysMode, this.rejectStringArrayLiterals, this.prepareCache, this.rejectStringArrayLiterals ? undefined : this.warnOnStringArrayLiteral);
684
+ return new DuckDBPreparedQuery(this.client, this.dialect, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, this.rejectStringArrayLiterals, this.prepareCache, this.rejectStringArrayLiterals ? undefined : this.warnOnStringArrayLiteral);
1471
685
  }
1472
686
  execute(query) {
1473
687
  this.dialect.resetPgJsonFlag();
@@ -1527,12 +741,8 @@ query: ${query}`, []);
1527
741
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
1528
742
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
1529
743
  });
1530
- const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
1531
- if (didRewrite) {
1532
- this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
1533
- }
1534
- this.logger.logQuery(rewrittenQuery, params);
1535
- return executeInBatches(this.client, rewrittenQuery, params, options);
744
+ this.logger.logQuery(builtQuery.sql, params);
745
+ return executeInBatches(this.client, builtQuery.sql, params, options);
1536
746
  }
1537
747
  executeBatchesRaw(query, options = {}) {
1538
748
  this.dialect.resetPgJsonFlag();
@@ -1542,12 +752,8 @@ query: ${query}`, []);
1542
752
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
1543
753
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
1544
754
  });
1545
- const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
1546
- if (didRewrite) {
1547
- this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
1548
- }
1549
- this.logger.logQuery(rewrittenQuery, params);
1550
- return executeInBatchesRaw(this.client, rewrittenQuery, params, options);
755
+ this.logger.logQuery(builtQuery.sql, params);
756
+ return executeInBatchesRaw(this.client, builtQuery.sql, params, options);
1551
757
  }
1552
758
  async executeArrow(query) {
1553
759
  this.dialect.resetPgJsonFlag();
@@ -1557,12 +763,8 @@ query: ${query}`, []);
1557
763
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
1558
764
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
1559
765
  });
1560
- const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
1561
- if (didRewrite) {
1562
- this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
1563
- }
1564
- this.logger.logQuery(rewrittenQuery, params);
1565
- return executeArrowOnClient(this.client, rewrittenQuery, params);
766
+ this.logger.logQuery(builtQuery.sql, params);
767
+ return executeArrowOnClient(this.client, builtQuery.sql, params);
1566
768
  }
1567
769
  markRollbackOnly() {
1568
770
  this.rollbackOnly = true;
@@ -1664,6 +866,524 @@ import {
1664
866
  import {
1665
867
  sql as sql2
1666
868
  } from "drizzle-orm";
869
+
870
+ // src/sql/ast-transformer.ts
871
+ import nodeSqlParser from "node-sql-parser";
872
+
873
+ // src/sql/visitors/array-operators.ts
874
+ var OPERATOR_MAP = {
875
+ "@>": { fn: "array_has_all" },
876
+ "<@": { fn: "array_has_all", swap: true },
877
+ "&&": { fn: "array_has_any" }
878
+ };
879
+ function walkExpression(expr, parent, key) {
880
+ if (!expr || typeof expr !== "object")
881
+ return false;
882
+ let transformed = false;
883
+ const exprObj = expr;
884
+ if ("type" in expr && exprObj.type === "binary_expr") {
885
+ const binary = expr;
886
+ const mapping = OPERATOR_MAP[binary.operator];
887
+ if (mapping) {
888
+ const fnExpr = {
889
+ type: "function",
890
+ name: { name: [{ type: "default", value: mapping.fn }] },
891
+ args: {
892
+ type: "expr_list",
893
+ value: mapping.swap ? [binary.right, binary.left] : [binary.left, binary.right]
894
+ }
895
+ };
896
+ if (parent && key) {
897
+ parent[key] = fnExpr;
898
+ }
899
+ transformed = true;
900
+ } else {
901
+ transformed = walkExpression(binary.left, binary, "left") || transformed;
902
+ transformed = walkExpression(binary.right, binary, "right") || transformed;
903
+ }
904
+ }
905
+ if ("type" in expr && exprObj.type === "unary_expr") {
906
+ if ("expr" in exprObj) {
907
+ transformed = walkExpression(exprObj.expr, exprObj, "expr") || transformed;
908
+ }
909
+ }
910
+ if ("type" in expr && exprObj.type === "case") {
911
+ if ("expr" in exprObj && exprObj.expr) {
912
+ transformed = walkExpression(exprObj.expr, exprObj, "expr") || transformed;
913
+ }
914
+ if ("args" in exprObj && Array.isArray(exprObj.args)) {
915
+ for (let i = 0;i < exprObj.args.length; i++) {
916
+ const whenClause = exprObj.args[i];
917
+ if (whenClause.cond) {
918
+ transformed = walkExpression(whenClause.cond, whenClause, "cond") || transformed;
919
+ }
920
+ if (whenClause.result) {
921
+ transformed = walkExpression(whenClause.result, whenClause, "result") || transformed;
922
+ }
923
+ }
924
+ }
925
+ }
926
+ if ("args" in expr && exprObj.args) {
927
+ const args = exprObj.args;
928
+ if ("value" in args && Array.isArray(args.value)) {
929
+ for (let i = 0;i < args.value.length; i++) {
930
+ transformed = walkExpression(args.value[i], args.value, String(i)) || transformed;
931
+ }
932
+ } else if ("expr" in args) {
933
+ transformed = walkExpression(args.expr, args, "expr") || transformed;
934
+ }
935
+ }
936
+ if ("ast" in exprObj && exprObj.ast) {
937
+ const subAst = exprObj.ast;
938
+ if (subAst.type === "select") {
939
+ transformed = walkSelectImpl(subAst) || transformed;
940
+ }
941
+ }
942
+ if ("type" in expr && exprObj.type === "expr_list") {
943
+ if ("value" in exprObj && Array.isArray(exprObj.value)) {
944
+ for (let i = 0;i < exprObj.value.length; i++) {
945
+ transformed = walkExpression(exprObj.value[i], exprObj.value, String(i)) || transformed;
946
+ }
947
+ }
948
+ }
949
+ return transformed;
950
+ }
951
+ function walkFrom(from) {
952
+ if (!from || !Array.isArray(from))
953
+ return false;
954
+ let transformed = false;
955
+ for (const f of from) {
956
+ if ("join" in f) {
957
+ const join = f;
958
+ transformed = walkExpression(join.on, join, "on") || transformed;
959
+ }
960
+ if ("expr" in f && f.expr && "ast" in f.expr) {
961
+ transformed = walkSelectImpl(f.expr.ast) || transformed;
962
+ }
963
+ }
964
+ return transformed;
965
+ }
966
+ function walkSelectImpl(select) {
967
+ let transformed = false;
968
+ if (select.with) {
969
+ for (const cte of select.with) {
970
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
971
+ if (cteSelect && cteSelect.type === "select") {
972
+ transformed = walkSelectImpl(cteSelect) || transformed;
973
+ }
974
+ }
975
+ }
976
+ if (Array.isArray(select.from)) {
977
+ transformed = walkFrom(select.from) || transformed;
978
+ }
979
+ transformed = walkExpression(select.where, select, "where") || transformed;
980
+ if (select.having) {
981
+ if (Array.isArray(select.having)) {
982
+ for (let i = 0;i < select.having.length; i++) {
983
+ transformed = walkExpression(select.having[i], select.having, String(i)) || transformed;
984
+ }
985
+ } else {
986
+ transformed = walkExpression(select.having, select, "having") || transformed;
987
+ }
988
+ }
989
+ if (Array.isArray(select.columns)) {
990
+ for (const col of select.columns) {
991
+ if ("expr" in col) {
992
+ transformed = walkExpression(col.expr, col, "expr") || transformed;
993
+ }
994
+ }
995
+ }
996
+ if (select._next) {
997
+ transformed = walkSelectImpl(select._next) || transformed;
998
+ }
999
+ return transformed;
1000
+ }
1001
+ function transformArrayOperators(ast) {
1002
+ const statements = Array.isArray(ast) ? ast : [ast];
1003
+ let transformed = false;
1004
+ for (const stmt of statements) {
1005
+ if (stmt.type === "select") {
1006
+ transformed = walkSelectImpl(stmt) || transformed;
1007
+ }
1008
+ }
1009
+ return transformed;
1010
+ }
1011
+
1012
+ // src/sql/visitors/column-qualifier.ts
1013
+ function getTableSource(from) {
1014
+ if ("table" in from && from.table) {
1015
+ return {
1016
+ name: from.table,
1017
+ alias: from.as ?? null,
1018
+ schema: "db" in from ? from.db ?? null : null
1019
+ };
1020
+ }
1021
+ if ("expr" in from && from.as) {
1022
+ return {
1023
+ name: from.as,
1024
+ alias: from.as,
1025
+ schema: null
1026
+ };
1027
+ }
1028
+ return null;
1029
+ }
1030
+ function getQualifier(source) {
1031
+ return {
1032
+ table: source.alias ?? source.name,
1033
+ schema: source.schema
1034
+ };
1035
+ }
1036
+ function isUnqualifiedColumnRef(expr) {
1037
+ return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && (!("table" in expr) || !expr.table);
1038
+ }
1039
+ function isQualifiedColumnRef(expr) {
1040
+ return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && "table" in expr && !!expr.table;
1041
+ }
1042
+ function getColumnName(col) {
1043
+ if (typeof col.column === "string") {
1044
+ return col.column;
1045
+ }
1046
+ if (col.column && "expr" in col.column && col.column.expr?.value) {
1047
+ return String(col.column.expr.value);
1048
+ }
1049
+ return null;
1050
+ }
1051
+ function applyQualifier(col, qualifier) {
1052
+ col.table = qualifier.table;
1053
+ if (!("schema" in col) || !col.schema) {
1054
+ col.schema = qualifier.schema;
1055
+ }
1056
+ }
1057
+ function unwrapColumnRef(expr) {
1058
+ if (!expr || typeof expr !== "object")
1059
+ return null;
1060
+ if ("type" in expr && expr.type === "column_ref") {
1061
+ return expr;
1062
+ }
1063
+ if ("expr" in expr && expr.expr) {
1064
+ return unwrapColumnRef(expr.expr);
1065
+ }
1066
+ if ("ast" in expr && expr.ast && typeof expr.ast === "object") {
1067
+ return null;
1068
+ }
1069
+ if ("args" in expr && expr.args) {
1070
+ const args = expr.args;
1071
+ if (args.expr) {
1072
+ return unwrapColumnRef(args.expr);
1073
+ }
1074
+ if (args.value && args.value.length === 1) {
1075
+ return unwrapColumnRef(args.value[0]);
1076
+ }
1077
+ }
1078
+ return null;
1079
+ }
1080
+ function isBinaryExpr(expr) {
1081
+ return !!expr && typeof expr === "object" && "type" in expr && expr.type === "binary_expr";
1082
+ }
1083
+ function walkOnClause(expr, leftQualifier, rightQualifier, ambiguousColumns) {
1084
+ if (!expr || typeof expr !== "object")
1085
+ return false;
1086
+ let transformed = false;
1087
+ if (isBinaryExpr(expr)) {
1088
+ const left = expr.left;
1089
+ const right = expr.right;
1090
+ const leftCol = unwrapColumnRef(left);
1091
+ const rightCol = unwrapColumnRef(right);
1092
+ const leftUnqualified = leftCol ? isUnqualifiedColumnRef(leftCol) : false;
1093
+ const rightUnqualified = rightCol ? isUnqualifiedColumnRef(rightCol) : false;
1094
+ const leftQualified = leftCol ? isQualifiedColumnRef(leftCol) : false;
1095
+ const rightQualified = rightCol ? isQualifiedColumnRef(rightCol) : false;
1096
+ const leftColName = leftCol ? getColumnName(leftCol) : null;
1097
+ const rightColName = rightCol ? getColumnName(rightCol) : null;
1098
+ if (expr.operator === "=" && leftColName && rightColName && leftColName === rightColName) {
1099
+ if (leftUnqualified && rightUnqualified) {
1100
+ applyQualifier(leftCol, leftQualifier);
1101
+ applyQualifier(rightCol, rightQualifier);
1102
+ ambiguousColumns.add(leftColName);
1103
+ transformed = true;
1104
+ } else if (leftQualified && rightUnqualified) {
1105
+ applyQualifier(rightCol, rightQualifier);
1106
+ ambiguousColumns.add(rightColName);
1107
+ transformed = true;
1108
+ } else if (leftUnqualified && rightQualified) {
1109
+ applyQualifier(leftCol, leftQualifier);
1110
+ ambiguousColumns.add(leftColName);
1111
+ transformed = true;
1112
+ }
1113
+ }
1114
+ transformed = walkOnClause(isBinaryExpr(expr.left) ? expr.left : expr.left, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1115
+ transformed = walkOnClause(isBinaryExpr(expr.right) ? expr.right : expr.right, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1116
+ }
1117
+ return transformed;
1118
+ }
1119
+ function qualifyAmbiguousInExpression(expr, defaultQualifier, ambiguousColumns) {
1120
+ if (!expr || typeof expr !== "object")
1121
+ return false;
1122
+ let transformed = false;
1123
+ if (isUnqualifiedColumnRef(expr)) {
1124
+ const colName = getColumnName(expr);
1125
+ if (colName && ambiguousColumns.has(colName)) {
1126
+ applyQualifier(expr, defaultQualifier);
1127
+ transformed = true;
1128
+ }
1129
+ return transformed;
1130
+ }
1131
+ if (isBinaryExpr(expr)) {
1132
+ const binary = expr;
1133
+ transformed = qualifyAmbiguousInExpression(binary.left, defaultQualifier, ambiguousColumns) || transformed;
1134
+ transformed = qualifyAmbiguousInExpression(binary.right, defaultQualifier, ambiguousColumns) || transformed;
1135
+ return transformed;
1136
+ }
1137
+ if ("args" in expr && expr.args) {
1138
+ const args = expr.args;
1139
+ if (args.value && Array.isArray(args.value)) {
1140
+ for (const arg of args.value) {
1141
+ transformed = qualifyAmbiguousInExpression(arg, defaultQualifier, ambiguousColumns) || transformed;
1142
+ }
1143
+ }
1144
+ if (args.expr) {
1145
+ transformed = qualifyAmbiguousInExpression(args.expr, defaultQualifier, ambiguousColumns) || transformed;
1146
+ }
1147
+ }
1148
+ if ("over" in expr && expr.over && typeof expr.over === "object") {
1149
+ const over = expr.over;
1150
+ if (Array.isArray(over.partition)) {
1151
+ for (const part of over.partition) {
1152
+ transformed = qualifyAmbiguousInExpression(part, defaultQualifier, ambiguousColumns) || transformed;
1153
+ }
1154
+ }
1155
+ if (Array.isArray(over.orderby)) {
1156
+ for (const order of over.orderby) {
1157
+ transformed = qualifyAmbiguousInExpression(order, defaultQualifier, ambiguousColumns) || transformed;
1158
+ }
1159
+ }
1160
+ }
1161
+ return transformed;
1162
+ }
1163
+ function hasUnqualifiedColumns(expr) {
1164
+ if (!expr || typeof expr !== "object")
1165
+ return false;
1166
+ if ("type" in expr && expr.type === "binary_expr") {
1167
+ const left = expr.left;
1168
+ const right = expr.right;
1169
+ const leftCol = unwrapColumnRef(left);
1170
+ const rightCol = unwrapColumnRef(right);
1171
+ if (isUnqualifiedColumnRef(left) || isUnqualifiedColumnRef(right) || leftCol && isUnqualifiedColumnRef(leftCol) || rightCol && isUnqualifiedColumnRef(rightCol)) {
1172
+ return true;
1173
+ }
1174
+ if (isBinaryExpr(expr.left) && hasUnqualifiedColumns(expr.left))
1175
+ return true;
1176
+ if (isBinaryExpr(expr.right) && hasUnqualifiedColumns(expr.right))
1177
+ return true;
1178
+ }
1179
+ if ("args" in expr && expr.args) {
1180
+ const args = expr.args;
1181
+ if (args.expr && isUnqualifiedColumnRef(args.expr))
1182
+ return true;
1183
+ if (args.value) {
1184
+ for (const arg of args.value) {
1185
+ if (isUnqualifiedColumnRef(arg))
1186
+ return true;
1187
+ }
1188
+ }
1189
+ }
1190
+ return false;
1191
+ }
1192
+ function walkSelect(select) {
1193
+ let transformed = false;
1194
+ const ambiguousColumns = new Set;
1195
+ if (Array.isArray(select.from) && select.from.length >= 2) {
1196
+ const firstSource = getTableSource(select.from[0]);
1197
+ const defaultQualifier = firstSource ? getQualifier(firstSource) : null;
1198
+ let prevSource = firstSource;
1199
+ let hasAnyUnqualified = false;
1200
+ for (const from of select.from) {
1201
+ if ("join" in from) {
1202
+ const join = from;
1203
+ if (join.on && hasUnqualifiedColumns(join.on)) {
1204
+ hasAnyUnqualified = true;
1205
+ break;
1206
+ }
1207
+ }
1208
+ }
1209
+ if (!hasAnyUnqualified) {
1210
+ for (const from of select.from) {
1211
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1212
+ transformed = walkSelect(from.expr.ast) || transformed;
1213
+ }
1214
+ }
1215
+ } else {
1216
+ for (const from of select.from) {
1217
+ if ("join" in from) {
1218
+ const join = from;
1219
+ const currentSource = getTableSource(join);
1220
+ if (join.on && prevSource && currentSource) {
1221
+ const leftQualifier = getQualifier(prevSource);
1222
+ const rightQualifier = getQualifier(currentSource);
1223
+ transformed = walkOnClause(join.on, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1224
+ }
1225
+ if (join.using && prevSource && currentSource) {
1226
+ for (const usingCol of join.using) {
1227
+ if (typeof usingCol === "string") {
1228
+ ambiguousColumns.add(usingCol);
1229
+ } else if ("value" in usingCol) {
1230
+ ambiguousColumns.add(String(usingCol.value));
1231
+ }
1232
+ }
1233
+ }
1234
+ prevSource = currentSource;
1235
+ } else {
1236
+ const source = getTableSource(from);
1237
+ if (source) {
1238
+ prevSource = source;
1239
+ }
1240
+ }
1241
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1242
+ transformed = walkSelect(from.expr.ast) || transformed;
1243
+ }
1244
+ }
1245
+ if (ambiguousColumns.size > 0 && defaultQualifier) {
1246
+ if (Array.isArray(select.columns)) {
1247
+ for (const col of select.columns) {
1248
+ if ("expr" in col) {
1249
+ transformed = qualifyAmbiguousInExpression(col.expr, defaultQualifier, ambiguousColumns) || transformed;
1250
+ }
1251
+ }
1252
+ }
1253
+ transformed = qualifyAmbiguousInExpression(select.where, defaultQualifier, ambiguousColumns) || transformed;
1254
+ if (Array.isArray(select.orderby)) {
1255
+ for (const order of select.orderby) {
1256
+ if (order.expr) {
1257
+ transformed = qualifyAmbiguousInExpression(order.expr, defaultQualifier, ambiguousColumns) || transformed;
1258
+ }
1259
+ }
1260
+ }
1261
+ }
1262
+ }
1263
+ }
1264
+ if (select.with) {
1265
+ for (const cte of select.with) {
1266
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
1267
+ if (cteSelect && cteSelect.type === "select") {
1268
+ transformed = walkSelect(cteSelect) || transformed;
1269
+ }
1270
+ }
1271
+ }
1272
+ if (select._next) {
1273
+ transformed = walkSelect(select._next) || transformed;
1274
+ }
1275
+ return transformed;
1276
+ }
1277
+ function qualifyJoinColumns(ast) {
1278
+ const statements = Array.isArray(ast) ? ast : [ast];
1279
+ let transformed = false;
1280
+ for (const stmt of statements) {
1281
+ if (stmt.type === "select") {
1282
+ transformed = walkSelect(stmt) || transformed;
1283
+ } else if (stmt.type === "insert") {
1284
+ const insert = stmt;
1285
+ if (insert.values && typeof insert.values === "object" && "type" in insert.values && insert.values.type === "select") {
1286
+ transformed = walkSelect(insert.values) || transformed;
1287
+ }
1288
+ } else if (stmt.type === "update") {
1289
+ const update = stmt;
1290
+ const mainSource = update.table?.[0] ? getTableSource(update.table[0]) : null;
1291
+ const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
1292
+ const fromSources = update.from ?? [];
1293
+ const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
1294
+ if (update.where && defaultQualifier && firstFrom) {
1295
+ const ambiguous = new Set;
1296
+ transformed = walkOnClause(update.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
1297
+ transformed = qualifyAmbiguousInExpression(update.where, defaultQualifier, ambiguous) || transformed;
1298
+ }
1299
+ if (Array.isArray(update.returning) && defaultQualifier) {
1300
+ for (const ret of update.returning) {
1301
+ transformed = qualifyAmbiguousInExpression(ret, defaultQualifier, new Set) || transformed;
1302
+ }
1303
+ }
1304
+ } else if (stmt.type === "delete") {
1305
+ const del = stmt;
1306
+ const mainSource = del.table?.[0] ? getTableSource(del.table[0]) : null;
1307
+ const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
1308
+ const fromSources = del.from ?? [];
1309
+ const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
1310
+ if (del.where && defaultQualifier && firstFrom) {
1311
+ const ambiguous = new Set;
1312
+ transformed = walkOnClause(del.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
1313
+ transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, ambiguous) || transformed;
1314
+ } else if (del.where && defaultQualifier) {
1315
+ transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, new Set) || transformed;
1316
+ }
1317
+ }
1318
+ }
1319
+ return transformed;
1320
+ }
1321
+
1322
+ // src/sql/ast-transformer.ts
1323
+ var { Parser } = nodeSqlParser;
1324
+ var parser = new Parser;
1325
+ var CACHE_SIZE = 500;
1326
+ var transformCache = new Map;
1327
+ function getCachedOrTransform(query, transform) {
1328
+ const cached = transformCache.get(query);
1329
+ if (cached) {
1330
+ transformCache.delete(query);
1331
+ transformCache.set(query, cached);
1332
+ return cached;
1333
+ }
1334
+ const result = transform();
1335
+ if (transformCache.size >= CACHE_SIZE) {
1336
+ const oldestKey = transformCache.keys().next().value;
1337
+ if (oldestKey) {
1338
+ transformCache.delete(oldestKey);
1339
+ }
1340
+ }
1341
+ transformCache.set(query, result);
1342
+ return result;
1343
+ }
1344
+ var DEBUG_ENV = "DRIZZLE_DUCKDB_DEBUG_AST";
1345
+ function hasJoin(query) {
1346
+ return /\bjoin\b/i.test(query);
1347
+ }
1348
+ function debugLog(message, payload) {
1349
+ if (process?.env?.[DEBUG_ENV]) {
1350
+ console.debug("[duckdb-ast]", message, payload ?? "");
1351
+ }
1352
+ }
1353
+ function transformSQL(query) {
1354
+ const needsArrayTransform = query.includes("@>") || query.includes("<@") || query.includes("&&");
1355
+ const needsJoinTransform = hasJoin(query) || /\bupdate\b/i.test(query) || /\bdelete\b/i.test(query);
1356
+ if (!needsArrayTransform && !needsJoinTransform) {
1357
+ return { sql: query, transformed: false };
1358
+ }
1359
+ return getCachedOrTransform(query, () => {
1360
+ try {
1361
+ const ast = parser.astify(query, { database: "PostgreSQL" });
1362
+ let transformed = false;
1363
+ if (needsArrayTransform) {
1364
+ transformed = transformArrayOperators(ast) || transformed;
1365
+ }
1366
+ if (needsJoinTransform) {
1367
+ transformed = qualifyJoinColumns(ast) || transformed;
1368
+ }
1369
+ if (!transformed) {
1370
+ debugLog("AST parsed but no transformation applied", {
1371
+ join: needsJoinTransform
1372
+ });
1373
+ return { sql: query, transformed: false };
1374
+ }
1375
+ const transformedSql = parser.sqlify(ast, { database: "PostgreSQL" });
1376
+ return { sql: transformedSql, transformed: true };
1377
+ } catch (err) {
1378
+ debugLog("AST transform failed; returning original SQL", {
1379
+ error: err.message
1380
+ });
1381
+ return { sql: query, transformed: false };
1382
+ }
1383
+ });
1384
+ }
1385
+
1386
+ // src/dialect.ts
1667
1387
  class DuckDBDialect extends PgDialect {
1668
1388
  static [entityKind2] = "DuckDBPgDialect";
1669
1389
  hasPgJsonColumn = false;
@@ -1740,6 +1460,14 @@ class DuckDBDialect extends PgDialect {
1740
1460
  return "none";
1741
1461
  }
1742
1462
  }
1463
+ sqlToQuery(sqlObj, invokeSource) {
1464
+ const result = super.sqlToQuery(sqlObj, invokeSource);
1465
+ const transformed = transformSQL(result.sql);
1466
+ return {
1467
+ ...result,
1468
+ sql: transformed.sql
1469
+ };
1470
+ }
1743
1471
  }
1744
1472
 
1745
1473
  // src/select-builder.ts
@@ -1750,10 +1478,10 @@ import {
1750
1478
  } from "drizzle-orm/pg-core/query-builders";
1751
1479
  import { Subquery, ViewBaseConfig } from "drizzle-orm";
1752
1480
  import { PgViewBase } from "drizzle-orm/pg-core/view-base";
1753
- import { SQL as SQL4 } from "drizzle-orm/sql/sql";
1481
+ import { SQL as SQL5 } from "drizzle-orm/sql/sql";
1754
1482
 
1755
1483
  // src/sql/selection.ts
1756
- import { Column as Column2, SQL as SQL3, getTableName as getTableName2, is as is3, sql as sql3 } from "drizzle-orm";
1484
+ import { Column as Column2, SQL as SQL4, getTableName as getTableName2, is as is3, sql as sql3 } from "drizzle-orm";
1757
1485
  function mapEntries(obj, prefix, fullJoin = false) {
1758
1486
  return Object.fromEntries(Object.entries(obj).filter(([key]) => key !== "enableRLS").map(([key, value]) => {
1759
1487
  const qualified = prefix ? `${prefix}.${key}` : key;
@@ -1763,16 +1491,16 @@ function mapEntries(obj, prefix, fullJoin = false) {
1763
1491
  sql3`${value}`.mapWith(value).as(`${getTableName2(value.table)}.${value.name}`)
1764
1492
  ];
1765
1493
  }
1766
- if (fullJoin && is3(value, SQL3)) {
1494
+ if (fullJoin && is3(value, SQL4)) {
1767
1495
  const col = value.getSQL().queryChunks.find((chunk) => is3(chunk, Column2));
1768
1496
  const tableName = col?.table && getTableName2(col?.table);
1769
1497
  return [key, value.as(tableName ? `${tableName}.${key}` : key)];
1770
1498
  }
1771
- if (is3(value, SQL3) || is3(value, Column2)) {
1772
- const aliased = is3(value, SQL3) ? value : sql3`${value}`.mapWith(value);
1499
+ if (is3(value, SQL4) || is3(value, Column2)) {
1500
+ const aliased = is3(value, SQL4) ? value : sql3`${value}`.mapWith(value);
1773
1501
  return [key, aliased.as(qualified)];
1774
1502
  }
1775
- if (is3(value, SQL3.Aliased)) {
1503
+ if (is3(value, SQL4.Aliased)) {
1776
1504
  return [key, value];
1777
1505
  }
1778
1506
  if (typeof value === "object" && value !== null) {
@@ -1820,7 +1548,7 @@ class DuckDBSelectBuilder extends PgSelectBuilder {
1820
1548
  ]));
1821
1549
  } else if (is4(src, PgViewBase)) {
1822
1550
  fields = src[ViewBaseConfig]?.selectedFields;
1823
- } else if (is4(src, SQL4)) {
1551
+ } else if (is4(src, SQL5)) {
1824
1552
  fields = {};
1825
1553
  } else {
1826
1554
  fields = aliasFields(getTableColumns(src), !isPartialSelect);
@@ -2011,16 +1739,6 @@ function createDuckDBConnectionPool(instance, options = {}) {
2011
1739
  }
2012
1740
 
2013
1741
  // src/options.ts
2014
- var DEFAULT_REWRITE_ARRAYS_MODE = "auto";
2015
- function resolveRewriteArraysOption(value) {
2016
- if (value === undefined)
2017
- return DEFAULT_REWRITE_ARRAYS_MODE;
2018
- if (value === true)
2019
- return "auto";
2020
- if (value === false)
2021
- return "never";
2022
- return value;
2023
- }
2024
1742
  var DEFAULT_PREPARED_CACHE_SIZE = 32;
2025
1743
  function resolvePrepareCacheOption(option) {
2026
1744
  if (!option)
@@ -2050,7 +1768,6 @@ class DuckDBDriver {
2050
1768
  createSession(schema) {
2051
1769
  return new DuckDBSession(this.client, this.dialect, schema, {
2052
1770
  logger: this.options.logger,
2053
- rewriteArrays: this.options.rewriteArrays ?? "auto",
2054
1771
  rejectStringArrayLiterals: this.options.rejectStringArrayLiterals,
2055
1772
  prepareCache: this.options.prepareCache
2056
1773
  });
@@ -2065,7 +1782,6 @@ function isConfigObject(data) {
2065
1782
  }
2066
1783
  function createFromClient(client, config = {}, instance) {
2067
1784
  const dialect = new DuckDBDialect;
2068
- const rewriteArraysMode = resolveRewriteArraysOption(config.rewriteArrays);
2069
1785
  const prepareCache = resolvePrepareCacheOption(config.prepareCache);
2070
1786
  const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
2071
1787
  let schema;
@@ -2079,7 +1795,6 @@ function createFromClient(client, config = {}, instance) {
2079
1795
  }
2080
1796
  const driver = new DuckDBDriver(client, dialect, {
2081
1797
  logger,
2082
- rewriteArrays: rewriteArraysMode,
2083
1798
  rejectStringArrayLiterals: config.rejectStringArrayLiterals,
2084
1799
  prepareCache
2085
1800
  });
@@ -2743,7 +2458,7 @@ function renderImports(imports, importBasePath) {
2743
2458
  // src/bin/duckdb-introspect.ts
2744
2459
  function parseArgs(argv) {
2745
2460
  const options = {
2746
- outFile: path.resolve(process.cwd(), "drizzle/schema.ts"),
2461
+ outFile: path.resolve(process2.cwd(), "drizzle/schema.ts"),
2747
2462
  outMeta: undefined,
2748
2463
  allDatabases: false,
2749
2464
  includeViews: false,
@@ -2768,12 +2483,12 @@ function parseArgs(argv) {
2768
2483
  break;
2769
2484
  case "--out":
2770
2485
  case "--outFile":
2771
- options.outFile = path.resolve(process.cwd(), argv[++i] ?? "drizzle/schema.ts");
2486
+ options.outFile = path.resolve(process2.cwd(), argv[++i] ?? "drizzle/schema.ts");
2772
2487
  break;
2773
2488
  case "--out-json":
2774
2489
  case "--outJson":
2775
2490
  case "--json":
2776
- options.outMeta = path.resolve(process.cwd(), argv[++i] ?? "drizzle/schema.meta.json");
2491
+ options.outMeta = path.resolve(process2.cwd(), argv[++i] ?? "drizzle/schema.meta.json");
2777
2492
  break;
2778
2493
  case "--include-views":
2779
2494
  case "--includeViews":
@@ -2788,7 +2503,7 @@ function parseArgs(argv) {
2788
2503
  case "--help":
2789
2504
  case "-h":
2790
2505
  printHelp();
2791
- process.exit(0);
2506
+ process2.exit(0);
2792
2507
  default:
2793
2508
  if (arg.startsWith("-")) {
2794
2509
  console.warn(`Unknown option ${arg}`);
@@ -2830,12 +2545,12 @@ Examples:
2830
2545
  `);
2831
2546
  }
2832
2547
  async function main() {
2833
- const options = parseArgs(process.argv.slice(2));
2548
+ const options = parseArgs(process2.argv.slice(2));
2834
2549
  if (!options.url) {
2835
2550
  printHelp();
2836
2551
  throw new Error("Missing required --url");
2837
2552
  }
2838
- const instanceOptions = options.url.startsWith("md:") && process.env.MOTHERDUCK_TOKEN ? { motherduck_token: process.env.MOTHERDUCK_TOKEN } : undefined;
2553
+ const instanceOptions = options.url.startsWith("md:") && process2.env.MOTHERDUCK_TOKEN ? { motherduck_token: process2.env.MOTHERDUCK_TOKEN } : undefined;
2839
2554
  const instance = await DuckDBInstance3.create(options.url, instanceOptions);
2840
2555
  const connection = await instance.connect();
2841
2556
  const db = drizzle(connection);
@@ -2871,5 +2586,5 @@ async function main() {
2871
2586
  }
2872
2587
  main().catch((err) => {
2873
2588
  console.error(err instanceof Error ? err.message : err);
2874
- process.exit(1);
2589
+ process2.exit(1);
2875
2590
  });