@leonardovida-md/drizzle-neo-duckdb 1.1.1 → 1.1.3
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 +44 -85
- package/dist/client.d.ts +15 -1
- package/dist/columns.d.ts +3 -2
- package/dist/driver.d.ts +7 -3
- package/dist/duckdb-introspect.mjs +595 -50
- package/dist/helpers.mjs +31 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +621 -50
- package/dist/options.d.ts +10 -0
- package/dist/session.d.ts +11 -5
- package/dist/sql/query-rewriters.d.ts +12 -0
- package/package.json +1 -1
- package/src/client.ts +300 -40
- package/src/columns.ts +51 -4
- package/src/driver.ts +30 -5
- package/src/index.ts +1 -0
- package/src/options.ts +40 -0
- package/src/session.ts +128 -27
- package/src/sql/query-rewriters.ts +503 -0
package/dist/index.mjs
CHANGED
|
@@ -153,6 +153,9 @@ function walkRight(source, scrubbed, start) {
|
|
|
153
153
|
return [scrubbed.length, source.slice(start)];
|
|
154
154
|
}
|
|
155
155
|
function adaptArrayOperators(query) {
|
|
156
|
+
if (query.indexOf("@>") === -1 && query.indexOf("<@") === -1 && query.indexOf("&&") === -1) {
|
|
157
|
+
return query;
|
|
158
|
+
}
|
|
156
159
|
let rewritten = query;
|
|
157
160
|
let scrubbed = scrubForRewrite(query);
|
|
158
161
|
let searchStart = 0;
|
|
@@ -172,6 +175,328 @@ function adaptArrayOperators(query) {
|
|
|
172
175
|
}
|
|
173
176
|
return rewritten;
|
|
174
177
|
}
|
|
178
|
+
function extractQuotedIdentifier(original, start) {
|
|
179
|
+
if (original[start] !== '"') {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
let pos = start + 1;
|
|
183
|
+
while (pos < original.length && original[pos] !== '"') {
|
|
184
|
+
if (original[pos] === '"' && original[pos + 1] === '"') {
|
|
185
|
+
pos += 2;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
pos++;
|
|
189
|
+
}
|
|
190
|
+
if (pos >= original.length) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
name: original.slice(start + 1, pos),
|
|
195
|
+
end: pos + 1
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function findMainFromClause(scrubbed) {
|
|
199
|
+
const lowerScrubbed = scrubbed.toLowerCase();
|
|
200
|
+
let searchStart = 0;
|
|
201
|
+
const withMatch = /\bwith\s+/i.exec(lowerScrubbed);
|
|
202
|
+
if (withMatch) {
|
|
203
|
+
let depth = 0;
|
|
204
|
+
let pos = withMatch.index + withMatch[0].length;
|
|
205
|
+
while (pos < scrubbed.length) {
|
|
206
|
+
const char = scrubbed[pos];
|
|
207
|
+
if (char === "(") {
|
|
208
|
+
depth++;
|
|
209
|
+
} else if (char === ")") {
|
|
210
|
+
depth--;
|
|
211
|
+
} else if (depth === 0) {
|
|
212
|
+
const remaining = lowerScrubbed.slice(pos);
|
|
213
|
+
if (/^\s*select\s+/i.test(remaining)) {
|
|
214
|
+
searchStart = pos;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
pos++;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const fromPattern = /\bfrom\s+/gi;
|
|
222
|
+
fromPattern.lastIndex = searchStart;
|
|
223
|
+
const fromMatch = fromPattern.exec(lowerScrubbed);
|
|
224
|
+
return fromMatch ? fromMatch.index + fromMatch[0].length : -1;
|
|
225
|
+
}
|
|
226
|
+
function parseTableSources(original, scrubbed) {
|
|
227
|
+
const sources = [];
|
|
228
|
+
const lowerScrubbed = scrubbed.toLowerCase();
|
|
229
|
+
const fromPos = findMainFromClause(scrubbed);
|
|
230
|
+
if (fromPos < 0) {
|
|
231
|
+
return sources;
|
|
232
|
+
}
|
|
233
|
+
const fromTable = parseTableRef(original, scrubbed, fromPos);
|
|
234
|
+
if (fromTable) {
|
|
235
|
+
sources.push(fromTable);
|
|
236
|
+
}
|
|
237
|
+
const joinPattern = /\b(left\s+|right\s+|inner\s+|full\s+|cross\s+)?join\s+/gi;
|
|
238
|
+
joinPattern.lastIndex = fromPos;
|
|
239
|
+
let joinMatch;
|
|
240
|
+
while ((joinMatch = joinPattern.exec(lowerScrubbed)) !== null) {
|
|
241
|
+
const tableStart = joinMatch.index + joinMatch[0].length;
|
|
242
|
+
const joinTable = parseTableRef(original, scrubbed, tableStart);
|
|
243
|
+
if (joinTable) {
|
|
244
|
+
sources.push(joinTable);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return sources;
|
|
248
|
+
}
|
|
249
|
+
function parseTableRef(original, scrubbed, start) {
|
|
250
|
+
let pos = start;
|
|
251
|
+
while (pos < scrubbed.length && isWhitespace(scrubbed[pos])) {
|
|
252
|
+
pos++;
|
|
253
|
+
}
|
|
254
|
+
if (original[pos] !== '"') {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const nameStart = pos;
|
|
258
|
+
const firstIdent = extractQuotedIdentifier(original, pos);
|
|
259
|
+
if (!firstIdent) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
let name = firstIdent.name;
|
|
263
|
+
pos = firstIdent.end;
|
|
264
|
+
let afterName = pos;
|
|
265
|
+
while (afterName < scrubbed.length && isWhitespace(scrubbed[afterName])) {
|
|
266
|
+
afterName++;
|
|
267
|
+
}
|
|
268
|
+
if (scrubbed[afterName] === ".") {
|
|
269
|
+
afterName++;
|
|
270
|
+
while (afterName < scrubbed.length && isWhitespace(scrubbed[afterName])) {
|
|
271
|
+
afterName++;
|
|
272
|
+
}
|
|
273
|
+
if (original[afterName] === '"') {
|
|
274
|
+
const tableIdent = extractQuotedIdentifier(original, afterName);
|
|
275
|
+
if (tableIdent) {
|
|
276
|
+
name = tableIdent.name;
|
|
277
|
+
pos = tableIdent.end;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
let alias;
|
|
282
|
+
let aliasPos = pos;
|
|
283
|
+
while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
|
|
284
|
+
aliasPos++;
|
|
285
|
+
}
|
|
286
|
+
const afterTable = scrubbed.slice(aliasPos).toLowerCase();
|
|
287
|
+
if (afterTable.startsWith("as ")) {
|
|
288
|
+
aliasPos += 3;
|
|
289
|
+
while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
|
|
290
|
+
aliasPos++;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (original[aliasPos] === '"' && !afterTable.startsWith("on ") && !afterTable.startsWith("left ") && !afterTable.startsWith("right ") && !afterTable.startsWith("inner ") && !afterTable.startsWith("full ") && !afterTable.startsWith("cross ") && !afterTable.startsWith("join ") && !afterTable.startsWith("where ") && !afterTable.startsWith("group ") && !afterTable.startsWith("order ") && !afterTable.startsWith("limit ") && !afterTable.startsWith("as ")) {
|
|
294
|
+
const aliasIdent = extractQuotedIdentifier(original, aliasPos);
|
|
295
|
+
if (aliasIdent) {
|
|
296
|
+
alias = aliasIdent.name;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
name,
|
|
301
|
+
alias,
|
|
302
|
+
position: nameStart
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function findJoinClauses(original, scrubbed, sources) {
|
|
306
|
+
const clauses = [];
|
|
307
|
+
const lowerScrubbed = scrubbed.toLowerCase();
|
|
308
|
+
const joinPattern = /\b(left\s+|right\s+|inner\s+|full\s+|cross\s+)?join\s+"[^"]*"(\s*\.\s*"[^"]*")?(\s+as)?(\s+"[^"]*")?\s+on\s+/gi;
|
|
309
|
+
let match;
|
|
310
|
+
let sourceIndex = 1;
|
|
311
|
+
while ((match = joinPattern.exec(lowerScrubbed)) !== null) {
|
|
312
|
+
const joinType = (match[1] || "").trim().toLowerCase();
|
|
313
|
+
const joinKeywordEnd = match.index + (match[1] || "").length + "join".length;
|
|
314
|
+
let tableStart = joinKeywordEnd;
|
|
315
|
+
while (tableStart < original.length && isWhitespace(original[tableStart])) {
|
|
316
|
+
tableStart++;
|
|
317
|
+
}
|
|
318
|
+
const tableIdent = extractQuotedIdentifier(original, tableStart);
|
|
319
|
+
if (!tableIdent)
|
|
320
|
+
continue;
|
|
321
|
+
let tableName = tableIdent.name;
|
|
322
|
+
let afterTable = tableIdent.end;
|
|
323
|
+
let checkPos = afterTable;
|
|
324
|
+
while (checkPos < scrubbed.length && isWhitespace(scrubbed[checkPos])) {
|
|
325
|
+
checkPos++;
|
|
326
|
+
}
|
|
327
|
+
if (scrubbed[checkPos] === ".") {
|
|
328
|
+
checkPos++;
|
|
329
|
+
while (checkPos < scrubbed.length && isWhitespace(scrubbed[checkPos])) {
|
|
330
|
+
checkPos++;
|
|
331
|
+
}
|
|
332
|
+
const realTableIdent = extractQuotedIdentifier(original, checkPos);
|
|
333
|
+
if (realTableIdent) {
|
|
334
|
+
tableName = realTableIdent.name;
|
|
335
|
+
afterTable = realTableIdent.end;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
let tableAlias;
|
|
339
|
+
let aliasPos = afterTable;
|
|
340
|
+
while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
|
|
341
|
+
aliasPos++;
|
|
342
|
+
}
|
|
343
|
+
const afterTableStr = scrubbed.slice(aliasPos).toLowerCase();
|
|
344
|
+
if (afterTableStr.startsWith("as ")) {
|
|
345
|
+
aliasPos += 3;
|
|
346
|
+
while (aliasPos < scrubbed.length && isWhitespace(scrubbed[aliasPos])) {
|
|
347
|
+
aliasPos++;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (original[aliasPos] === '"' && !afterTableStr.startsWith("on ")) {
|
|
351
|
+
const aliasIdent = extractQuotedIdentifier(original, aliasPos);
|
|
352
|
+
if (aliasIdent) {
|
|
353
|
+
tableAlias = aliasIdent.name;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const onStart = match.index + match[0].length;
|
|
357
|
+
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;
|
|
358
|
+
const remaining = lowerScrubbed.slice(onStart);
|
|
359
|
+
const endMatch = endPattern.exec(remaining);
|
|
360
|
+
const onEnd = endMatch ? onStart + endMatch.index : scrubbed.length;
|
|
361
|
+
let leftSource = "";
|
|
362
|
+
if (sourceIndex > 0 && sourceIndex <= sources.length) {
|
|
363
|
+
const prev = sources[sourceIndex - 1];
|
|
364
|
+
leftSource = prev?.alias || prev?.name || "";
|
|
365
|
+
}
|
|
366
|
+
const rightSource = tableAlias || tableName;
|
|
367
|
+
clauses.push({
|
|
368
|
+
joinType,
|
|
369
|
+
tableName,
|
|
370
|
+
tableAlias,
|
|
371
|
+
onStart,
|
|
372
|
+
onEnd,
|
|
373
|
+
leftSource,
|
|
374
|
+
rightSource
|
|
375
|
+
});
|
|
376
|
+
sourceIndex++;
|
|
377
|
+
}
|
|
378
|
+
return clauses;
|
|
379
|
+
}
|
|
380
|
+
function qualifyJoinColumns(query) {
|
|
381
|
+
const lowerQuery = query.toLowerCase();
|
|
382
|
+
if (!lowerQuery.includes("join")) {
|
|
383
|
+
return query;
|
|
384
|
+
}
|
|
385
|
+
const scrubbed = scrubForRewrite(query);
|
|
386
|
+
const sources = parseTableSources(query, scrubbed);
|
|
387
|
+
if (sources.length < 2) {
|
|
388
|
+
return query;
|
|
389
|
+
}
|
|
390
|
+
const joinClauses = findJoinClauses(query, scrubbed, sources);
|
|
391
|
+
if (joinClauses.length === 0) {
|
|
392
|
+
return query;
|
|
393
|
+
}
|
|
394
|
+
let result = query;
|
|
395
|
+
let offset = 0;
|
|
396
|
+
for (const join of joinClauses) {
|
|
397
|
+
const scrubbedOnClause = scrubbed.slice(join.onStart, join.onEnd);
|
|
398
|
+
const originalOnClause = query.slice(join.onStart, join.onEnd);
|
|
399
|
+
let clauseResult = originalOnClause;
|
|
400
|
+
let clauseOffset = 0;
|
|
401
|
+
let eqPos = -1;
|
|
402
|
+
while ((eqPos = scrubbedOnClause.indexOf("=", eqPos + 1)) !== -1) {
|
|
403
|
+
let lhsEnd = eqPos - 1;
|
|
404
|
+
while (lhsEnd >= 0 && isWhitespace(scrubbedOnClause[lhsEnd])) {
|
|
405
|
+
lhsEnd--;
|
|
406
|
+
}
|
|
407
|
+
if (scrubbedOnClause[lhsEnd] !== '"')
|
|
408
|
+
continue;
|
|
409
|
+
let lhsStartPos = lhsEnd - 1;
|
|
410
|
+
while (lhsStartPos >= 0 && scrubbedOnClause[lhsStartPos] !== '"') {
|
|
411
|
+
lhsStartPos--;
|
|
412
|
+
}
|
|
413
|
+
if (lhsStartPos < 0)
|
|
414
|
+
continue;
|
|
415
|
+
const lhsIsQualified = lhsStartPos > 0 && scrubbedOnClause[lhsStartPos - 1] === ".";
|
|
416
|
+
let rhsStartPos = eqPos + 1;
|
|
417
|
+
while (rhsStartPos < scrubbedOnClause.length && isWhitespace(scrubbedOnClause[rhsStartPos])) {
|
|
418
|
+
rhsStartPos++;
|
|
419
|
+
}
|
|
420
|
+
const rhsChar = originalOnClause[rhsStartPos];
|
|
421
|
+
const rhsIsParam = rhsChar === "$";
|
|
422
|
+
const rhsIsStringLiteral = rhsChar === "'";
|
|
423
|
+
const rhsIsColumn = rhsChar === '"';
|
|
424
|
+
if (!rhsIsParam && !rhsIsStringLiteral && !rhsIsColumn)
|
|
425
|
+
continue;
|
|
426
|
+
const rhsIsQualified = !rhsIsColumn || rhsStartPos > 0 && scrubbedOnClause[rhsStartPos - 1] === ".";
|
|
427
|
+
if (lhsIsQualified || rhsIsQualified)
|
|
428
|
+
continue;
|
|
429
|
+
const lhsIdent = extractQuotedIdentifier(originalOnClause, lhsStartPos);
|
|
430
|
+
if (!lhsIdent)
|
|
431
|
+
continue;
|
|
432
|
+
let rhsIdent = null;
|
|
433
|
+
let rhsValue = "";
|
|
434
|
+
let rhsEnd = rhsStartPos;
|
|
435
|
+
if (rhsIsParam) {
|
|
436
|
+
let paramEnd = rhsStartPos + 1;
|
|
437
|
+
while (paramEnd < originalOnClause.length && /\d/.test(originalOnClause[paramEnd])) {
|
|
438
|
+
paramEnd++;
|
|
439
|
+
}
|
|
440
|
+
rhsValue = originalOnClause.slice(rhsStartPos, paramEnd);
|
|
441
|
+
rhsEnd = paramEnd;
|
|
442
|
+
} else if (rhsIsStringLiteral) {
|
|
443
|
+
let literalEnd = rhsStartPos + 1;
|
|
444
|
+
while (literalEnd < originalOnClause.length) {
|
|
445
|
+
if (originalOnClause[literalEnd] === "'") {
|
|
446
|
+
if (originalOnClause[literalEnd + 1] === "'") {
|
|
447
|
+
literalEnd += 2;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
literalEnd++;
|
|
453
|
+
}
|
|
454
|
+
rhsValue = originalOnClause.slice(rhsStartPos, literalEnd + 1);
|
|
455
|
+
rhsEnd = literalEnd + 1;
|
|
456
|
+
} else if (rhsIsColumn) {
|
|
457
|
+
rhsIdent = extractQuotedIdentifier(originalOnClause, rhsStartPos);
|
|
458
|
+
if (rhsIdent) {
|
|
459
|
+
if (rhsIdent.end < scrubbedOnClause.length && scrubbedOnClause[rhsIdent.end] === ".") {
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
rhsValue = `"${rhsIdent.name}"`;
|
|
463
|
+
rhsEnd = rhsIdent.end;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (!rhsValue)
|
|
467
|
+
continue;
|
|
468
|
+
if (!rhsIsColumn || !rhsIdent || lhsIdent.name !== rhsIdent.name) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
const lhsOriginal = `"${lhsIdent.name}"`;
|
|
472
|
+
let newLhs = lhsOriginal;
|
|
473
|
+
let newRhs = rhsValue;
|
|
474
|
+
if (!lhsIsQualified && join.leftSource) {
|
|
475
|
+
newLhs = `"${join.leftSource}"."${lhsIdent.name}"`;
|
|
476
|
+
}
|
|
477
|
+
if (!rhsIsQualified && rhsIsColumn && rhsIdent && join.rightSource) {
|
|
478
|
+
newRhs = `"${join.rightSource}"."${rhsIdent.name}"`;
|
|
479
|
+
}
|
|
480
|
+
if (newLhs !== lhsOriginal || newRhs !== rhsValue) {
|
|
481
|
+
const opStart = lhsIdent.end;
|
|
482
|
+
let opEnd = opStart;
|
|
483
|
+
while (opEnd < rhsEnd && (isWhitespace(originalOnClause[opEnd]) || originalOnClause[opEnd] === "=")) {
|
|
484
|
+
opEnd++;
|
|
485
|
+
}
|
|
486
|
+
const operator = originalOnClause.slice(opStart, opEnd);
|
|
487
|
+
const newExpr = `${newLhs}${operator}${newRhs}`;
|
|
488
|
+
const oldExprLength = rhsEnd - lhsStartPos;
|
|
489
|
+
clauseResult = clauseResult.slice(0, lhsStartPos + clauseOffset) + newExpr + clauseResult.slice(lhsStartPos + oldExprLength + clauseOffset);
|
|
490
|
+
clauseOffset += newExpr.length - oldExprLength;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (clauseResult !== originalOnClause) {
|
|
494
|
+
result = result.slice(0, join.onStart + offset) + clauseResult + result.slice(join.onEnd + offset);
|
|
495
|
+
offset += clauseResult.length - originalOnClause.length;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return result;
|
|
499
|
+
}
|
|
175
500
|
|
|
176
501
|
// src/sql/result-mapper.ts
|
|
177
502
|
import {
|
|
@@ -524,6 +849,7 @@ function wrapperToNodeApiValue(wrapper, toValue) {
|
|
|
524
849
|
function isPool(client) {
|
|
525
850
|
return typeof client.acquire === "function";
|
|
526
851
|
}
|
|
852
|
+
var PREPARED_CACHE = Symbol.for("drizzle-duckdb:prepared-cache");
|
|
527
853
|
function isPgArrayLiteral(value) {
|
|
528
854
|
return value.startsWith("{") && value.endsWith("}");
|
|
529
855
|
}
|
|
@@ -537,16 +863,20 @@ function parsePgArrayLiteral(value) {
|
|
|
537
863
|
}
|
|
538
864
|
function prepareParams(params, options = {}) {
|
|
539
865
|
return params.map((param) => {
|
|
540
|
-
if (typeof param === "string") {
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
866
|
+
if (typeof param === "string" && param.length > 0) {
|
|
867
|
+
const firstChar = param[0];
|
|
868
|
+
const maybeArrayLiteral = firstChar === "{" || firstChar === "[" || firstChar === " " || firstChar === "\t";
|
|
869
|
+
if (maybeArrayLiteral) {
|
|
870
|
+
const trimmed = firstChar === "{" || firstChar === "[" ? param : param.trim();
|
|
871
|
+
if (trimmed && isPgArrayLiteral(trimmed)) {
|
|
872
|
+
if (options.rejectStringArrayLiterals) {
|
|
873
|
+
throw new Error("Stringified array literals are not supported. Use duckDbList()/duckDbArray() or pass native arrays.");
|
|
874
|
+
}
|
|
875
|
+
if (options.warnOnStringArrayLiteral) {
|
|
876
|
+
options.warnOnStringArrayLiteral();
|
|
877
|
+
}
|
|
878
|
+
return parsePgArrayLiteral(trimmed);
|
|
548
879
|
}
|
|
549
|
-
return parsePgArrayLiteral(trimmed);
|
|
550
880
|
}
|
|
551
881
|
}
|
|
552
882
|
return param;
|
|
@@ -571,13 +901,121 @@ function toNodeApiValue(value) {
|
|
|
571
901
|
return value;
|
|
572
902
|
}
|
|
573
903
|
function deduplicateColumns(columns) {
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
904
|
+
const counts = new Map;
|
|
905
|
+
let hasDuplicates = false;
|
|
906
|
+
for (const column of columns) {
|
|
907
|
+
const next = (counts.get(column) ?? 0) + 1;
|
|
908
|
+
counts.set(column, next);
|
|
909
|
+
if (next > 1) {
|
|
910
|
+
hasDuplicates = true;
|
|
911
|
+
break;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
if (!hasDuplicates) {
|
|
915
|
+
return columns;
|
|
916
|
+
}
|
|
917
|
+
counts.clear();
|
|
918
|
+
return columns.map((column) => {
|
|
919
|
+
const count = counts.get(column) ?? 0;
|
|
920
|
+
counts.set(column, count + 1);
|
|
921
|
+
return count === 0 ? column : `${column}_${count}`;
|
|
579
922
|
});
|
|
580
923
|
}
|
|
924
|
+
function destroyPreparedStatement(entry) {
|
|
925
|
+
if (!entry)
|
|
926
|
+
return;
|
|
927
|
+
try {
|
|
928
|
+
entry.statement.destroySync();
|
|
929
|
+
} catch {}
|
|
930
|
+
}
|
|
931
|
+
function getPreparedCache(connection, size) {
|
|
932
|
+
const store = connection;
|
|
933
|
+
const existing = store[PREPARED_CACHE];
|
|
934
|
+
if (existing) {
|
|
935
|
+
existing.size = size;
|
|
936
|
+
return existing;
|
|
937
|
+
}
|
|
938
|
+
const cache = { size, entries: new Map };
|
|
939
|
+
store[PREPARED_CACHE] = cache;
|
|
940
|
+
return cache;
|
|
941
|
+
}
|
|
942
|
+
function evictOldest(cache) {
|
|
943
|
+
const oldest = cache.entries.keys().next();
|
|
944
|
+
if (!oldest.done) {
|
|
945
|
+
const key = oldest.value;
|
|
946
|
+
const entry = cache.entries.get(key);
|
|
947
|
+
cache.entries.delete(key);
|
|
948
|
+
destroyPreparedStatement(entry);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
function evictCacheEntry(cache, key) {
|
|
952
|
+
const entry = cache.entries.get(key);
|
|
953
|
+
cache.entries.delete(key);
|
|
954
|
+
destroyPreparedStatement(entry);
|
|
955
|
+
}
|
|
956
|
+
async function getOrPrepareStatement(connection, query, cacheConfig) {
|
|
957
|
+
const cache = getPreparedCache(connection, cacheConfig.size);
|
|
958
|
+
const cached = cache.entries.get(query);
|
|
959
|
+
if (cached) {
|
|
960
|
+
cache.entries.delete(query);
|
|
961
|
+
cache.entries.set(query, cached);
|
|
962
|
+
return cached.statement;
|
|
963
|
+
}
|
|
964
|
+
const statement = await connection.prepare(query);
|
|
965
|
+
cache.entries.set(query, { statement });
|
|
966
|
+
while (cache.entries.size > cache.size) {
|
|
967
|
+
evictOldest(cache);
|
|
968
|
+
}
|
|
969
|
+
return statement;
|
|
970
|
+
}
|
|
971
|
+
async function materializeResultRows(result) {
|
|
972
|
+
const rows = await result.getRowsJS() ?? [];
|
|
973
|
+
const baseColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
|
|
974
|
+
const columns = typeof result.deduplicatedColumnNames === "function" ? baseColumns : deduplicateColumns(baseColumns);
|
|
975
|
+
return { columns, rows };
|
|
976
|
+
}
|
|
977
|
+
async function materializeRows(client, query, params, options = {}) {
|
|
978
|
+
if (isPool(client)) {
|
|
979
|
+
const connection2 = await client.acquire();
|
|
980
|
+
try {
|
|
981
|
+
return await materializeRows(connection2, query, params, options);
|
|
982
|
+
} finally {
|
|
983
|
+
await client.release(connection2);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
987
|
+
const connection = client;
|
|
988
|
+
if (options.prepareCache && typeof connection.prepare === "function") {
|
|
989
|
+
const cache = getPreparedCache(connection, options.prepareCache.size);
|
|
990
|
+
try {
|
|
991
|
+
const statement = await getOrPrepareStatement(connection, query, options.prepareCache);
|
|
992
|
+
if (values) {
|
|
993
|
+
statement.bind(values);
|
|
994
|
+
} else {
|
|
995
|
+
statement.clearBindings?.();
|
|
996
|
+
}
|
|
997
|
+
const result2 = await statement.run();
|
|
998
|
+
cache.entries.delete(query);
|
|
999
|
+
cache.entries.set(query, { statement });
|
|
1000
|
+
return await materializeResultRows(result2);
|
|
1001
|
+
} catch (error) {
|
|
1002
|
+
evictCacheEntry(cache, query);
|
|
1003
|
+
throw error;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
const result = await connection.run(query, values);
|
|
1007
|
+
return await materializeResultRows(result);
|
|
1008
|
+
}
|
|
1009
|
+
function clearPreparedCache(connection) {
|
|
1010
|
+
const store = connection;
|
|
1011
|
+
const cache = store[PREPARED_CACHE];
|
|
1012
|
+
if (!cache)
|
|
1013
|
+
return;
|
|
1014
|
+
for (const entry of cache.entries.values()) {
|
|
1015
|
+
destroyPreparedStatement(entry);
|
|
1016
|
+
}
|
|
1017
|
+
cache.entries.clear();
|
|
1018
|
+
}
|
|
581
1019
|
function mapRowsToObjects(columns, rows) {
|
|
582
1020
|
return rows.map((vals) => {
|
|
583
1021
|
const obj = {};
|
|
@@ -588,6 +1026,7 @@ function mapRowsToObjects(columns, rows) {
|
|
|
588
1026
|
});
|
|
589
1027
|
}
|
|
590
1028
|
async function closeClientConnection(connection) {
|
|
1029
|
+
clearPreparedCache(connection);
|
|
591
1030
|
if ("close" in connection && typeof connection.close === "function") {
|
|
592
1031
|
await connection.close();
|
|
593
1032
|
return;
|
|
@@ -600,27 +1039,51 @@ async function closeClientConnection(connection) {
|
|
|
600
1039
|
connection.disconnectSync();
|
|
601
1040
|
}
|
|
602
1041
|
}
|
|
603
|
-
async function executeOnClient(client, query, params) {
|
|
1042
|
+
async function executeOnClient(client, query, params, options = {}) {
|
|
1043
|
+
const { columns, rows } = await materializeRows(client, query, params, options);
|
|
1044
|
+
if (!rows || rows.length === 0) {
|
|
1045
|
+
return [];
|
|
1046
|
+
}
|
|
1047
|
+
return mapRowsToObjects(columns, rows);
|
|
1048
|
+
}
|
|
1049
|
+
async function executeArraysOnClient(client, query, params, options = {}) {
|
|
1050
|
+
return await materializeRows(client, query, params, options);
|
|
1051
|
+
}
|
|
1052
|
+
async function* executeInBatches(client, query, params, options = {}) {
|
|
604
1053
|
if (isPool(client)) {
|
|
605
1054
|
const connection = await client.acquire();
|
|
606
1055
|
try {
|
|
607
|
-
|
|
1056
|
+
yield* executeInBatches(connection, query, params, options);
|
|
1057
|
+
return;
|
|
608
1058
|
} finally {
|
|
609
1059
|
await client.release(connection);
|
|
610
1060
|
}
|
|
611
1061
|
}
|
|
1062
|
+
const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
|
|
612
1063
|
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
613
|
-
const result = await client.
|
|
614
|
-
const
|
|
615
|
-
const columns = result.deduplicatedColumnNames
|
|
616
|
-
|
|
617
|
-
|
|
1064
|
+
const result = await client.stream(query, values);
|
|
1065
|
+
const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
|
|
1066
|
+
const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
|
|
1067
|
+
let buffer = [];
|
|
1068
|
+
for await (const chunk of result.yieldRowsJs()) {
|
|
1069
|
+
const objects = mapRowsToObjects(columns, chunk);
|
|
1070
|
+
for (const row of objects) {
|
|
1071
|
+
buffer.push(row);
|
|
1072
|
+
if (buffer.length >= rowsPerChunk) {
|
|
1073
|
+
yield buffer;
|
|
1074
|
+
buffer = [];
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
if (buffer.length > 0) {
|
|
1079
|
+
yield buffer;
|
|
1080
|
+
}
|
|
618
1081
|
}
|
|
619
|
-
async function*
|
|
1082
|
+
async function* executeInBatchesRaw(client, query, params, options = {}) {
|
|
620
1083
|
if (isPool(client)) {
|
|
621
1084
|
const connection = await client.acquire();
|
|
622
1085
|
try {
|
|
623
|
-
yield*
|
|
1086
|
+
yield* executeInBatchesRaw(connection, query, params, options);
|
|
624
1087
|
return;
|
|
625
1088
|
} finally {
|
|
626
1089
|
await client.release(connection);
|
|
@@ -629,21 +1092,20 @@ async function* executeInBatches(client, query, params, options = {}) {
|
|
|
629
1092
|
const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
|
|
630
1093
|
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
631
1094
|
const result = await client.stream(query, values);
|
|
632
|
-
const
|
|
633
|
-
const
|
|
1095
|
+
const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
|
|
1096
|
+
const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
|
|
634
1097
|
let buffer = [];
|
|
635
1098
|
for await (const chunk of result.yieldRowsJs()) {
|
|
636
|
-
const
|
|
637
|
-
for (const row of objects) {
|
|
1099
|
+
for (const row of chunk) {
|
|
638
1100
|
buffer.push(row);
|
|
639
1101
|
if (buffer.length >= rowsPerChunk) {
|
|
640
|
-
yield buffer;
|
|
1102
|
+
yield { columns, rows: buffer };
|
|
641
1103
|
buffer = [];
|
|
642
1104
|
}
|
|
643
1105
|
}
|
|
644
1106
|
}
|
|
645
1107
|
if (buffer.length > 0) {
|
|
646
|
-
yield buffer;
|
|
1108
|
+
yield { columns, rows: buffer };
|
|
647
1109
|
}
|
|
648
1110
|
}
|
|
649
1111
|
async function executeArrowOnClient(client, query, params) {
|
|
@@ -671,6 +1133,24 @@ function isSavepointSyntaxError(error) {
|
|
|
671
1133
|
}
|
|
672
1134
|
return error.message.toLowerCase().includes("savepoint") && error.message.toLowerCase().includes("syntax error");
|
|
673
1135
|
}
|
|
1136
|
+
function rewriteQuery(mode, query) {
|
|
1137
|
+
if (mode === "never") {
|
|
1138
|
+
return { sql: query, rewritten: false };
|
|
1139
|
+
}
|
|
1140
|
+
let result = query;
|
|
1141
|
+
let wasRewritten = false;
|
|
1142
|
+
const arrayRewritten = adaptArrayOperators(result);
|
|
1143
|
+
if (arrayRewritten !== result) {
|
|
1144
|
+
result = arrayRewritten;
|
|
1145
|
+
wasRewritten = true;
|
|
1146
|
+
}
|
|
1147
|
+
const joinQualified = qualifyJoinColumns(result);
|
|
1148
|
+
if (joinQualified !== result) {
|
|
1149
|
+
result = joinQualified;
|
|
1150
|
+
wasRewritten = true;
|
|
1151
|
+
}
|
|
1152
|
+
return { sql: result, rewritten: wasRewritten };
|
|
1153
|
+
}
|
|
674
1154
|
|
|
675
1155
|
class DuckDBPreparedQuery extends PgPreparedQuery {
|
|
676
1156
|
client;
|
|
@@ -681,11 +1161,12 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
|
|
|
681
1161
|
fields;
|
|
682
1162
|
_isResponseInArrayMode;
|
|
683
1163
|
customResultMapper;
|
|
684
|
-
|
|
1164
|
+
rewriteArraysMode;
|
|
685
1165
|
rejectStringArrayLiterals;
|
|
1166
|
+
prepareCache;
|
|
686
1167
|
warnOnStringArrayLiteral;
|
|
687
1168
|
static [entityKind] = "DuckDBPreparedQuery";
|
|
688
|
-
constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper,
|
|
1169
|
+
constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rewriteArraysMode, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
|
|
689
1170
|
super({ sql: queryString, params });
|
|
690
1171
|
this.client = client;
|
|
691
1172
|
this.dialect = dialect;
|
|
@@ -695,8 +1176,9 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
|
|
|
695
1176
|
this.fields = fields;
|
|
696
1177
|
this._isResponseInArrayMode = _isResponseInArrayMode;
|
|
697
1178
|
this.customResultMapper = customResultMapper;
|
|
698
|
-
this.
|
|
1179
|
+
this.rewriteArraysMode = rewriteArraysMode;
|
|
699
1180
|
this.rejectStringArrayLiterals = rejectStringArrayLiterals;
|
|
1181
|
+
this.prepareCache = prepareCache;
|
|
700
1182
|
this.warnOnStringArrayLiteral = warnOnStringArrayLiteral;
|
|
701
1183
|
}
|
|
702
1184
|
async execute(placeholderValues = {}) {
|
|
@@ -705,18 +1187,23 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
|
|
|
705
1187
|
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
706
1188
|
warnOnStringArrayLiteral: this.warnOnStringArrayLiteral ? () => this.warnOnStringArrayLiteral?.(this.queryString) : undefined
|
|
707
1189
|
});
|
|
708
|
-
const rewrittenQuery
|
|
709
|
-
if (
|
|
1190
|
+
const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, this.queryString);
|
|
1191
|
+
if (didRewrite) {
|
|
710
1192
|
this.logger.logQuery(`[duckdb] original query before array rewrite: ${this.queryString}`, params);
|
|
711
1193
|
}
|
|
712
1194
|
this.logger.logQuery(rewrittenQuery, params);
|
|
713
1195
|
const { fields, joinsNotNullableMap, customResultMapper } = this;
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
1196
|
+
if (fields) {
|
|
1197
|
+
const { rows: rows2 } = await executeArraysOnClient(this.client, rewrittenQuery, params, { prepareCache: this.prepareCache });
|
|
1198
|
+
if (rows2.length === 0) {
|
|
1199
|
+
return [];
|
|
1200
|
+
}
|
|
1201
|
+
return customResultMapper ? customResultMapper(rows2) : rows2.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
|
|
717
1202
|
}
|
|
718
|
-
const
|
|
719
|
-
|
|
1203
|
+
const rows = await executeOnClient(this.client, rewrittenQuery, params, {
|
|
1204
|
+
prepareCache: this.prepareCache
|
|
1205
|
+
});
|
|
1206
|
+
return rows;
|
|
720
1207
|
}
|
|
721
1208
|
all(placeholderValues = {}) {
|
|
722
1209
|
return this.execute(placeholderValues);
|
|
@@ -733,8 +1220,9 @@ class DuckDBSession extends PgSession {
|
|
|
733
1220
|
static [entityKind] = "DuckDBSession";
|
|
734
1221
|
dialect;
|
|
735
1222
|
logger;
|
|
736
|
-
|
|
1223
|
+
rewriteArraysMode;
|
|
737
1224
|
rejectStringArrayLiterals;
|
|
1225
|
+
prepareCache;
|
|
738
1226
|
hasWarnedArrayLiteral = false;
|
|
739
1227
|
rollbackOnly = false;
|
|
740
1228
|
constructor(client, dialect, schema, options = {}) {
|
|
@@ -744,11 +1232,17 @@ class DuckDBSession extends PgSession {
|
|
|
744
1232
|
this.options = options;
|
|
745
1233
|
this.dialect = dialect;
|
|
746
1234
|
this.logger = options.logger ?? new NoopLogger;
|
|
747
|
-
this.
|
|
1235
|
+
this.rewriteArraysMode = options.rewriteArrays ?? "auto";
|
|
748
1236
|
this.rejectStringArrayLiterals = options.rejectStringArrayLiterals ?? false;
|
|
1237
|
+
this.prepareCache = options.prepareCache;
|
|
1238
|
+
this.options = {
|
|
1239
|
+
...options,
|
|
1240
|
+
rewriteArrays: this.rewriteArraysMode,
|
|
1241
|
+
prepareCache: this.prepareCache
|
|
1242
|
+
};
|
|
749
1243
|
}
|
|
750
1244
|
prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
|
|
751
|
-
return new DuckDBPreparedQuery(this.client, this.dialect, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, this.
|
|
1245
|
+
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);
|
|
752
1246
|
}
|
|
753
1247
|
execute(query) {
|
|
754
1248
|
this.dialect.resetPgJsonFlag();
|
|
@@ -808,13 +1302,28 @@ query: ${query}`, []);
|
|
|
808
1302
|
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
809
1303
|
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
810
1304
|
});
|
|
811
|
-
const rewrittenQuery = this.
|
|
812
|
-
if (
|
|
1305
|
+
const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
|
|
1306
|
+
if (didRewrite) {
|
|
813
1307
|
this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
|
|
814
1308
|
}
|
|
815
1309
|
this.logger.logQuery(rewrittenQuery, params);
|
|
816
1310
|
return executeInBatches(this.client, rewrittenQuery, params, options);
|
|
817
1311
|
}
|
|
1312
|
+
executeBatchesRaw(query, options = {}) {
|
|
1313
|
+
this.dialect.resetPgJsonFlag();
|
|
1314
|
+
const builtQuery = this.dialect.sqlToQuery(query);
|
|
1315
|
+
this.dialect.assertNoPgJsonColumns();
|
|
1316
|
+
const params = prepareParams(builtQuery.params, {
|
|
1317
|
+
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
1318
|
+
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
1319
|
+
});
|
|
1320
|
+
const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
|
|
1321
|
+
if (didRewrite) {
|
|
1322
|
+
this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
|
|
1323
|
+
}
|
|
1324
|
+
this.logger.logQuery(rewrittenQuery, params);
|
|
1325
|
+
return executeInBatchesRaw(this.client, rewrittenQuery, params, options);
|
|
1326
|
+
}
|
|
818
1327
|
async executeArrow(query) {
|
|
819
1328
|
this.dialect.resetPgJsonFlag();
|
|
820
1329
|
const builtQuery = this.dialect.sqlToQuery(query);
|
|
@@ -823,8 +1332,8 @@ query: ${query}`, []);
|
|
|
823
1332
|
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
824
1333
|
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
825
1334
|
});
|
|
826
|
-
const rewrittenQuery = this.
|
|
827
|
-
if (
|
|
1335
|
+
const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
|
|
1336
|
+
if (didRewrite) {
|
|
828
1337
|
this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
|
|
829
1338
|
}
|
|
830
1339
|
this.logger.logQuery(rewrittenQuery, params);
|
|
@@ -862,6 +1371,9 @@ class DuckDBTransaction extends PgTransaction {
|
|
|
862
1371
|
executeBatches(query, options = {}) {
|
|
863
1372
|
return this.session.executeBatches(query, options);
|
|
864
1373
|
}
|
|
1374
|
+
executeBatchesRaw(query, options = {}) {
|
|
1375
|
+
return this.session.executeBatchesRaw(query, options);
|
|
1376
|
+
}
|
|
865
1377
|
executeArrow(query) {
|
|
866
1378
|
return this.session.executeArrow(query);
|
|
867
1379
|
}
|
|
@@ -1273,6 +1785,32 @@ function createDuckDBConnectionPool(instance, options = {}) {
|
|
|
1273
1785
|
};
|
|
1274
1786
|
}
|
|
1275
1787
|
|
|
1788
|
+
// src/options.ts
|
|
1789
|
+
var DEFAULT_REWRITE_ARRAYS_MODE = "auto";
|
|
1790
|
+
function resolveRewriteArraysOption(value) {
|
|
1791
|
+
if (value === undefined)
|
|
1792
|
+
return DEFAULT_REWRITE_ARRAYS_MODE;
|
|
1793
|
+
if (value === true)
|
|
1794
|
+
return "auto";
|
|
1795
|
+
if (value === false)
|
|
1796
|
+
return "never";
|
|
1797
|
+
return value;
|
|
1798
|
+
}
|
|
1799
|
+
var DEFAULT_PREPARED_CACHE_SIZE = 32;
|
|
1800
|
+
function resolvePrepareCacheOption(option) {
|
|
1801
|
+
if (!option)
|
|
1802
|
+
return;
|
|
1803
|
+
if (option === true) {
|
|
1804
|
+
return { size: DEFAULT_PREPARED_CACHE_SIZE };
|
|
1805
|
+
}
|
|
1806
|
+
if (typeof option === "number") {
|
|
1807
|
+
const size2 = Math.max(1, Math.floor(option));
|
|
1808
|
+
return { size: size2 };
|
|
1809
|
+
}
|
|
1810
|
+
const size = option.size ?? DEFAULT_PREPARED_CACHE_SIZE;
|
|
1811
|
+
return { size: Math.max(1, Math.floor(size)) };
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1276
1814
|
// src/driver.ts
|
|
1277
1815
|
class DuckDBDriver {
|
|
1278
1816
|
client;
|
|
@@ -1287,8 +1825,9 @@ class DuckDBDriver {
|
|
|
1287
1825
|
createSession(schema) {
|
|
1288
1826
|
return new DuckDBSession(this.client, this.dialect, schema, {
|
|
1289
1827
|
logger: this.options.logger,
|
|
1290
|
-
rewriteArrays: this.options.rewriteArrays,
|
|
1291
|
-
rejectStringArrayLiterals: this.options.rejectStringArrayLiterals
|
|
1828
|
+
rewriteArrays: this.options.rewriteArrays ?? "auto",
|
|
1829
|
+
rejectStringArrayLiterals: this.options.rejectStringArrayLiterals,
|
|
1830
|
+
prepareCache: this.options.prepareCache
|
|
1292
1831
|
});
|
|
1293
1832
|
}
|
|
1294
1833
|
}
|
|
@@ -1301,6 +1840,8 @@ function isConfigObject(data) {
|
|
|
1301
1840
|
}
|
|
1302
1841
|
function createFromClient(client, config = {}, instance) {
|
|
1303
1842
|
const dialect = new DuckDBDialect;
|
|
1843
|
+
const rewriteArraysMode = resolveRewriteArraysOption(config.rewriteArrays);
|
|
1844
|
+
const prepareCache = resolvePrepareCacheOption(config.prepareCache);
|
|
1304
1845
|
const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
|
|
1305
1846
|
let schema;
|
|
1306
1847
|
if (config.schema) {
|
|
@@ -1313,8 +1854,9 @@ function createFromClient(client, config = {}, instance) {
|
|
|
1313
1854
|
}
|
|
1314
1855
|
const driver = new DuckDBDriver(client, dialect, {
|
|
1315
1856
|
logger,
|
|
1316
|
-
rewriteArrays:
|
|
1317
|
-
rejectStringArrayLiterals: config.rejectStringArrayLiterals
|
|
1857
|
+
rewriteArrays: rewriteArraysMode,
|
|
1858
|
+
rejectStringArrayLiterals: config.rejectStringArrayLiterals,
|
|
1859
|
+
prepareCache
|
|
1318
1860
|
});
|
|
1319
1861
|
const session = driver.createSession(schema);
|
|
1320
1862
|
const db = new DuckDBDatabase(dialect, session, schema, client, instance);
|
|
@@ -1394,6 +1936,9 @@ class DuckDBDatabase extends PgDatabase {
|
|
|
1394
1936
|
executeBatches(query, options = {}) {
|
|
1395
1937
|
return this.session.executeBatches(query, options);
|
|
1396
1938
|
}
|
|
1939
|
+
executeBatchesRaw(query, options = {}) {
|
|
1940
|
+
return this.session.executeBatchesRaw(query, options);
|
|
1941
|
+
}
|
|
1397
1942
|
executeArrow(query) {
|
|
1398
1943
|
return this.session.executeArrow(query);
|
|
1399
1944
|
}
|
|
@@ -1594,6 +2139,21 @@ var duckDbInterval = (name) => customType({
|
|
|
1594
2139
|
return value;
|
|
1595
2140
|
}
|
|
1596
2141
|
})(name);
|
|
2142
|
+
function shouldBindTimestamp(options) {
|
|
2143
|
+
const bindMode = options.bindMode ?? "auto";
|
|
2144
|
+
if (bindMode === "bind")
|
|
2145
|
+
return true;
|
|
2146
|
+
if (bindMode === "literal")
|
|
2147
|
+
return false;
|
|
2148
|
+
const isBun = typeof process !== "undefined" && typeof process.versions?.bun !== "undefined";
|
|
2149
|
+
if (isBun)
|
|
2150
|
+
return false;
|
|
2151
|
+
const forceLiteral = typeof process !== "undefined" ? process.env.DRIZZLE_DUCKDB_FORCE_LITERAL_TIMESTAMPS : undefined;
|
|
2152
|
+
if (forceLiteral && forceLiteral !== "0") {
|
|
2153
|
+
return false;
|
|
2154
|
+
}
|
|
2155
|
+
return true;
|
|
2156
|
+
}
|
|
1597
2157
|
var duckDbTimestamp = (name, options = {}) => customType({
|
|
1598
2158
|
dataType() {
|
|
1599
2159
|
if (options.withTimezone) {
|
|
@@ -1603,12 +2163,19 @@ var duckDbTimestamp = (name, options = {}) => customType({
|
|
|
1603
2163
|
return `TIMESTAMP${precision}`;
|
|
1604
2164
|
},
|
|
1605
2165
|
toDriver(value) {
|
|
2166
|
+
if (shouldBindTimestamp(options)) {
|
|
2167
|
+
return wrapTimestamp(value, options.withTimezone ?? false, options.precision);
|
|
2168
|
+
}
|
|
1606
2169
|
const iso = value instanceof Date ? value.toISOString() : value;
|
|
1607
2170
|
const normalized = iso.replace("T", " ").replace("Z", "+00");
|
|
1608
2171
|
const typeKeyword = options.withTimezone ? "TIMESTAMPTZ" : "TIMESTAMP";
|
|
1609
2172
|
return sql4.raw(`${typeKeyword} '${normalized}'`);
|
|
1610
2173
|
},
|
|
1611
2174
|
fromDriver(value) {
|
|
2175
|
+
if (value && typeof value === "object" && "kind" in value && value.kind === "timestamp") {
|
|
2176
|
+
const wrapped = value;
|
|
2177
|
+
return wrapped.data instanceof Date ? wrapped.data : typeof wrapped.data === "number" || typeof wrapped.data === "bigint" ? new Date(Number(wrapped.data) / 1000) : wrapped.data;
|
|
2178
|
+
}
|
|
1612
2179
|
if (options.mode === "string") {
|
|
1613
2180
|
if (value instanceof Date) {
|
|
1614
2181
|
return value.toISOString().replace("T", " ").replace("Z", "+00");
|
|
@@ -2364,6 +2931,8 @@ export {
|
|
|
2364
2931
|
sumDistinctN,
|
|
2365
2932
|
splitTopLevel,
|
|
2366
2933
|
rowNumber,
|
|
2934
|
+
resolveRewriteArraysOption,
|
|
2935
|
+
resolvePrepareCacheOption,
|
|
2367
2936
|
resolvePoolSize,
|
|
2368
2937
|
rank,
|
|
2369
2938
|
prepareParams,
|
|
@@ -2380,8 +2949,10 @@ export {
|
|
|
2380
2949
|
introspect,
|
|
2381
2950
|
formatLiteral,
|
|
2382
2951
|
executeOnClient,
|
|
2952
|
+
executeInBatchesRaw,
|
|
2383
2953
|
executeInBatches,
|
|
2384
2954
|
executeArrowOnClient,
|
|
2955
|
+
executeArraysOnClient,
|
|
2385
2956
|
duckDbTimestamp,
|
|
2386
2957
|
duckDbTime,
|
|
2387
2958
|
duckDbStruct,
|