@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.
package/dist/index.mjs CHANGED
@@ -15,765 +15,6 @@ import { PgTransaction } from "drizzle-orm/pg-core";
15
15
  import { PgPreparedQuery, PgSession } from "drizzle-orm/pg-core/session";
16
16
  import { fillPlaceholders, sql } from "drizzle-orm/sql/sql";
17
17
 
18
- // src/sql/query-rewriters.ts
19
- var OPERATORS = [
20
- { token: "@>", fn: "array_has_all" },
21
- { token: "<@", fn: "array_has_all", swap: true },
22
- { token: "&&", fn: "array_has_any" }
23
- ];
24
- var isWhitespace = (char) => char !== undefined && /\s/.test(char);
25
- function scrubForRewrite(query) {
26
- let scrubbed = "";
27
- let state = "code";
28
- for (let i = 0;i < query.length; i += 1) {
29
- const char = query[i];
30
- const next = query[i + 1];
31
- if (state === "code") {
32
- if (char === "'") {
33
- scrubbed += "'";
34
- state = "single";
35
- continue;
36
- }
37
- if (char === '"') {
38
- scrubbed += '"';
39
- state = "double";
40
- continue;
41
- }
42
- if (char === "-" && next === "-") {
43
- scrubbed += " ";
44
- i += 1;
45
- state = "lineComment";
46
- continue;
47
- }
48
- if (char === "/" && next === "*") {
49
- scrubbed += " ";
50
- i += 1;
51
- state = "blockComment";
52
- continue;
53
- }
54
- scrubbed += char;
55
- continue;
56
- }
57
- if (state === "single") {
58
- if (char === "'" && next === "'") {
59
- scrubbed += "''";
60
- i += 1;
61
- continue;
62
- }
63
- scrubbed += char === "'" ? "'" : ".";
64
- if (char === "'") {
65
- state = "code";
66
- }
67
- continue;
68
- }
69
- if (state === "double") {
70
- if (char === '"' && next === '"') {
71
- scrubbed += '""';
72
- i += 1;
73
- continue;
74
- }
75
- scrubbed += char === '"' ? '"' : ".";
76
- if (char === '"') {
77
- state = "code";
78
- }
79
- continue;
80
- }
81
- if (state === "lineComment") {
82
- scrubbed += char === `
83
- ` ? `
84
- ` : " ";
85
- if (char === `
86
- `) {
87
- state = "code";
88
- }
89
- continue;
90
- }
91
- if (state === "blockComment") {
92
- if (char === "*" && next === "/") {
93
- scrubbed += " ";
94
- i += 1;
95
- state = "code";
96
- } else {
97
- scrubbed += " ";
98
- }
99
- }
100
- }
101
- return scrubbed;
102
- }
103
- function findNextOperator(scrubbed, start) {
104
- for (let idx = start;idx < scrubbed.length; idx += 1) {
105
- for (const operator of OPERATORS) {
106
- if (scrubbed.startsWith(operator.token, idx)) {
107
- return { index: idx, operator };
108
- }
109
- }
110
- }
111
- return null;
112
- }
113
- function walkLeft(source, scrubbed, start) {
114
- let idx = start;
115
- while (idx >= 0 && isWhitespace(scrubbed[idx])) {
116
- idx -= 1;
117
- }
118
- let depth = 0;
119
- for (;idx >= 0; idx -= 1) {
120
- const ch = scrubbed[idx];
121
- if (ch === ")" || ch === "]") {
122
- depth += 1;
123
- } else if (ch === "(" || ch === "[") {
124
- if (depth === 0) {
125
- return [idx + 1, source.slice(idx + 1, start + 1)];
126
- }
127
- depth = Math.max(0, depth - 1);
128
- } else if (depth === 0 && isWhitespace(ch)) {
129
- return [idx + 1, source.slice(idx + 1, start + 1)];
130
- }
131
- }
132
- return [0, source.slice(0, start + 1)];
133
- }
134
- function walkRight(source, scrubbed, start) {
135
- let idx = start;
136
- while (idx < scrubbed.length && isWhitespace(scrubbed[idx])) {
137
- idx += 1;
138
- }
139
- let depth = 0;
140
- for (;idx < scrubbed.length; idx += 1) {
141
- const ch = scrubbed[idx];
142
- if (ch === "(" || ch === "[") {
143
- depth += 1;
144
- } else if (ch === ")" || ch === "]") {
145
- if (depth === 0) {
146
- return [idx, source.slice(start, idx)];
147
- }
148
- depth = Math.max(0, depth - 1);
149
- } else if (depth === 0 && isWhitespace(ch)) {
150
- return [idx, source.slice(start, idx)];
151
- }
152
- }
153
- return [scrubbed.length, source.slice(start)];
154
- }
155
- function adaptArrayOperators(query) {
156
- if (query.indexOf("@>") === -1 && query.indexOf("<@") === -1 && query.indexOf("&&") === -1) {
157
- return query;
158
- }
159
- let rewritten = query;
160
- let scrubbed = scrubForRewrite(query);
161
- let searchStart = 0;
162
- while (true) {
163
- const next = findNextOperator(scrubbed, searchStart);
164
- if (!next)
165
- break;
166
- const { index, operator } = next;
167
- const [leftStart, leftExpr] = walkLeft(rewritten, scrubbed, index - 1);
168
- const [rightEnd, rightExpr] = walkRight(rewritten, scrubbed, index + operator.token.length);
169
- const left = leftExpr.trim();
170
- const right = rightExpr.trim();
171
- const replacement = `${operator.fn}(${operator.swap ? right : left}, ${operator.swap ? left : right})`;
172
- rewritten = rewritten.slice(0, leftStart) + replacement + rewritten.slice(rightEnd);
173
- scrubbed = scrubForRewrite(rewritten);
174
- searchStart = leftStart + replacement.length;
175
- }
176
- return rewritten;
177
- }
178
- function extractQuotedIdentifier(original, start) {
179
- if (original[start] !== '"') {
180
- return null;
181
- }
182
- let pos = start + 1;
183
- while (pos < original.length) {
184
- if (original[pos] === '"') {
185
- if (original[pos + 1] === '"') {
186
- pos += 2;
187
- continue;
188
- }
189
- break;
190
- }
191
- pos++;
192
- }
193
- if (pos >= original.length) {
194
- return null;
195
- }
196
- return {
197
- name: original.slice(start + 1, pos),
198
- end: pos + 1
199
- };
200
- }
201
- function findMainFromClause(scrubbed) {
202
- const lowerScrubbed = scrubbed.toLowerCase();
203
- let searchStart = 0;
204
- const withMatch = /\bwith\s+/i.exec(lowerScrubbed);
205
- if (withMatch) {
206
- let depth = 0;
207
- let pos = withMatch.index + withMatch[0].length;
208
- while (pos < scrubbed.length) {
209
- const char = scrubbed[pos];
210
- if (char === "(") {
211
- depth++;
212
- } else if (char === ")") {
213
- depth--;
214
- } else if (depth === 0) {
215
- const remaining = lowerScrubbed.slice(pos);
216
- if (/^\s*select\s+/i.test(remaining)) {
217
- searchStart = pos;
218
- break;
219
- }
220
- }
221
- pos++;
222
- }
223
- }
224
- const fromPattern = /\bfrom\s+/gi;
225
- fromPattern.lastIndex = searchStart;
226
- const fromMatch = fromPattern.exec(lowerScrubbed);
227
- return fromMatch ? fromMatch.index + fromMatch[0].length : -1;
228
- }
229
- function parseTableSources(original, scrubbed) {
230
- const sources = [];
231
- const lowerScrubbed = scrubbed.toLowerCase();
232
- const fromPos = findMainFromClause(scrubbed);
233
- if (fromPos < 0) {
234
- return sources;
235
- }
236
- const fromTable = parseTableRef(original, scrubbed, fromPos);
237
- if (fromTable) {
238
- sources.push(fromTable);
239
- }
240
- const joinPattern = /\b(left\s+|right\s+|inner\s+|full\s+|cross\s+)?join\s+/gi;
241
- joinPattern.lastIndex = fromPos;
242
- let joinMatch;
243
- while ((joinMatch = joinPattern.exec(lowerScrubbed)) !== null) {
244
- const tableStart = joinMatch.index + joinMatch[0].length;
245
- const joinTable = parseTableRef(original, scrubbed, tableStart);
246
- if (joinTable) {
247
- sources.push(joinTable);
248
- }
249
- }
250
- return sources;
251
- }
252
- function parseTableRef(original, scrubbed, start) {
253
- let pos = start;
254
- while (pos < scrubbed.length && isWhitespace(scrubbed[pos])) {
255
- pos++;
256
- }
257
- if (scrubbed[pos] === "(") {
258
- const nameStart2 = pos;
259
- let depth = 1;
260
- pos++;
261
- while (pos < scrubbed.length && depth > 0) {
262
- if (scrubbed[pos] === "(")
263
- depth++;
264
- else if (scrubbed[pos] === ")")
265
- depth--;
266
- pos++;
267
- }
268
- while (pos < scrubbed.length && isWhitespace(scrubbed[pos])) {
269
- pos++;
270
- }
271
- const afterSubquery = scrubbed.slice(pos).toLowerCase();
272
- if (afterSubquery.startsWith("as ")) {
273
- pos += 3;
274
- while (pos < scrubbed.length && isWhitespace(scrubbed[pos])) {
275
- pos++;
276
- }
277
- }
278
- if (original[pos] === '"') {
279
- const aliasIdent = extractQuotedIdentifier(original, pos);
280
- if (aliasIdent) {
281
- return {
282
- name: aliasIdent.name,
283
- alias: aliasIdent.name,
284
- position: nameStart2
285
- };
286
- }
287
- }
288
- return null;
289
- }
290
- if (original[pos] !== '"') {
291
- return null;
292
- }
293
- const nameStart = pos;
294
- const firstIdent = extractQuotedIdentifier(original, pos);
295
- if (!firstIdent) {
296
- return null;
297
- }
298
- let name = firstIdent.name;
299
- pos = firstIdent.end;
300
- let afterName = pos;
301
- while (afterName < scrubbed.length && isWhitespace(scrubbed[afterName])) {
302
- afterName++;
303
- }
304
- if (scrubbed[afterName] === ".") {
305
- afterName++;
306
- while (afterName < scrubbed.length && isWhitespace(scrubbed[afterName])) {
307
- afterName++;
308
- }
309
- if (original[afterName] === '"') {
310
- const tableIdent = extractQuotedIdentifier(original, afterName);
311
- if (tableIdent) {
312
- name = tableIdent.name;
313
- pos = tableIdent.end;
314
- }
315
- }
316
- }
317
- let alias;
318
- let aliasPos = pos;
319
- while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
320
- aliasPos++;
321
- }
322
- const afterTable = scrubbed.slice(aliasPos).toLowerCase();
323
- if (afterTable.startsWith("as ")) {
324
- aliasPos += 3;
325
- while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
326
- aliasPos++;
327
- }
328
- }
329
- const afterAlias = scrubbed.slice(aliasPos).toLowerCase();
330
- 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 ")) {
331
- const aliasIdent = extractQuotedIdentifier(original, aliasPos);
332
- if (aliasIdent) {
333
- alias = aliasIdent.name;
334
- }
335
- }
336
- return {
337
- name,
338
- alias,
339
- position: nameStart
340
- };
341
- }
342
- function findJoinClauses(original, scrubbed, sources, fromPos) {
343
- const clauses = [];
344
- const lowerScrubbed = scrubbed.toLowerCase();
345
- const joinPattern = /\b(left\s+|right\s+|inner\s+|full\s+|cross\s+)?join\s+"[^"]*"(\s*\.\s*"[^"]*")?(\s+as)?(\s+"[^"]*")?\s+on\s+/gi;
346
- joinPattern.lastIndex = fromPos;
347
- let match;
348
- let sourceIndex = 1;
349
- while ((match = joinPattern.exec(lowerScrubbed)) !== null) {
350
- const joinType = (match[1] || "").trim().toLowerCase();
351
- const joinKeywordEnd = match.index + (match[1] || "").length + "join".length;
352
- let tableStart = joinKeywordEnd;
353
- while (tableStart < original.length && isWhitespace(original[tableStart])) {
354
- tableStart++;
355
- }
356
- const tableIdent = extractQuotedIdentifier(original, tableStart);
357
- if (!tableIdent)
358
- continue;
359
- let tableName = tableIdent.name;
360
- let afterTable = tableIdent.end;
361
- let checkPos = afterTable;
362
- while (checkPos < scrubbed.length && isWhitespace(scrubbed[checkPos])) {
363
- checkPos++;
364
- }
365
- if (scrubbed[checkPos] === ".") {
366
- checkPos++;
367
- while (checkPos < scrubbed.length && isWhitespace(scrubbed[checkPos])) {
368
- checkPos++;
369
- }
370
- const realTableIdent = extractQuotedIdentifier(original, checkPos);
371
- if (realTableIdent) {
372
- tableName = realTableIdent.name;
373
- afterTable = realTableIdent.end;
374
- }
375
- }
376
- let tableAlias;
377
- let aliasPos = afterTable;
378
- while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
379
- aliasPos++;
380
- }
381
- const afterTableStr = scrubbed.slice(aliasPos).toLowerCase();
382
- if (afterTableStr.startsWith("as ")) {
383
- aliasPos += 3;
384
- while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
385
- aliasPos++;
386
- }
387
- }
388
- if (original[aliasPos] === '"' && !afterTableStr.startsWith("on ")) {
389
- const aliasIdent = extractQuotedIdentifier(original, aliasPos);
390
- if (aliasIdent) {
391
- tableAlias = aliasIdent.name;
392
- }
393
- }
394
- const onStart = match.index + match[0].length;
395
- 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;
396
- const remaining = lowerScrubbed.slice(onStart);
397
- const endMatch = endPattern.exec(remaining);
398
- const onEnd = endMatch ? onStart + endMatch.index : scrubbed.length;
399
- let leftSource = "";
400
- if (sourceIndex > 0 && sourceIndex <= sources.length) {
401
- const prev = sources[sourceIndex - 1];
402
- leftSource = prev?.alias || prev?.name || "";
403
- }
404
- const rightSource = tableAlias || tableName;
405
- clauses.push({
406
- joinType,
407
- tableName,
408
- tableAlias,
409
- onStart,
410
- onEnd,
411
- leftSource,
412
- rightSource
413
- });
414
- sourceIndex++;
415
- }
416
- return clauses;
417
- }
418
- function findMainSelectClause(scrubbed) {
419
- const lowerScrubbed = scrubbed.toLowerCase();
420
- let searchStart = 0;
421
- const withMatch = /\bwith\s+/i.exec(lowerScrubbed);
422
- if (withMatch) {
423
- let depth2 = 0;
424
- let pos2 = withMatch.index + withMatch[0].length;
425
- while (pos2 < scrubbed.length) {
426
- const char = scrubbed[pos2];
427
- if (char === "(") {
428
- depth2++;
429
- } else if (char === ")") {
430
- depth2--;
431
- } else if (depth2 === 0) {
432
- const remaining = lowerScrubbed.slice(pos2);
433
- if (/^\s*select\s+/i.test(remaining)) {
434
- searchStart = pos2;
435
- break;
436
- }
437
- }
438
- pos2++;
439
- }
440
- }
441
- const selectPattern = /\bselect\s+/gi;
442
- selectPattern.lastIndex = searchStart;
443
- const selectMatch = selectPattern.exec(lowerScrubbed);
444
- if (!selectMatch)
445
- return null;
446
- const selectStart = selectMatch.index + selectMatch[0].length;
447
- let depth = 0;
448
- let pos = selectStart;
449
- while (pos < scrubbed.length) {
450
- const char = scrubbed[pos];
451
- if (char === "(") {
452
- depth++;
453
- } else if (char === ")") {
454
- depth--;
455
- } else if (depth === 0) {
456
- const remaining = lowerScrubbed.slice(pos);
457
- if (/^\s*from\s+/i.test(remaining)) {
458
- return { start: selectStart, end: pos };
459
- }
460
- }
461
- pos++;
462
- }
463
- return null;
464
- }
465
- function findWhereClause(scrubbed, fromPos) {
466
- const lowerScrubbed = scrubbed.toLowerCase();
467
- let depth = 0;
468
- let pos = fromPos;
469
- let whereStart = -1;
470
- while (pos < scrubbed.length) {
471
- const char = scrubbed[pos];
472
- if (char === "(") {
473
- depth++;
474
- } else if (char === ")") {
475
- depth--;
476
- } else if (depth === 0) {
477
- const remaining = lowerScrubbed.slice(pos);
478
- if (whereStart === -1 && /^\s*where\s+/i.test(remaining)) {
479
- const match = /^\s*where\s+/i.exec(remaining);
480
- if (match) {
481
- whereStart = pos + match[0].length;
482
- }
483
- } else if (whereStart !== -1 && /^\s*(group\s+by|order\s+by|limit|having|union|intersect|except|$)/i.test(remaining)) {
484
- return { start: whereStart, end: pos };
485
- }
486
- }
487
- pos++;
488
- }
489
- if (whereStart !== -1) {
490
- return { start: whereStart, end: scrubbed.length };
491
- }
492
- return null;
493
- }
494
- function findOrderByClause(scrubbed, fromPos) {
495
- const lowerScrubbed = scrubbed.toLowerCase();
496
- let depth = 0;
497
- let pos = fromPos;
498
- let orderStart = -1;
499
- while (pos < scrubbed.length) {
500
- const char = scrubbed[pos];
501
- if (char === "(") {
502
- depth++;
503
- } else if (char === ")") {
504
- depth--;
505
- } else if (depth === 0) {
506
- const remaining = lowerScrubbed.slice(pos);
507
- if (orderStart === -1 && /^\s*order\s+by\s+/i.test(remaining)) {
508
- const match = /^\s*order\s+by\s+/i.exec(remaining);
509
- if (match) {
510
- orderStart = pos + match[0].length;
511
- }
512
- } else if (orderStart !== -1 && /^\s*(limit|offset|fetch|for\s+update|$)/i.test(remaining)) {
513
- return { start: orderStart, end: pos };
514
- }
515
- }
516
- pos++;
517
- }
518
- if (orderStart !== -1) {
519
- return { start: orderStart, end: scrubbed.length };
520
- }
521
- return null;
522
- }
523
- function qualifyClauseColumnsSelective(original, scrubbed, clauseStart, clauseEnd, defaultSource, ambiguousColumns) {
524
- const clauseOriginal = original.slice(clauseStart, clauseEnd);
525
- const clauseScrubbed = scrubbed.slice(clauseStart, clauseEnd);
526
- let result = clauseOriginal;
527
- let offset = 0;
528
- let pos = 0;
529
- while (pos < clauseScrubbed.length) {
530
- const quotePos = clauseScrubbed.indexOf('"', pos);
531
- if (quotePos === -1)
532
- break;
533
- if (quotePos > 0 && clauseScrubbed[quotePos - 1] === ".") {
534
- const ident2 = extractQuotedIdentifier(clauseOriginal, quotePos);
535
- pos = ident2 ? ident2.end : quotePos + 1;
536
- continue;
537
- }
538
- const ident = extractQuotedIdentifier(clauseOriginal, quotePos);
539
- if (!ident) {
540
- pos = quotePos + 1;
541
- continue;
542
- }
543
- if (ident.end < clauseScrubbed.length && clauseScrubbed[ident.end] === ".") {
544
- pos = ident.end + 1;
545
- continue;
546
- }
547
- if (!ambiguousColumns.has(ident.name)) {
548
- pos = ident.end;
549
- continue;
550
- }
551
- let afterIdent = ident.end;
552
- while (afterIdent < clauseScrubbed.length && isWhitespace(clauseScrubbed[afterIdent])) {
553
- afterIdent++;
554
- }
555
- if (clauseScrubbed[afterIdent] === "(") {
556
- pos = ident.end;
557
- continue;
558
- }
559
- const beforeQuote = clauseScrubbed.slice(0, quotePos).toLowerCase();
560
- if (/\bas\s*$/i.test(beforeQuote)) {
561
- pos = ident.end;
562
- continue;
563
- }
564
- const qualified = `"${defaultSource}"."${ident.name}"`;
565
- const oldLength = ident.end - quotePos;
566
- result = result.slice(0, quotePos + offset) + qualified + result.slice(quotePos + oldLength + offset);
567
- offset += qualified.length - oldLength;
568
- pos = ident.end;
569
- }
570
- return { result, offset };
571
- }
572
- function qualifyJoinColumns(query) {
573
- const lowerQuery = query.toLowerCase();
574
- if (!lowerQuery.includes("join")) {
575
- return query;
576
- }
577
- const scrubbed = scrubForRewrite(query);
578
- const fromPos = findMainFromClause(scrubbed);
579
- if (fromPos < 0) {
580
- return query;
581
- }
582
- const sources = parseTableSources(query, scrubbed);
583
- if (sources.length < 2) {
584
- return query;
585
- }
586
- const joinClauses = findJoinClauses(query, scrubbed, sources, fromPos);
587
- if (joinClauses.length === 0) {
588
- return query;
589
- }
590
- const firstSource = sources[0];
591
- const defaultQualifier = firstSource.alias || firstSource.name;
592
- let result = query;
593
- let totalOffset = 0;
594
- for (const join of joinClauses) {
595
- const scrubbedOnClause = scrubbed.slice(join.onStart, join.onEnd);
596
- const originalOnClause = query.slice(join.onStart, join.onEnd);
597
- let clauseResult = originalOnClause;
598
- let clauseOffset = 0;
599
- let eqPos = -1;
600
- while ((eqPos = scrubbedOnClause.indexOf("=", eqPos + 1)) !== -1) {
601
- let lhsEnd = eqPos - 1;
602
- while (lhsEnd >= 0 && isWhitespace(scrubbedOnClause[lhsEnd])) {
603
- lhsEnd--;
604
- }
605
- if (scrubbedOnClause[lhsEnd] !== '"')
606
- continue;
607
- let lhsStartPos = lhsEnd - 1;
608
- while (lhsStartPos >= 0) {
609
- if (scrubbedOnClause[lhsStartPos] === '"') {
610
- if (lhsStartPos > 0 && scrubbedOnClause[lhsStartPos - 1] === '"') {
611
- lhsStartPos -= 2;
612
- continue;
613
- }
614
- break;
615
- }
616
- lhsStartPos--;
617
- }
618
- if (lhsStartPos < 0)
619
- continue;
620
- const lhsIsQualified = lhsStartPos > 0 && scrubbedOnClause[lhsStartPos - 1] === ".";
621
- let rhsStartPos = eqPos + 1;
622
- while (rhsStartPos < scrubbedOnClause.length && isWhitespace(scrubbedOnClause[rhsStartPos])) {
623
- rhsStartPos++;
624
- }
625
- const rhsChar = originalOnClause[rhsStartPos];
626
- const rhsIsParam = rhsChar === "$";
627
- const rhsIsStringLiteral = rhsChar === "'";
628
- const rhsIsColumn = rhsChar === '"';
629
- if (!rhsIsParam && !rhsIsStringLiteral && !rhsIsColumn)
630
- continue;
631
- const rhsIsQualified = !rhsIsColumn || rhsStartPos > 0 && scrubbedOnClause[rhsStartPos - 1] === ".";
632
- if (lhsIsQualified || rhsIsQualified)
633
- continue;
634
- const lhsIdent = extractQuotedIdentifier(originalOnClause, lhsStartPos);
635
- if (!lhsIdent)
636
- continue;
637
- let rhsIdent = null;
638
- let rhsValue = "";
639
- let rhsEnd = rhsStartPos;
640
- if (rhsIsParam) {
641
- let paramEnd = rhsStartPos + 1;
642
- while (paramEnd < originalOnClause.length && /\d/.test(originalOnClause[paramEnd])) {
643
- paramEnd++;
644
- }
645
- rhsValue = originalOnClause.slice(rhsStartPos, paramEnd);
646
- rhsEnd = paramEnd;
647
- } else if (rhsIsStringLiteral) {
648
- let literalEnd = rhsStartPos + 1;
649
- while (literalEnd < originalOnClause.length) {
650
- if (originalOnClause[literalEnd] === "'") {
651
- if (originalOnClause[literalEnd + 1] === "'") {
652
- literalEnd += 2;
653
- continue;
654
- }
655
- break;
656
- }
657
- literalEnd++;
658
- }
659
- rhsValue = originalOnClause.slice(rhsStartPos, literalEnd + 1);
660
- rhsEnd = literalEnd + 1;
661
- } else if (rhsIsColumn) {
662
- rhsIdent = extractQuotedIdentifier(originalOnClause, rhsStartPos);
663
- if (rhsIdent) {
664
- if (rhsIdent.end < scrubbedOnClause.length && scrubbedOnClause[rhsIdent.end] === ".") {
665
- continue;
666
- }
667
- rhsValue = `"${rhsIdent.name}"`;
668
- rhsEnd = rhsIdent.end;
669
- }
670
- }
671
- if (!rhsValue)
672
- continue;
673
- if (!rhsIsColumn || !rhsIdent || lhsIdent.name !== rhsIdent.name) {
674
- continue;
675
- }
676
- const lhsOriginal = `"${lhsIdent.name}"`;
677
- let newLhs = lhsOriginal;
678
- let newRhs = rhsValue;
679
- if (!lhsIsQualified && join.leftSource) {
680
- newLhs = `"${join.leftSource}"."${lhsIdent.name}"`;
681
- }
682
- if (!rhsIsQualified && rhsIsColumn && rhsIdent && join.rightSource) {
683
- newRhs = `"${join.rightSource}"."${rhsIdent.name}"`;
684
- }
685
- if (newLhs !== lhsOriginal || newRhs !== rhsValue) {
686
- const opStart = lhsIdent.end;
687
- let opEnd = opStart;
688
- while (opEnd < rhsEnd && (isWhitespace(originalOnClause[opEnd]) || originalOnClause[opEnd] === "=")) {
689
- opEnd++;
690
- }
691
- const operator = originalOnClause.slice(opStart, opEnd);
692
- const newExpr = `${newLhs}${operator}${newRhs}`;
693
- const oldExprLength = rhsEnd - lhsStartPos;
694
- clauseResult = clauseResult.slice(0, lhsStartPos + clauseOffset) + newExpr + clauseResult.slice(lhsStartPos + oldExprLength + clauseOffset);
695
- clauseOffset += newExpr.length - oldExprLength;
696
- }
697
- }
698
- if (clauseResult !== originalOnClause) {
699
- result = result.slice(0, join.onStart + totalOffset) + clauseResult + result.slice(join.onEnd + totalOffset);
700
- totalOffset += clauseResult.length - originalOnClause.length;
701
- }
702
- }
703
- const ambiguousColumns = new Set;
704
- for (const join of joinClauses) {
705
- const scrubbedOnClause = scrubbed.slice(join.onStart, join.onEnd);
706
- const originalOnClause = query.slice(join.onStart, join.onEnd);
707
- let eqPos = -1;
708
- while ((eqPos = scrubbedOnClause.indexOf("=", eqPos + 1)) !== -1) {
709
- let lhsEnd = eqPos - 1;
710
- while (lhsEnd >= 0 && isWhitespace(scrubbedOnClause[lhsEnd])) {
711
- lhsEnd--;
712
- }
713
- if (scrubbedOnClause[lhsEnd] !== '"')
714
- continue;
715
- let lhsStartPos = lhsEnd - 1;
716
- while (lhsStartPos >= 0) {
717
- if (scrubbedOnClause[lhsStartPos] === '"') {
718
- if (lhsStartPos > 0 && scrubbedOnClause[lhsStartPos - 1] === '"') {
719
- lhsStartPos -= 2;
720
- continue;
721
- }
722
- break;
723
- }
724
- lhsStartPos--;
725
- }
726
- if (lhsStartPos < 0)
727
- continue;
728
- let rhsStartPos = eqPos + 1;
729
- while (rhsStartPos < scrubbedOnClause.length && isWhitespace(scrubbedOnClause[rhsStartPos])) {
730
- rhsStartPos++;
731
- }
732
- if (originalOnClause[rhsStartPos] !== '"')
733
- continue;
734
- const lhsIdent = extractQuotedIdentifier(originalOnClause, lhsStartPos);
735
- const rhsIdent = extractQuotedIdentifier(originalOnClause, rhsStartPos);
736
- if (lhsIdent && rhsIdent && lhsIdent.name === rhsIdent.name) {
737
- ambiguousColumns.add(lhsIdent.name);
738
- }
739
- }
740
- }
741
- if (ambiguousColumns.size === 0) {
742
- return result;
743
- }
744
- const updatedScrubbed = scrubForRewrite(result);
745
- const selectClause = findMainSelectClause(updatedScrubbed);
746
- if (selectClause) {
747
- const { result: selectResult, offset: selectOffset } = qualifyClauseColumnsSelective(result, updatedScrubbed, selectClause.start, selectClause.end, defaultQualifier, ambiguousColumns);
748
- if (selectOffset !== 0) {
749
- result = result.slice(0, selectClause.start) + selectResult + result.slice(selectClause.end);
750
- }
751
- }
752
- const scrubbed2 = scrubForRewrite(result);
753
- const fromPos2 = findMainFromClause(scrubbed2);
754
- if (fromPos2 >= 0) {
755
- const whereClause = findWhereClause(scrubbed2, fromPos2);
756
- if (whereClause) {
757
- const { result: whereResult, offset: whereOffset } = qualifyClauseColumnsSelective(result, scrubbed2, whereClause.start, whereClause.end, defaultQualifier, ambiguousColumns);
758
- if (whereOffset !== 0) {
759
- result = result.slice(0, whereClause.start) + whereResult + result.slice(whereClause.end);
760
- }
761
- }
762
- }
763
- const scrubbed3 = scrubForRewrite(result);
764
- const fromPos3 = findMainFromClause(scrubbed3);
765
- if (fromPos3 >= 0) {
766
- const orderByClause = findOrderByClause(scrubbed3, fromPos3);
767
- if (orderByClause) {
768
- const { result: orderResult, offset: orderOffset } = qualifyClauseColumnsSelective(result, scrubbed3, orderByClause.start, orderByClause.end, defaultQualifier, ambiguousColumns);
769
- if (orderOffset !== 0) {
770
- result = result.slice(0, orderByClause.start) + orderResult + result.slice(orderByClause.end);
771
- }
772
- }
773
- }
774
- return result;
775
- }
776
-
777
18
  // src/sql/result-mapper.ts
