@leonardovida-md/drizzle-neo-duckdb 1.1.4 → 1.2.0

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.
@@ -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,335 @@ 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
+ };
1019
+ }
1020
+ if ("expr" in from && from.as) {
1021
+ return {
1022
+ name: from.as,
1023
+ alias: from.as
1024
+ };
1025
+ }
1026
+ return null;
1027
+ }
1028
+ function getQualifier(source) {
1029
+ return source.alias ?? source.name;
1030
+ }
1031
+ function isUnqualifiedColumnRef(expr) {
1032
+ return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && !(("table" in expr) && expr.table);
1033
+ }
1034
+ function getColumnName(col) {
1035
+ if (typeof col.column === "string") {
1036
+ return col.column;
1037
+ }
1038
+ if (col.column && "expr" in col.column && col.column.expr?.value) {
1039
+ return String(col.column.expr.value);
1040
+ }
1041
+ return null;
1042
+ }
1043
+ function walkOnClause(expr, leftSource, rightSource, ambiguousColumns) {
1044
+ if (!expr || typeof expr !== "object")
1045
+ return false;
1046
+ let transformed = false;
1047
+ if (expr.type === "binary_expr") {
1048
+ if (expr.operator === "=") {
1049
+ const left = expr.left;
1050
+ const right = expr.right;
1051
+ if (isUnqualifiedColumnRef(left) && isUnqualifiedColumnRef(right)) {
1052
+ const leftColName = getColumnName(left);
1053
+ const rightColName = getColumnName(right);
1054
+ if (leftColName && rightColName && leftColName === rightColName) {
1055
+ left.table = leftSource;
1056
+ right.table = rightSource;
1057
+ ambiguousColumns.add(leftColName);
1058
+ transformed = true;
1059
+ }
1060
+ }
1061
+ }
1062
+ if (expr.operator === "AND" || expr.operator === "OR") {
1063
+ transformed = walkOnClause(expr.left, leftSource, rightSource, ambiguousColumns) || transformed;
1064
+ transformed = walkOnClause(expr.right, leftSource, rightSource, ambiguousColumns) || transformed;
1065
+ }
1066
+ }
1067
+ return transformed;
1068
+ }
1069
+ function qualifyAmbiguousInExpression(expr, defaultQualifier, ambiguousColumns) {
1070
+ if (!expr || typeof expr !== "object")
1071
+ return false;
1072
+ let transformed = false;
1073
+ if (isUnqualifiedColumnRef(expr)) {
1074
+ const colName = getColumnName(expr);
1075
+ if (colName && ambiguousColumns.has(colName)) {
1076
+ expr.table = defaultQualifier;
1077
+ transformed = true;
1078
+ }
1079
+ return transformed;
1080
+ }
1081
+ if ("type" in expr && expr.type === "binary_expr") {
1082
+ const binary = expr;
1083
+ transformed = qualifyAmbiguousInExpression(binary.left, defaultQualifier, ambiguousColumns) || transformed;
1084
+ transformed = qualifyAmbiguousInExpression(binary.right, defaultQualifier, ambiguousColumns) || transformed;
1085
+ return transformed;
1086
+ }
1087
+ if ("args" in expr && expr.args) {
1088
+ const args = expr.args;
1089
+ if (args.value && Array.isArray(args.value)) {
1090
+ for (const arg of args.value) {
1091
+ transformed = qualifyAmbiguousInExpression(arg, defaultQualifier, ambiguousColumns) || transformed;
1092
+ }
1093
+ }
1094
+ if (args.expr) {
1095
+ transformed = qualifyAmbiguousInExpression(args.expr, defaultQualifier, ambiguousColumns) || transformed;
1096
+ }
1097
+ }
1098
+ return transformed;
1099
+ }
1100
+ function walkSelect(select) {
1101
+ let transformed = false;
1102
+ const ambiguousColumns = new Set;
1103
+ if (Array.isArray(select.from) && select.from.length >= 2) {
1104
+ const firstSource = getTableSource(select.from[0]);
1105
+ const defaultQualifier = firstSource ? getQualifier(firstSource) : "";
1106
+ let prevSource = firstSource;
1107
+ for (const from of select.from) {
1108
+ if ("join" in from) {
1109
+ const join = from;
1110
+ const currentSource = getTableSource(join);
1111
+ if (join.on && prevSource && currentSource) {
1112
+ const leftQualifier = getQualifier(prevSource);
1113
+ const rightQualifier = getQualifier(currentSource);
1114
+ transformed = walkOnClause(join.on, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
1115
+ }
1116
+ prevSource = currentSource;
1117
+ } else {
1118
+ const source = getTableSource(from);
1119
+ if (source) {
1120
+ prevSource = source;
1121
+ }
1122
+ }
1123
+ if ("expr" in from && from.expr && "ast" in from.expr) {
1124
+ transformed = walkSelect(from.expr.ast) || transformed;
1125
+ }
1126
+ }
1127
+ if (ambiguousColumns.size > 0 && defaultQualifier) {
1128
+ if (Array.isArray(select.columns)) {
1129
+ for (const col of select.columns) {
1130
+ if ("expr" in col) {
1131
+ transformed = qualifyAmbiguousInExpression(col.expr, defaultQualifier, ambiguousColumns) || transformed;
1132
+ }
1133
+ }
1134
+ }
1135
+ transformed = qualifyAmbiguousInExpression(select.where, defaultQualifier, ambiguousColumns) || transformed;
1136
+ if (Array.isArray(select.orderby)) {
1137
+ for (const order of select.orderby) {
1138
+ if (order.expr) {
1139
+ transformed = qualifyAmbiguousInExpression(order.expr, defaultQualifier, ambiguousColumns) || transformed;
1140
+ }
1141
+ }
1142
+ }
1143
+ }
1144
+ }
1145
+ if (select.with) {
1146
+ for (const cte of select.with) {
1147
+ const cteSelect = cte.stmt?.ast ?? cte.stmt;
1148
+ if (cteSelect && cteSelect.type === "select") {
1149
+ transformed = walkSelect(cteSelect) || transformed;
1150
+ }
1151
+ }
1152
+ }
1153
+ if (select._next) {
1154
+ transformed = walkSelect(select._next) || transformed;
1155
+ }
1156
+ return transformed;
1157
+ }
1158
+ function qualifyJoinColumns(ast) {
1159
+ const statements = Array.isArray(ast) ? ast : [ast];
1160
+ let transformed = false;
1161
+ for (const stmt of statements) {
1162
+ if (stmt.type === "select") {
1163
+ transformed = walkSelect(stmt) || transformed;
1164
+ }
1165
+ }
1166
+ return transformed;
1167
+ }
1168
+
1169
+ // src/sql/ast-transformer.ts
1170
+ var { Parser } = nodeSqlParser;
1171
+ var parser = new Parser;
1172
+ function transformSQL(query) {
1173
+ const needsArrayTransform = query.includes("@>") || query.includes("<@") || query.includes("&&");
1174
+ const needsJoinTransform = query.toLowerCase().includes("join");
1175
+ if (!needsArrayTransform && !needsJoinTransform) {
1176
+ return { sql: query, transformed: false };
1177
+ }
1178
+ try {
1179
+ const ast = parser.astify(query, { database: "PostgreSQL" });
1180
+ let transformed = false;
1181
+ if (needsArrayTransform) {
1182
+ transformed = transformArrayOperators(ast) || transformed;
1183
+ }
1184
+ if (needsJoinTransform) {
1185
+ transformed = qualifyJoinColumns(ast) || transformed;
1186
+ }
1187
+ if (!transformed) {
1188
+ return { sql: query, transformed: false };
1189
+ }
1190
+ const transformedSql = parser.sqlify(ast, { database: "PostgreSQL" });
1191
+ return { sql: transformedSql, transformed: true };
1192
+ } catch {
1193
+ return { sql: query, transformed: false };
1194
+ }
1195
+ }
1196
+
1197
+ // src/dialect.ts
1667
1198
  class DuckDBDialect extends PgDialect {
1668
1199
  static [entityKind2] = "DuckDBPgDialect";
1669
1200
  hasPgJsonColumn = false;
@@ -1740,6 +1271,14 @@ class DuckDBDialect extends PgDialect {
1740
1271
  return "none";
1741
1272
  }
1742
1273
  }
1274
+ sqlToQuery(sqlObj, invokeSource) {
1275
+ const result = super.sqlToQuery(sqlObj, invokeSource);
1276
+ const transformed = transformSQL(result.sql);
1277
+ return {
1278
+ ...result,
1279
+ sql: transformed.sql
1280
+ };
1281
+ }
1743
1282
  }
