@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.
- package/README.md +2 -3
- package/dist/dialect.d.ts +3 -1
- package/dist/driver.d.ts +1 -3
- package/dist/duckdb-introspect.mjs +355 -829
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +371 -832
- package/dist/operators.d.ts +8 -0
- package/dist/options.d.ts +0 -3
- package/dist/session.d.ts +2 -5
- package/dist/sql/ast-transformer.d.ts +15 -0
- package/dist/sql/visitors/array-operators.d.ts +5 -0
- package/dist/sql/visitors/column-qualifier.d.ts +5 -0
- package/dist/utils.d.ts +0 -1
- package/package.json +3 -2
- package/src/dialect.ts +20 -0
- package/src/driver.ts +0 -8
- package/src/index.ts +1 -0
- package/src/operators.ts +27 -0
- package/src/options.ts +0 -15
- package/src/session.ts +10 -96
- package/src/sql/ast-transformer.ts +68 -0
- package/src/sql/visitors/array-operators.ts +214 -0
- package/src/sql/visitors/column-qualifier.ts +278 -0
- package/src/utils.ts +0 -1
- package/dist/sql/query-rewriters.d.ts +0 -15
- package/src/sql/query-rewriters.ts +0 -1161
|
@@ -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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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.
|
|
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
|
-
|
|
1531
|
-
|
|
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
|
-
|
|
1546
|
-
|
|
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
|
-
|
|
1561
|
-
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
1772
|
-
const aliased = is3(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,
|
|
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,
|
|
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
|
});
|