778
19
  import {
779
20
  Column,
@@ -1409,24 +650,6 @@ function isSavepointSyntaxError(error) {
1409
650
  }
1410
651
  return error.message.toLowerCase().includes("savepoint") && error.message.toLowerCase().includes("syntax error");
1411
652
  }
1412
- function rewriteQuery(mode, query) {
1413
- if (mode === "never") {
1414
- return { sql: query, rewritten: false };
1415
- }
1416
- let result = query;
1417
- let wasRewritten = false;
1418
- const arrayRewritten = adaptArrayOperators(result);
1419
- if (arrayRewritten !== result) {
1420
- result = arrayRewritten;
1421
- wasRewritten = true;
1422
- }
1423
- const joinQualified = qualifyJoinColumns(result);
1424
- if (joinQualified !== result) {
1425
- result = joinQualified;
1426
- wasRewritten = true;
1427
- }
1428
- return { sql: result, rewritten: wasRewritten };
1429
- }
1430
653
 
1431
654
  class DuckDBPreparedQuery extends PgPreparedQuery {
1432
655
  client;
@@ -1437,12 +660,11 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
1437
660
  fields;
1438
661
  _isResponseInArrayMode;
1439
662
  customResultMapper;
1440
- rewriteArraysMode;
1441
663
  rejectStringArrayLiterals;
1442
664
  prepareCache;
1443
665
  warnOnStringArrayLiteral;
1444
666
  static [entityKind] = "DuckDBPreparedQuery";
1445
- constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rewriteArraysMode, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
667
+ constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
1446
668
  super({ sql: queryString, params });
1447
669
  this.client = client;
1448
670
  this.dialect = dialect;
@@ -1452,7 +674,6 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
1452
674
  this.fields = fields;
1453
675
  this._isResponseInArrayMode = _isResponseInArrayMode;
1454
676
  this.customResultMapper = customResultMapper;
1455
- this.rewriteArraysMode = rewriteArraysMode;
1456
677
  this.rejectStringArrayLiterals = rejectStringArrayLiterals;
1457
678
  this.prepareCache = prepareCache;
1458
679
  this.warnOnStringArrayLiteral = warnOnStringArrayLiteral;
@@ -1463,20 +684,16 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
1463
684
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
1464
685
  warnOnStringArrayLiteral: this.warnOnStringArrayLiteral ? () => this.warnOnStringArrayLiteral?.(this.queryString) : undefined
1465
686
  });