1744
1283
 
1745
1284
  // src/select-builder.ts
@@ -1750,10 +1289,10 @@ import {
1750
1289
  } from "drizzle-orm/pg-core/query-builders";
1751
1290
  import { Subquery, ViewBaseConfig } from "drizzle-orm";
1752
1291
  import { PgViewBase } from "drizzle-orm/pg-core/view-base";
1753
- import { SQL as SQL4 } from "drizzle-orm/sql/sql";
1292
+ import { SQL as SQL5 } from "drizzle-orm/sql/sql";
1754
1293
 
1755
1294
  // src/sql/selection.ts
1756
- import { Column as Column2, SQL as SQL3, getTableName as getTableName2, is as is3, sql as sql3 } from "drizzle-orm";
1295
+ import { Column as Column2, SQL as SQL4, getTableName as getTableName2, is as is3, sql as sql3 } from "drizzle-orm";
1757
1296
  function mapEntries(obj, prefix, fullJoin = false) {
1758
1297
  return Object.fromEntries(Object.entries(obj).filter(([key]) => key !== "enableRLS").map(([key, value]) => {
1759
1298
  const qualified = prefix ? `${prefix}.${key}` : key;
@@ -1763,16 +1302,16 @@ function mapEntries(obj, prefix, fullJoin = false) {
1763
1302
  sql3`${value}`.mapWith(value).as(`${getTableName2(value.table)}.${value.name}`)
1764
1303
  ];
1765
1304
  }
1766
- if (fullJoin && is3(value, SQL3)) {
1305
+ if (fullJoin && is3(value, SQL4)) {
1767
1306
  const col = value.getSQL().queryChunks.find((chunk) => is3(chunk, Column2));
1768
1307
  const tableName = col?.table && getTableName2(col?.table);
1769
1308
  return [key, value.as(tableName ? `${tableName}.${key}` : key)];
1770
1309
  }
1771
- if (is3(value, SQL3) || is3(value, Column2)) {
1772
- const aliased = is3(value, SQL3) ? value : sql3`${value}`.mapWith(value);
1310
+ if (is3(value, SQL4) || is3(value, Column2)) {
1311
+ const aliased = is3(value, SQL4) ? value : sql3`${value}`.mapWith(value);
1773
1312
  return [key, aliased.as(qualified)];
1774
1313
  }
1775
- if (is3(value, SQL3.Aliased)) {
1314
+ if (is3(value, SQL4.Aliased)) {
1776
1315
  return [key, value];
1777
1316
  }
1778
1317
  if (typeof value === "object" && value !== null) {
@@ -1820,7 +1359,7 @@ class DuckDBSelectBuilder extends PgSelectBuilder {
1820
1359
  ]));
1821
1360
  } else if (is4(src, PgViewBase)) {
1822
1361
  fields = src[ViewBaseConfig]?.selectedFields;
1823
- } else if (is4(src, SQL4)) {
1362
+ } else if (is4(src, SQL5)) {
1824
1363
  fields = {};
1825
1364
  } else {
1826
1365
  fields = aliasFields(getTableColumns(src), !isPartialSelect);
@@ -2011,16 +1550,6 @@ function createDuckDBConnectionPool(instance, options = {}) {
2011
1550
  }
2012
1551
 
2013
1552
  // 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
1553
  var DEFAULT_PREPARED_CACHE_SIZE = 32;
2025
1554
  function resolvePrepareCacheOption(option) {
2026
1555
  if (!option)
@@ -2050,7 +1579,6 @@ class DuckDBDriver {
2050
1579
  createSession(schema) {
2051
1580
  return new DuckDBSession(this.client, this.dialect, schema, {
2052
1581
  logger: this.options.logger,
2053
- rewriteArrays: this.options.rewriteArrays ?? "auto",
2054
1582
  rejectStringArrayLiterals: this.options.rejectStringArrayLiterals,
2055
1583
  prepareCache: this.options.prepareCache
2056
1584
  });
@@ -2065,7 +1593,6 @@ function isConfigObject(data) {
2065
1593
  }
2066
1594
  function createFromClient(client, config = {}, instance) {
2067
1595
  const dialect = new DuckDBDialect;
2068
- const rewriteArraysMode = resolveRewriteArraysOption(config.rewriteArrays);
2069
1596
  const prepareCache = resolvePrepareCacheOption(config.prepareCache);
2070
1597
  const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
2071
1598
  let schema;
@@ -2079,7 +1606,6 @@ function createFromClient(client, config = {}, instance) {
2079
1606
  }
2080
1607
  const driver = new DuckDBDriver(client, dialect, {
2081
1608
  logger,
2082
- rewriteArrays: rewriteArraysMode,
2083
1609
  rejectStringArrayLiterals: config.rejectStringArrayLiterals,
2084
1610
  prepareCache
2085
1611
  });