1466
- const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, this.queryString);
1467
- if (didRewrite) {
1468
- this.logger.logQuery(`[duckdb] original query before array rewrite: ${this.queryString}`, params);
1469
- }
1470
- this.logger.logQuery(rewrittenQuery, params);
687
+ this.logger.logQuery(this.queryString, params);
1471
688
  const { fields, joinsNotNullableMap, customResultMapper } = this;
1472
689
  if (fields) {
1473
- const { rows: rows2 } = await executeArraysOnClient(this.client, rewrittenQuery, params, { prepareCache: this.prepareCache });
690
+ const { rows: rows2 } = await executeArraysOnClient(this.client, this.queryString, params, { prepareCache: this.prepareCache });
1474
691
  if (rows2.length === 0) {
1475
692
  return [];
1476
693
  }
1477
694
  return customResultMapper ? customResultMapper(rows2) : rows2.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
1478
695
  }
1479
- const rows = await executeOnClient(this.client, rewrittenQuery, params, {
696
+ const rows = await executeOnClient(this.client, this.queryString, params, {
1480
697
  prepareCache: this.prepareCache
1481
698
  });
1482
699
  return rows;
@@ -1496,7 +713,6 @@ class DuckDBSession extends PgSession {
1496
713
  static [entityKind] = "DuckDBSession";
1497
714
  dialect;
1498
715
  logger;
1499
- rewriteArraysMode;
1500
716
  rejectStringArrayLiterals;
1501
717
  prepareCache;
1502
718
  hasWarnedArrayLiteral = false;
@@ -1508,17 +724,15 @@ class DuckDBSession extends PgSession {
1508
724
  this.options = options;
1509
725
  this.dialect = dialect;
1510
726
  this.logger = options.logger ?? new NoopLogger;
1511
- this.rewriteArraysMode = options.rewriteArrays ?? "auto";
1512
727
  this.rejectStringArrayLiterals = options.rejectStringArrayLiterals ?? false;
1513
728
  this.prepareCache = options.prepareCache;
1514
729
  this.options = {
1515
730
  ...options,
1516
- rewriteArrays: this.rewriteArraysMode,
1517
731
  prepareCache: this.prepareCache
1518
732
  };
1519
733
  }
1520
734
  prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
1521
- 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);
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);
1522
736
  }
1523
737
  execute(query) {
1524
738
  this.dialect.resetPgJsonFlag();
@@ -1578,12 +792,8 @@ query: ${query}`, []);
1578
792
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
1579
793
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
1580
794
  });
1581
- const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
1582
- if (didRewrite) {
1583
- this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
1584
- }
1585
- this.logger.logQuery(rewrittenQuery, params);
1586
- return executeInBatches(this.client, rewrittenQuery, params, options);
795
+ this.logger.logQuery(builtQuery.sql, params);
796
+ return executeInBatches(this.client, builtQuery.sql, params, options);
1587
797
  }
1588
798
  executeBatchesRaw(query, options = {}) {
1589
799
  this.dialect.resetPgJsonFlag();
@@ -1593,12 +803,8 @@ query: ${query}`, []);
1593
803
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
1594
804
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
1595
805
  });
1596
- const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
1597
- if (didRewrite) {
1598
- this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
1599
- }
1600
- this.logger.logQuery(rewrittenQuery, params);
1601
- return executeInBatchesRaw(this.client, rewrittenQuery, params, options);
806
+ this.logger.logQuery(builtQuery.sql, params);
807
+ return executeInBatchesRaw(this.client, builtQuery.sql, params, options);
1602
808
  }
1603
809
  async executeArrow(query) {
1604
810
  this.dialect.resetPgJsonFlag();
@@ -1608,12 +814,8 @@ query: ${query}`, []);
1608
814
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
1609
815
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
1610
816
  });
1611
- const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
1612
- if (didRewrite) {
1613
- this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
1614
- }
1615
- this.logger.logQuery(rewrittenQuery, params);
1616
- return executeArrowOnClient(this.client, rewrittenQuery, params);
817
+ this.logger.logQuery(builtQuery.sql, params);
818
+ return executeArrowOnClient(this.client, builtQuery.sql, params);
1617
819
  }
1618
820
  markRollbackOnly() {
1619
821
  this.rollbackOnly = true;
@@ -1715,6 +917,524 @@ import {
1715
917
  import {
1716
918
  sql as sql2
1717
919
  } from "drizzle-orm";
920
+
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]
945
+ }
946
+ };
947
+ if (parent && key) {
948
+ parent[key] = fnExpr;
949
+ }
950
+ transformed = true;
951
+ } else {
952
+ transformed = walkExpression(binary.left, binary, "left") || transformed;
953
+ transformed = walkExpression(binary.right, binary, "right") || transformed;
954
+ }
955
+ }
956
+ if ("type" in expr && exprObj.type === "unary_expr") {
957
+ if ("expr" in exprObj) {
958
+ transformed = walkExpression(exprObj.expr, exprObj, "expr") || transformed;
959
+ }
960
+ }
961
+ if ("type" in expr && exprObj.type === "case") {
962
+ if ("expr" in exprObj && exprObj.expr) {
963
+ transformed = walkExpression(exprObj.expr, exprObj, "expr") || transformed;
964
+ }
965
+ if ("args" in exprObj && Array.isArray(exprObj.args)) {
966
+ for (let i = 0;i < exprObj.args.length; i++) {
967
+ const whenClause = exprObj.args[i];
968
+ if (whenClause.cond) {
969
+ transformed = walkExpression(whenClause.cond, whenClause, "cond") || transformed;
970
+ }
971
+ if (whenClause.result) {
972
+ transformed = walkExpression(whenClause.result, whenClause, "result") || transformed;
973
+ }
974
+ }
975
+ }
976
+ }
977
+ if ("args" in expr && exprObj.args) {
978
+ const args = exprObj.args;
979
+ if ("value" in args && Array.isArray(args.value)) {
980
+ for (let i = 0;i < args.value.length; i++) {
981
+ transformed = walkExpression(args.value[i], args.value, String(i)) || transformed;
982
+ }
983
+ } else if ("expr" in args) {
984
+ transformed = walkExpression(args.expr, args, "expr") || transformed;
985
+ }
986
+ }
987
+ if ("ast" in exprObj && exprObj.ast) {
988
+ const subAst = exprObj.ast;
989
+ if (subAst.type === "select") {
990
+ transformed = walkSelectImpl(subAst) || transformed;
991
+ }
992
+ }
993
+ if ("type" in expr && exprObj.type === "expr_list") {
994
+ if ("value" in exprObj && Array.isArray(exprObj.value)) {
995
+ for (let i = 0;i < exprObj.value.length; i++) {
996
+ transformed = walkExpression(exprObj.value[i], exprObj.value, String(i)) || transformed;
997
+ }
998
+ }
999
+ }
1000
+ return transformed;
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
+ transformed = walkOnClause(isBinaryExpr(expr.left) ? expr.left : expr.left, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1166
+ transformed = walkOnClause(isBinaryExpr(expr.right) ? expr.right : expr.right, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1167
+ }
1168
+ return transformed;
1169
+ }
1170
+ function qualifyAmbiguousInExpression(expr, defaultQualifier, ambiguousColumns) {
1171
+ if (!expr || typeof expr !== "object")
1172
+ return false;
1173
+ let transformed = false;
1174
+ if (isUnqualifiedColumnRef(expr)) {
1175
+ const colName = getColumnName(expr);
1176
+ if (colName && ambiguousColumns.has(colName)) {
1177
+ applyQualifier(expr, defaultQualifier);
1178
+ transformed = true;
1179
+ }
1180
+ return transformed;
1181
+ }
1182
+ if (isBinaryExpr(expr)) {
1183
+ const binary = expr;
1184
+ transformed = qualifyAmbiguousInExpression(binary.left, defaultQualifier, ambiguousColumns) || transformed;
1185
+ transformed = qualifyAmbiguousInExpression(binary.right, defaultQualifier, ambiguousColumns) || transformed;
1186
+ return transformed;
1187
+ }
1188
+ if ("args" in expr && expr.args) {
1189
+ const args = expr.args;
1190
+ if (args.value && Array.isArray(args.value)) {
1191
+ for (const arg of args.value) {
1192
+ transformed = qualifyAmbiguousInExpression(arg, defaultQualifier, ambiguousColumns) || transformed;
1193
+ }
1194
+ }
1195
+ if (args.expr) {
1196
+ transformed = qualifyAmbiguousInExpression(args.expr, defaultQualifier, ambiguousColumns) || transformed;
1197
+ }
1198
+ }
1199
+ if ("over" in expr && expr.over && typeof expr.over === "object") {
1200
+ const over = expr.over;
1201
+ if (Array.isArray(over.partition)) {
1202
+ for (const part of over.partition) {
1203
+ transformed = qualifyAmbiguousInExpression(part, defaultQualifier, ambiguousColumns) || transformed;
1204
+ }
1205
+ }
1206
+ if (Array.isArray(over.orderby)) {
1207
+ for (const order of over.orderby) {
1208
+ transformed = qualifyAmbiguousInExpression(order, defaultQualifier, ambiguousColumns) || transformed;
1209
+ }
1210
+ }
1211
+ }
1212
+ return transformed;
1213
+ }
1214
+ function hasUnqualifiedColumns(expr) {
1215
+ if (!expr || typeof expr !== "object")
1216
+ return false;
1217
+ if ("type" in expr && expr.type === "binary_expr") {
1218
+ const left = expr.left;
1219
+ const right = expr.right;
1220
+ const leftCol = unwrapColumnRef(left);
1221
+ const rightCol = unwrapColumnRef(right);
1222
+ if (isUnqualifiedColumnRef(left) || isUnqualifiedColumnRef(right) || leftCol && isUnqualifiedColumnRef(leftCol) || rightCol && isUnqualifiedColumnRef(rightCol)) {
1223
+ return true;
1224
+ }
1225
+ if (isBinaryExpr(expr.left) && hasUnqualifiedColumns(expr.left))
1226
+ return true;
1227
+ if (isBinaryExpr(expr.right) && hasUnqualifiedColumns(expr.right))
1228
+ return true;
1229
+ }
1230
+ if ("args" in expr && expr.args) {
1231
+ const args = expr.args;
1232
+ if (args.expr && isUnqualifiedColumnRef(args.expr))
1233
+ return true;
1234
+ if (args.value) {
1235
+ for (const arg of args.value) {
1236
+ if (isUnqualifiedColumnRef(arg))
1237
+ return true;
1238
+ }
1239
+ }
1240
+ }
1241
+ return false;
1242
+ }
1243
+ function walkSelect(select) {
1244
+ let transformed = false;
1245
+ const ambiguousColumns = new Set;
1246
+ if (Array.isArray(select.from) && select.from.length >= 2) {
1247
+ const firstSource = getTableSource(select.from[0]);
1248
+ const defaultQualifier = firstSource ? getQualifier(firstSource) : null;
1249
+ let prevSource = firstSource;
1250
+ let hasAnyUnqualified = false;
1251
+ for (const from of select.from) {
1252
+ if ("join" in from) {
1253
+ const join = from;
1254
+ if (join.on && hasUnqualifiedColumns(join.on)) {
1255
+ hasAnyUnqualified = true;
1256
+ break;
1257
+ }
1258
+ }
1259
+ }
1260
+ if (!hasAnyUnqualified) {
1261
+ for (const from of select.from) {
1262
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1263
+ transformed = walkSelect(from.expr.ast) || transformed;
1264
+ }
1265
+ }
1266
+ } else {
1267
+ for (const from of select.from) {
1268
+ if ("join" in from) {
1269
+ const join = from;
1270
+ const currentSource = getTableSource(join);
1271
+ if (join.on && prevSource && currentSource) {
1272
+ const leftQualifier = getQualifier(prevSource);
1273
+ const rightQualifier = getQualifier(currentSource);
1274
+ transformed = walkOnClause(join.on, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1275
+ }
1276
+ if (join.using && prevSource && currentSource) {
1277
+ for (const usingCol of join.using) {
1278
+ if (typeof usingCol === "string") {
1279
+ ambiguousColumns.add(usingCol);
1280
+ } else if ("value" in usingCol) {
1281
+ ambiguousColumns.add(String(usingCol.value));
1282
+ }
1283
+ }
1284
+ }
1285
+ prevSource = currentSource;
1286
+ } else {
1287
+ const source = getTableSource(from);
1288
+ if (source) {
1289
+ prevSource = source;
1290
+ }
1291
+ }
1292
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1293
+ transformed = walkSelect(from.expr.ast) || transformed;
1294
+ }
1295
+ }
1296
+ if (ambiguousColumns.size > 0 && defaultQualifier) {
1297
+ if (Array.isArray(select.columns)) {
1298
+ for (const col of select.columns) {
1299
+ if ("expr" in col) {
1300
+ transformed = qualifyAmbiguousInExpression(col.expr, defaultQualifier, ambiguousColumns) || transformed;
1301
+ }
1302
+ }
1303
+ }
1304
+ transformed = qualifyAmbiguousInExpression(select.where, defaultQualifier, ambiguousColumns) || transformed;
1305
+ if (Array.isArray(select.orderby)) {
1306
+ for (const order of select.orderby) {
1307
+ if (order.expr) {
1308
+ transformed = qualifyAmbiguousInExpression(order.expr, defaultQualifier, ambiguousColumns) || transformed;
1309
+ }
1310
+ }
1311
+ }
1312
+ }
1313
+ }
1314
+ }
1315
+ if (select.with) {
1316
+ for (const cte of select.with) {
1317
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
1318
+ if (cteSelect && cteSelect.type === "select") {
1319
+ transformed = walkSelect(cteSelect) || transformed;
1320
+ }
1321
+ }
1322
+ }
1323
+ if (select._next) {
1324
+ transformed = walkSelect(select._next) || transformed;
1325
+ }
1326
+ return transformed;
1327
+ }
1328
+ function qualifyJoinColumns(ast) {
1329
+ const statements = Array.isArray(ast) ? ast : [ast];
1330
+ let transformed = false;
1331
+ for (const stmt of statements) {
1332
+ if (stmt.type === "select") {
1333
+ transformed = walkSelect(stmt) || transformed;
1334
+ } else if (stmt.type === "insert") {
1335
+ const insert = stmt;
1336
+ if (insert.values && typeof insert.values === "object" && "type" in insert.values && insert.values.type === "select") {
1337
+ transformed = walkSelect(insert.values) || transformed;
1338
+ }
1339
+ } else if (stmt.type === "update") {
1340
+ const update = stmt;
1341
+ const mainSource = update.table?.[0] ? getTableSource(update.table[0]) : null;
1342
+ const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
1343
+ const fromSources = update.from ?? [];
1344
+ const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
1345
+ if (update.where && defaultQualifier && firstFrom) {
1346
+ const ambiguous = new Set;
1347
+ transformed = walkOnClause(update.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
1348
+ transformed = qualifyAmbiguousInExpression(update.where, defaultQualifier, ambiguous) || transformed;
1349
+ }
1350
+ if (Array.isArray(update.returning) && defaultQualifier) {
1351
+ for (const ret of update.returning) {
1352
+ transformed = qualifyAmbiguousInExpression(ret, defaultQualifier, new Set) || transformed;
1353
+ }
1354
+ }
1355
+ } else if (stmt.type === "delete") {
1356
+ const del = stmt;
1357
+ const mainSource = del.table?.[0] ? getTableSource(del.table[0]) : null;
1358
+ const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
1359
+ const fromSources = del.from ?? [];
1360
+ const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
1361
+ if (del.where && defaultQualifier && firstFrom) {
1362
+ const ambiguous = new Set;
1363
+ transformed = walkOnClause(del.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
1364
+ transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, ambiguous) || transformed;
1365
+ } else if (del.where && defaultQualifier) {
1366
+ transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, new Set) || transformed;
1367
+ }
1368
+ }
1369
+ }
1370
+ return transformed;
1371
+ }
1372
+
1373
+ // src/sql/ast-transformer.ts
1374
+ var { Parser } = nodeSqlParser;
1375
+ var parser = new Parser;
1376
+ var CACHE_SIZE = 500;
1377
+ var transformCache = new Map;
1378
+ function getCachedOrTransform(query, transform) {
1379
+ const cached = transformCache.get(query);
1380
+ if (cached) {
1381
+ transformCache.delete(query);
1382
+ transformCache.set(query, cached);
1383
+ return cached;
1384
+ }
1385
+ const result = transform();
1386
+ if (transformCache.size >= CACHE_SIZE) {
1387
+ const oldestKey = transformCache.keys().next().value;
1388
+ if (oldestKey) {
1389
+ transformCache.delete(oldestKey);
1390
+ }
1391
+ }
1392
+ transformCache.set(query, result);
1393
+ return result;
1394
+ }
1395
+ var DEBUG_ENV = "DRIZZLE_DUCKDB_DEBUG_AST";
1396
+ function hasJoin(query) {
1397
+ return /\bjoin\b/i.test(query);
1398
+ }
1399
+ function debugLog(message, payload) {
1400
+ if (process?.env?.[DEBUG_ENV]) {
1401
+ console.debug("[duckdb-ast]", message, payload ?? "");
1402
+ }
1403
+ }
1404
+ function transformSQL(query) {
1405
+ const needsArrayTransform = query.includes("@>") || query.includes("<@") || query.includes("&&");
1406
+ const needsJoinTransform = hasJoin(query) || /\bupdate\b/i.test(query) || /\bdelete\b/i.test(query);
1407
+ if (!needsArrayTransform && !needsJoinTransform) {
1408
+ return { sql: query, transformed: false };
1409
+ }
1410
+ return getCachedOrTransform(query, () => {
1411
+ try {
1412
+ const ast = parser.astify(query, { database: "PostgreSQL" });
1413
+ let transformed = false;
1414
+ if (needsArrayTransform) {
1415
+ transformed = transformArrayOperators(ast) || transformed;
1416
+ }
1417
+ if (needsJoinTransform) {
1418
+ transformed = qualifyJoinColumns(ast) || transformed;
1419
+ }
1420
+ if (!transformed) {
1421
+ debugLog("AST parsed but no transformation applied", {
1422
+ join: needsJoinTransform
1423
+ });
1424
+ return { sql: query, transformed: false };
1425
+ }
1426
+ const transformedSql = parser.sqlify(ast, { database: "PostgreSQL" });
1427
+ return { sql: transformedSql, transformed: true };
1428
+ } catch (err) {
1429
+ debugLog("AST transform failed; returning original SQL", {
1430
+ error: err.message
1431
+ });
1432
+ return { sql: query, transformed: false };
1433
+ }
1434
+ });
1435
+ }
1436
+
1437
+ // src/dialect.ts
1718
1438
  class DuckDBDialect extends PgDialect {
1719
1439
  static [entityKind2] = "DuckDBPgDialect";
1720
1440
  hasPgJsonColumn = false;
@@ -1791,6 +1511,14 @@ class DuckDBDialect extends PgDialect {
1791
1511
  return "none";
1792
1512
  }
1793
1513
  }
1514
+ sqlToQuery(sqlObj, invokeSource) {
1515
+ const result = super.sqlToQuery(sqlObj, invokeSource);
1516
+ const transformed = transformSQL(result.sql);
1517
+ return {
1518
+ ...result,
1519
+ sql: transformed.sql
1520
+ };
1521
+ }
1794
1522
  }
1795
1523
 
1796
1524
  // src/select-builder.ts
@@ -1801,10 +1529,10 @@ import {
1801
1529
  } from "drizzle-orm/pg-core/query-builders";
1802
1530
  import { Subquery, ViewBaseConfig } from "drizzle-orm";
1803
1531
  import { PgViewBase } from "drizzle-orm/pg-core/view-base";
1804
- import { SQL as SQL4 } from "drizzle-orm/sql/sql";
1532
+ import { SQL as SQL5 } from "drizzle-orm/sql/sql";
1805
1533
 
1806
1534
  // src/sql/selection.ts
1807
- import { Column as Column2, SQL as SQL3, getTableName as getTableName2, is as is3, sql as sql3 } from "drizzle-orm";
1535
+ import { Column as Column2, SQL as SQL4, getTableName as getTableName2, is as is3, sql as sql3 } from "drizzle-orm";
1808
1536
  function mapEntries(obj, prefix, fullJoin = false) {
1809
1537
  return Object.fromEntries(Object.entries(obj).filter(([key]) => key !== "enableRLS").map(([key, value]) => {
1810
1538
  const qualified = prefix ? `${prefix}.${key}` : key;
@@ -1814,16 +1542,16 @@ function mapEntries(obj, prefix, fullJoin = false) {
1814
1542
  sql3`${value}`.mapWith(value).as(`${getTableName2(value.table)}.${value.name}`)
1815
1543
  ];
1816
1544
  }
1817
- if (fullJoin && is3(value, SQL3)) {
1545
+ if (fullJoin && is3(value, SQL4)) {
1818
1546
  const col = value.getSQL().queryChunks.find((chunk) => is3(chunk, Column2));
1819
1547
  const tableName = col?.table && getTableName2(col?.table);
1820
1548
  return [key, value.as(tableName ? `${tableName}.${key}` : key)];
1821
1549
  }
1822
- if (is3(value, SQL3) || is3(value, Column2)) {
1823
- const aliased = is3(value, SQL3) ? value : sql3`${value}`.mapWith(value);
1550
+ if (is3(value, SQL4) || is3(value, Column2)) {
1551
+ const aliased = is3(value, SQL4) ? value : sql3`${value}`.mapWith(value);
1824
1552
  return [key, aliased.as(qualified)];
1825
1553
  }
1826
- if (is3(value, SQL3.Aliased)) {
1554
+ if (is3(value, SQL4.Aliased)) {
1827
1555
  return [key, value];
1828
1556
  }
1829
1557
  if (typeof value === "object" && value !== null) {
@@ -1871,7 +1599,7 @@ class DuckDBSelectBuilder extends PgSelectBuilder {
1871
1599
  ]));
1872
1600
  } else if (is4(src, PgViewBase)) {
1873
1601
  fields = src[ViewBaseConfig]?.selectedFields;
1874
- } else if (is4(src, SQL4)) {
1602
+ } else if (is4(src, SQL5)) {
1875
1603
  fields = {};
1876
1604
  } else {
1877
1605
  fields = aliasFields(getTableColumns(src), !isPartialSelect);
@@ -2062,16 +1790,6 @@ function createDuckDBConnectionPool(instance, options = {}) {
2062
1790
  }
2063
1791
 
2064
1792
  // src/options.ts
2065
- var DEFAULT_REWRITE_ARRAYS_MODE = "auto";
2066
- function resolveRewriteArraysOption(value) {
2067
- if (value === undefined)
2068
- return DEFAULT_REWRITE_ARRAYS_MODE;
2069
- if (value === true)
2070
- return "auto";
2071
- if (value === false)
2072
- return "never";
2073
- return value;
2074
- }
2075
1793
  var DEFAULT_PREPARED_CACHE_SIZE = 32;
2076
1794
  function resolvePrepareCacheOption(option) {
2077
1795
  if (!option)
@@ -2101,7 +1819,6 @@ class DuckDBDriver {
2101
1819
  createSession(schema) {
2102
1820
  return new DuckDBSession(this.client, this.dialect, schema, {
2103
1821
  logger: this.options.logger,
2104
- rewriteArrays: this.options.rewriteArrays ?? "auto",
2105
1822
  rejectStringArrayLiterals: this.options.rejectStringArrayLiterals,
2106
1823
  prepareCache: this.options.prepareCache
2107
1824
  });
@@ -2116,7 +1833,6 @@ function isConfigObject(data) {
2116
1833
  }
2117
1834
  function createFromClient(client, config = {}, instance) {
2118
1835
  const dialect = new DuckDBDialect;
2119
- const rewriteArraysMode = resolveRewriteArraysOption(config.rewriteArrays);
2120
1836
  const prepareCache = resolvePrepareCacheOption(config.prepareCache);
2121
1837
  const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
2122
1838
  let schema;
@@ -2130,7 +1846,6 @@ function createFromClient(client, config = {}, instance) {
2130
1846
  }
2131
1847
  const driver = new DuckDBDriver(client, dialect, {
2132
1848
  logger,
2133
- rewriteArrays: rewriteArraysMode,
2134
1849
  rejectStringArrayLiterals: config.rejectStringArrayLiterals,
2135
1850
  prepareCache
2136
1851
  });
@@ -3090,7 +2805,7 @@ function renderImports(imports, importBasePath) {
3090
2805
  // src/olap.ts
3091
2806
  import { is as is5 } from "drizzle-orm/entity";
3092
2807
  import { sql as sql6 } from "drizzle-orm";
3093
- import { SQL as SQL5 } from "drizzle-orm/sql/sql";
2808
+ import { SQL as SQL6 } from "drizzle-orm/sql/sql";
3094
2809
  import { Column as Column3, getTableName as getTableName3 } from "drizzle-orm";
3095
2810
  var countN = (expr = sql6`*`) => sql6`count(${expr})`.mapWith(Number);
3096
2811
  var sumN = (expr) => sql6`sum(${expr})`.mapWith(Number);
@@ -3125,7 +2840,7 @@ var denseRank = (options) => sql6`dense_rank() ${overClause(options)}`.mapWith(N
3125
2840
  var lag = (expr, offset = 1, defaultValue, options) => defaultValue ? sql6`lag(${expr}, ${offset}, ${defaultValue}) ${overClause(options)}` : sql6`lag(${expr}, ${offset}) ${overClause(options)}`;
3126
2841
  var lead = (expr, offset = 1, defaultValue, options) => defaultValue ? sql6`lead(${expr}, ${offset}, ${defaultValue}) ${overClause(options)}` : sql6`lead(${expr}, ${offset}) ${overClause(options)}`;
3127
2842
  function keyAlias(key, fallback) {
3128
- if (is5(key, SQL5.Aliased)) {
2843
+ if (is5(key, SQL6.Aliased)) {
3129
2844
  return key.fieldAlias ?? fallback;
3130
2845
  }
3131
2846
  if (is5(key, Column3)) {
@@ -3196,6 +2911,17 @@ class OlapBuilder {
3196
2911
  }
3197
2912
  }
3198
2913
  var olap = (db) => new OlapBuilder(db);
2914
+ // src/operators.ts
2915
+ import { sql as sql7 } from "drizzle-orm";
2916
+ function arrayHasAll(column, values) {
2917
+ return sql7`array_has_all(${column}, ${values})`;
2918
+ }
2919
+ function arrayHasAny(column, values) {
2920
+ return sql7`array_has_any(${column}, ${values})`;
2921
+ }
2922
+ function arrayContainedBy(column, values) {
2923
+ return sql7`array_has_all(${values}, ${column})`;
2924
+ }
3199
2925
  export {
3200
2926
  wrapperToNodeApiValue,
3201
2927
  wrapTimestamp,
@@ -3210,7 +2936,6 @@ export {
3210
2936
  sumDistinctN,
3211
2937
  splitTopLevel,
3212
2938
  rowNumber,
3213
- resolveRewriteArraysOption,
3214
2939
  resolvePrepareCacheOption,
3215
2940
  resolvePoolSize,
3216
2941
  rank,
@@ -3257,6 +2982,9 @@ export {
3257
2982
  buildListLiteral,
3258
2983
  buildDefault,
3259
2984
  avgN,
2985
+ arrayHasAny,
2986
+ arrayHasAll,
2987
+ arrayContainedBy,
3260
2988
  anyValue,
3261
2989
  POOL_PRESETS,
3262
2990
  OlapBuilder,