@leonardovida-md/drizzle-neo-duckdb 1.0.3 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -5
- package/dist/client.d.ts +7 -1
- package/dist/columns.d.ts +6 -1
- package/dist/dialect.d.ts +21 -0
- package/dist/driver.d.ts +33 -1
- package/dist/duckdb-introspect.mjs +610 -114
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.mjs +319 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +603 -117
- package/dist/introspect.d.ts +9 -0
- package/dist/pool.d.ts +30 -0
- package/dist/session.d.ts +7 -1
- package/dist/sql/query-rewriters.d.ts +1 -1
- package/dist/sql/result-mapper.d.ts +7 -0
- package/dist/utils.d.ts +1 -1
- package/dist/value-wrappers-core.d.ts +42 -0
- package/dist/value-wrappers.d.ts +2 -98
- package/package.json +6 -2
- package/src/bin/duckdb-introspect.ts +27 -0
- package/src/client.ts +54 -13
- package/src/columns.ts +10 -10
- package/src/dialect.ts +51 -3
- package/src/driver.ts +204 -7
- package/src/helpers.ts +18 -0
- package/src/index.ts +1 -0
- package/src/introspect.ts +47 -29
- package/src/migrator.ts +1 -1
- package/src/olap.ts +1 -0
- package/src/pool.ts +274 -0
- package/src/session.ts +134 -15
- package/src/sql/query-rewriters.ts +177 -116
- package/src/sql/result-mapper.ts +7 -7
- package/src/utils.ts +1 -1
- package/src/value-wrappers-core.ts +156 -0
- package/src/value-wrappers.ts +60 -219
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/driver.ts
|
|
2
|
+
import { DuckDBInstance as DuckDBInstance2 } from "@duckdb/node-api";
|
|
2
3
|
import { entityKind as entityKind3 } from "drizzle-orm/entity";
|
|
3
4
|
import { DefaultLogger } from "drizzle-orm/logger";
|
|
4
5
|
import { PgDatabase } from "drizzle-orm/pg-core/db";
|
|
@@ -15,83 +16,159 @@ import { PgPreparedQuery, PgSession } from "drizzle-orm/pg-core/session";
|
|
|
15
16
|
import { fillPlaceholders, sql } from "drizzle-orm/sql/sql";
|
|
16
17
|
|
|
17
18
|
// src/sql/query-rewriters.ts
|
|
18
|
-
var
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
let inString = false;
|
|
36
|
-
for (;idx >= 0; idx--) {
|
|
37
|
-
const ch = source[idx];
|
|
38
|
-
if (ch === "'" && source[idx - 1] !== "\\") {
|
|
39
|
-
inString = !inString;
|
|
19
|
+
var OPERATORS = [
|
|
20
|
+
{ token: "@>", fn: "array_has_all" },
|
|
21
|
+
{ token: "<@", fn: "array_has_all", swap: true },
|
|
22
|
+
{ token: "&&", fn: "array_has_any" }
|
|
23
|
+
];
|
|
24
|
+
var isWhitespace = (char) => char !== undefined && /\s/.test(char);
|
|
25
|
+
function scrubForRewrite(query) {
|
|
26
|
+
let scrubbed = "";
|
|
27
|
+
let state = "code";
|
|
28
|
+
for (let i = 0;i < query.length; i += 1) {
|
|
29
|
+
const char = query[i];
|
|
30
|
+
const next = query[i + 1];
|
|
31
|
+
if (state === "code") {
|
|
32
|
+
if (char === "'") {
|
|
33
|
+
scrubbed += "'";
|
|
34
|
+
state = "single";
|
|
35
|
+
continue;
|
|
40
36
|
}
|
|
41
|
-
if (
|
|
37
|
+
if (char === '"') {
|
|
38
|
+
scrubbed += '"';
|
|
39
|
+
state = "double";
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (char === "-" && next === "-") {
|
|
43
|
+
scrubbed += " ";
|
|
44
|
+
i += 1;
|
|
45
|
+
state = "lineComment";
|
|
42
46
|
continue;
|
|
43
|
-
if (ch === ")" || ch === "]") {
|
|
44
|
-
depth++;
|
|
45
|
-
} else if (ch === "(" || ch === "[") {
|
|
46
|
-
depth--;
|
|
47
|
-
if (depth < 0) {
|
|
48
|
-
return [idx + 1, source.slice(idx + 1, start + 1)];
|
|
49
|
-
}
|
|
50
|
-
} else if (depth === 0 && isWhitespace(ch)) {
|
|
51
|
-
return [idx + 1, source.slice(idx + 1, start + 1)];
|
|
52
47
|
}
|
|
48
|
+
if (char === "/" && next === "*") {
|
|
49
|
+
scrubbed += " ";
|
|
50
|
+
i += 1;
|
|
51
|
+
state = "blockComment";
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
scrubbed += char;
|
|
55
|
+
continue;
|
|
53
56
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
for (;idx < source.length; idx++) {
|
|
64
|
-
const ch = source[idx];
|
|
65
|
-
if (ch === "'" && source[idx - 1] !== "\\") {
|
|
66
|
-
inString = !inString;
|
|
57
|
+
if (state === "single") {
|
|
58
|
+
if (char === "'" && next === "'") {
|
|
59
|
+
scrubbed += "''";
|
|
60
|
+
i += 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
scrubbed += char === "'" ? "'" : ".";
|
|
64
|
+
if (char === "'") {
|
|
65
|
+
state = "code";
|
|
67
66
|
}
|
|
68
|
-
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (state === "double") {
|
|
70
|
+
if (char === '"' && next === '"') {
|
|
71
|
+
scrubbed += '""';
|
|
72
|
+
i += 1;
|
|
69
73
|
continue;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
}
|
|
75
|
+
scrubbed += char === '"' ? '"' : ".";
|
|
76
|
+
if (char === '"') {
|
|
77
|
+
state = "code";
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (state === "lineComment") {
|
|
82
|
+
scrubbed += char === `
|
|
83
|
+
` ? `
|
|
84
|
+
` : " ";
|
|
85
|
+
if (char === `
|
|
86
|
+
`) {
|
|
87
|
+
state = "code";
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (state === "blockComment") {
|
|
92
|
+
if (char === "*" && next === "/") {
|
|
93
|
+
scrubbed += " ";
|
|
94
|
+
i += 1;
|
|
95
|
+
state = "code";
|
|
96
|
+
} else {
|
|
97
|
+
scrubbed += " ";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return scrubbed;
|
|
102
|
+
}
|
|
103
|
+
function findNextOperator(scrubbed, start) {
|
|
104
|
+
for (let idx = start;idx < scrubbed.length; idx += 1) {
|
|
105
|
+
for (const operator of OPERATORS) {
|
|
106
|
+
if (scrubbed.startsWith(operator.token, idx)) {
|
|
107
|
+
return { index: idx, operator };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
function walkLeft(source, scrubbed, start) {
|
|
114
|
+
let idx = start;
|
|
115
|
+
while (idx >= 0 && isWhitespace(scrubbed[idx])) {
|
|
116
|
+
idx -= 1;
|
|
117
|
+
}
|
|
118
|
+
let depth = 0;
|
|
119
|
+
for (;idx >= 0; idx -= 1) {
|
|
120
|
+
const ch = scrubbed[idx];
|
|
121
|
+
if (ch === ")" || ch === "]") {
|
|
122
|
+
depth += 1;
|
|
123
|
+
} else if (ch === "(" || ch === "[") {
|
|
124
|
+
if (depth === 0) {
|
|
125
|
+
return [idx + 1, source.slice(idx + 1, start + 1)];
|
|
126
|
+
}
|
|
127
|
+
depth = Math.max(0, depth - 1);
|
|
128
|
+
} else if (depth === 0 && isWhitespace(ch)) {
|
|
129
|
+
return [idx + 1, source.slice(idx + 1, start + 1)];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return [0, source.slice(0, start + 1)];
|
|
133
|
+
}
|
|
134
|
+
function walkRight(source, scrubbed, start) {
|
|
135
|
+
let idx = start;
|
|
136
|
+
while (idx < scrubbed.length && isWhitespace(scrubbed[idx])) {
|
|
137
|
+
idx += 1;
|
|
138
|
+
}
|
|
139
|
+
let depth = 0;
|
|
140
|
+
for (;idx < scrubbed.length; idx += 1) {
|
|
141
|
+
const ch = scrubbed[idx];
|
|
142
|
+
if (ch === "(" || ch === "[") {
|
|
143
|
+
depth += 1;
|
|
144
|
+
} else if (ch === ")" || ch === "]") {
|
|
145
|
+
if (depth === 0) {
|
|
78
146
|
return [idx, source.slice(start, idx)];
|
|
79
147
|
}
|
|
148
|
+
depth = Math.max(0, depth - 1);
|
|
149
|
+
} else if (depth === 0 && isWhitespace(ch)) {
|
|
150
|
+
return [idx, source.slice(start, idx)];
|
|
80
151
|
}
|
|
81
|
-
|
|
82
|
-
|
|
152
|
+
}
|
|
153
|
+
return [scrubbed.length, source.slice(start)];
|
|
154
|
+
}
|
|
155
|
+
function adaptArrayOperators(query) {
|
|
83
156
|
let rewritten = query;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
157
|
+
let scrubbed = scrubForRewrite(query);
|
|
158
|
+
let searchStart = 0;
|
|
159
|
+
while (true) {
|
|
160
|
+
const next = findNextOperator(scrubbed, searchStart);
|
|
161
|
+
if (!next)
|
|
162
|
+
break;
|
|
163
|
+
const { index, operator } = next;
|
|
164
|
+
const [leftStart, leftExpr] = walkLeft(rewritten, scrubbed, index - 1);
|
|
165
|
+
const [rightEnd, rightExpr] = walkRight(rewritten, scrubbed, index + operator.token.length);
|
|
166
|
+
const left = leftExpr.trim();
|
|
167
|
+
const right = rightExpr.trim();
|
|
168
|
+
const replacement = `${operator.fn}(${operator.swap ? right : left}, ${operator.swap ? left : right})`;
|
|
169
|
+
rewritten = rewritten.slice(0, leftStart) + replacement + rewritten.slice(rightEnd);
|
|
170
|
+
scrubbed = scrubForRewrite(rewritten);
|
|
171
|
+
searchStart = leftStart + replacement.length;
|
|
95
172
|
}
|
|
96
173
|
return rewritten;
|
|
97
174
|
}
|
|
@@ -317,6 +394,8 @@ import {
|
|
|
317
394
|
timestampValue,
|
|
318
395
|
timestampTZValue
|
|
319
396
|
} from "@duckdb/node-api";
|
|
397
|
+
|
|
398
|
+
// src/value-wrappers-core.ts
|
|
320
399
|
var DUCKDB_VALUE_MARKER = Symbol.for("drizzle-duckdb:value");
|
|
321
400
|
function isDuckDBWrapper(value) {
|
|
322
401
|
return value !== null && typeof value === "object" && DUCKDB_VALUE_MARKER in value && value[DUCKDB_VALUE_MARKER] === true;
|
|
@@ -377,10 +456,18 @@ function wrapJson(data) {
|
|
|
377
456
|
data
|
|
378
457
|
};
|
|
379
458
|
}
|
|
459
|
+
|
|
460
|
+
// src/value-wrappers.ts
|
|
380
461
|
function dateToMicros(value) {
|
|
381
462
|
if (value instanceof Date) {
|
|
382
463
|
return BigInt(value.getTime()) * 1000n;
|
|
383
464
|
}
|
|
465
|
+
if (typeof value === "bigint") {
|
|
466
|
+
return value;
|
|
467
|
+
}
|
|
468
|
+
if (typeof value === "number") {
|
|
469
|
+
return BigInt(Math.trunc(value)) * 1000n;
|
|
470
|
+
}
|
|
384
471
|
let normalized = value;
|
|
385
472
|
if (!value.includes("T") && value.includes(" ")) {
|
|
386
473
|
normalized = value.replace(" ", "T");
|
|
@@ -434,6 +521,9 @@ function wrapperToNodeApiValue(wrapper, toValue) {
|
|
|
434
521
|
}
|
|
435
522
|
|
|
436
523
|
// src/client.ts
|
|
524
|
+
function isPool(client) {
|
|
525
|
+
return typeof client.acquire === "function";
|
|
526
|
+
}
|
|
437
527
|
function isPgArrayLiteral(value) {
|
|
438
528
|
return value.startsWith("{") && value.endsWith("}");
|
|
439
529
|
}
|
|
@@ -445,18 +535,19 @@ function parsePgArrayLiteral(value) {
|
|
|
445
535
|
return value;
|
|
446
536
|
}
|
|
447
537
|
}
|
|
448
|
-
var warnedArrayLiteral = false;
|
|
449
538
|
function prepareParams(params, options = {}) {
|
|
450
539
|
return params.map((param) => {
|
|
451
|
-
if (typeof param === "string"
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
options.warnOnStringArrayLiteral
|
|
540
|
+
if (typeof param === "string") {
|
|
541
|
+
const trimmed = param.trim();
|
|
542
|
+
if (trimmed && isPgArrayLiteral(trimmed)) {
|
|
543
|
+
if (options.rejectStringArrayLiterals) {
|
|
544
|
+
throw new Error("Stringified array literals are not supported. Use duckDbList()/duckDbArray() or pass native arrays.");
|
|
545
|
+
}
|
|
546
|
+
if (options.warnOnStringArrayLiteral) {
|
|
547
|
+
options.warnOnStringArrayLiteral();
|
|
548
|
+
}
|
|
549
|
+
return parsePgArrayLiteral(trimmed);
|
|
458
550
|
}
|
|
459
|
-
return parsePgArrayLiteral(param);
|
|
460
551
|
}
|
|
461
552
|
return param;
|
|
462
553
|
});
|
|
@@ -510,6 +601,14 @@ async function closeClientConnection(connection) {
|
|
|
510
601
|
}
|
|
511
602
|
}
|
|
512
603
|
async function executeOnClient(client, query, params) {
|
|
604
|
+
if (isPool(client)) {
|
|
605
|
+
const connection = await client.acquire();
|
|
606
|
+
try {
|
|
607
|
+
return await executeOnClient(connection, query, params);
|
|
608
|
+
} finally {
|
|
609
|
+
await client.release(connection);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
513
612
|
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
514
613
|
const result = await client.run(query, values);
|
|
515
614
|
const rows = await result.getRowsJS();
|
|
@@ -518,6 +617,15 @@ async function executeOnClient(client, query, params) {
|
|
|
518
617
|
return rows ? mapRowsToObjects(uniqueColumns, rows) : [];
|
|
519
618
|
}
|
|
520
619
|
async function* executeInBatches(client, query, params, options = {}) {
|
|
620
|
+
if (isPool(client)) {
|
|
621
|
+
const connection = await client.acquire();
|
|
622
|
+
try {
|
|
623
|
+
yield* executeInBatches(connection, query, params, options);
|
|
624
|
+
return;
|
|
625
|
+
} finally {
|
|
626
|
+
await client.release(connection);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
521
629
|
const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
|
|
522
630
|
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
523
631
|
const result = await client.stream(query, values);
|
|
@@ -539,6 +647,14 @@ async function* executeInBatches(client, query, params, options = {}) {
|
|
|
539
647
|
}
|
|
540
648
|
}
|
|
541
649
|
async function executeArrowOnClient(client, query, params) {
|
|
650
|
+
if (isPool(client)) {
|
|
651
|
+
const connection = await client.acquire();
|
|
652
|
+
try {
|
|
653
|
+
return await executeArrowOnClient(connection, query, params);
|
|
654
|
+
} finally {
|
|
655
|
+
await client.release(connection);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
542
658
|
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
543
659
|
const result = await client.run(query, values);
|
|
544
660
|
const maybeArrow = result.toArrow ?? result.getArrowTable;
|
|
@@ -549,6 +665,13 @@ async function executeArrowOnClient(client, query, params) {
|
|
|
549
665
|
}
|
|
550
666
|
|
|
551
667
|
// src/session.ts
|
|
668
|
+
function isSavepointSyntaxError(error) {
|
|
669
|
+
if (!(error instanceof Error) || !error.message) {
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
return error.message.toLowerCase().includes("savepoint") && error.message.toLowerCase().includes("syntax error");
|
|
673
|
+
}
|
|
674
|
+
|
|
552
675
|
class DuckDBPreparedQuery extends PgPreparedQuery {
|
|
553
676
|
client;
|
|
554
677
|
dialect;
|
|
@@ -613,6 +736,7 @@ class DuckDBSession extends PgSession {
|
|
|
613
736
|
rewriteArrays;
|
|
614
737
|
rejectStringArrayLiterals;
|
|
615
738
|
hasWarnedArrayLiteral = false;
|
|
739
|
+
rollbackOnly = false;
|
|
616
740
|
constructor(client, dialect, schema, options = {}) {
|
|
617
741
|
super(dialect);
|
|
618
742
|
this.client = client;
|
|
@@ -626,17 +750,46 @@ class DuckDBSession extends PgSession {
|
|
|
626
750
|
prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
|
|
627
751
|
return new DuckDBPreparedQuery(this.client, this.dialect, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, this.rewriteArrays, this.rejectStringArrayLiterals, this.rejectStringArrayLiterals ? undefined : this.warnOnStringArrayLiteral);
|
|
628
752
|
}
|
|
629
|
-
|
|
630
|
-
|
|
753
|
+
execute(query) {
|
|
754
|
+
this.dialect.resetPgJsonFlag();
|
|
755
|
+
return super.execute(query);
|
|
756
|
+
}
|
|
757
|
+
all(query) {
|
|
758
|
+
this.dialect.resetPgJsonFlag();
|
|
759
|
+
return super.all(query);
|
|
760
|
+
}
|
|
761
|
+
async transaction(transaction, config) {
|
|
762
|
+
let pinnedConnection;
|
|
763
|
+
let pool;
|
|
764
|
+
let clientForTx = this.client;
|
|
765
|
+
if (isPool(this.client)) {
|
|
766
|
+
pool = this.client;
|
|
767
|
+
pinnedConnection = await pool.acquire();
|
|
768
|
+
clientForTx = pinnedConnection;
|
|
769
|
+
}
|
|
770
|
+
const session = new DuckDBSession(clientForTx, this.dialect, this.schema, this.options);
|
|
631
771
|
const tx = new DuckDBTransaction(this.dialect, session, this.schema);
|
|
632
|
-
await tx.execute(sql`BEGIN TRANSACTION;`);
|
|
633
772
|
try {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
773
|
+
await tx.execute(sql`BEGIN TRANSACTION;`);
|
|
774
|
+
if (config) {
|
|
775
|
+
await tx.setTransaction(config);
|
|
776
|
+
}
|
|
777
|
+
try {
|
|
778
|
+
const result = await transaction(tx);
|
|
779
|
+
if (session.isRollbackOnly()) {
|
|
780
|
+
await tx.execute(sql`rollback`);
|
|
781
|
+
throw new TransactionRollbackError;
|
|
782
|
+
}
|
|
783
|
+
await tx.execute(sql`commit`);
|
|
784
|
+
return result;
|
|
785
|
+
} catch (error) {
|
|
786
|
+
await tx.execute(sql`rollback`);
|
|
787
|
+
throw error;
|
|
788
|
+
}
|
|
789
|
+
} finally {
|
|
790
|
+
if (pinnedConnection && pool) {
|
|
791
|
+
await pool.release(pinnedConnection);
|
|
792
|
+
}
|
|
640
793
|
}
|
|
641
794
|
}
|
|
642
795
|
warnOnStringArrayLiteral = (query) => {
|
|
@@ -648,7 +801,9 @@ class DuckDBSession extends PgSession {
|
|
|
648
801
|
query: ${query}`, []);
|
|
649
802
|
};
|
|
650
803
|
executeBatches(query, options = {}) {
|
|
804
|
+
this.dialect.resetPgJsonFlag();
|
|
651
805
|
const builtQuery = this.dialect.sqlToQuery(query);
|
|
806
|
+
this.dialect.assertNoPgJsonColumns();
|
|
652
807
|
const params = prepareParams(builtQuery.params, {
|
|
653
808
|
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
654
809
|
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
@@ -661,7 +816,9 @@ query: ${query}`, []);
|
|
|
661
816
|
return executeInBatches(this.client, rewrittenQuery, params, options);
|
|
662
817
|
}
|
|
663
818
|
async executeArrow(query) {
|
|
819
|
+
this.dialect.resetPgJsonFlag();
|
|
664
820
|
const builtQuery = this.dialect.sqlToQuery(query);
|
|
821
|
+
this.dialect.assertNoPgJsonColumns();
|
|
665
822
|
const params = prepareParams(builtQuery.params, {
|
|
666
823
|
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
667
824
|
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
@@ -673,6 +830,12 @@ query: ${query}`, []);
|
|
|
673
830
|
this.logger.logQuery(rewrittenQuery, params);
|
|
674
831
|
return executeArrowOnClient(this.client, rewrittenQuery, params);
|
|
675
832
|
}
|
|
833
|
+
markRollbackOnly() {
|
|
834
|
+
this.rollbackOnly = true;
|
|
835
|
+
}
|
|
836
|
+
isRollbackOnly() {
|
|
837
|
+
return this.rollbackOnly;
|
|
838
|
+
}
|
|
676
839
|
}
|
|
677
840
|
|
|
678
841
|
class DuckDBTransaction extends PgTransaction {
|
|
@@ -703,8 +866,46 @@ class DuckDBTransaction extends PgTransaction {
|
|
|
703
866
|
return this.session.executeArrow(query);
|
|
704
867
|
}
|
|
705
868
|
async transaction(transaction) {
|
|
706
|
-
const
|
|
707
|
-
|
|
869
|
+
const internals = this;
|
|
870
|
+
const savepoint = `drizzle_savepoint_${this.nestedIndex + 1}`;
|
|
871
|
+
const savepointSql = sql.raw(`savepoint ${savepoint}`);
|
|
872
|
+
const releaseSql = sql.raw(`release savepoint ${savepoint}`);
|
|
873
|
+
const rollbackSql = sql.raw(`rollback to savepoint ${savepoint}`);
|
|
874
|
+
const nestedTx = new DuckDBTransaction(internals.dialect, internals.session, this.schema, this.nestedIndex + 1);
|
|
875
|
+
if (internals.dialect.areSavepointsUnsupported()) {
|
|
876
|
+
return this.runNestedWithoutSavepoint(transaction, nestedTx, internals);
|
|
877
|
+
}
|
|
878
|
+
let createdSavepoint = false;
|
|
879
|
+
try {
|
|
880
|
+
await internals.session.execute(savepointSql);
|
|
881
|
+
internals.dialect.markSavepointsSupported();
|
|
882
|
+
createdSavepoint = true;
|
|
883
|
+
} catch (error) {
|
|
884
|
+
if (!isSavepointSyntaxError(error)) {
|
|
885
|
+
throw error;
|
|
886
|
+
}
|
|
887
|
+
internals.dialect.markSavepointsUnsupported();
|
|
888
|
+
return this.runNestedWithoutSavepoint(transaction, nestedTx, internals);
|
|
889
|
+
}
|
|
890
|
+
try {
|
|
891
|
+
const result = await transaction(nestedTx);
|
|
892
|
+
if (createdSavepoint) {
|
|
893
|
+
await internals.session.execute(releaseSql);
|
|
894
|
+
}
|
|
895
|
+
return result;
|
|
896
|
+
} catch (error) {
|
|
897
|
+
if (createdSavepoint) {
|
|
898
|
+
await internals.session.execute(rollbackSql);
|
|
899
|
+
}
|
|
900
|
+
internals.session.markRollbackOnly();
|
|
901
|
+
throw error;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
runNestedWithoutSavepoint(transaction, nestedTx, internals) {
|
|
905
|
+
return transaction(nestedTx).catch((error) => {
|
|
906
|
+
internals.session.markRollbackOnly();
|
|
907
|
+
throw error;
|
|
908
|
+
});
|
|
708
909
|
}
|
|
709
910
|
}
|
|
710
911
|
var arrayLiteralWarning = "Received a stringified Postgres-style array literal. Use duckDbList()/duckDbArray() or pass native arrays instead. You can also set rejectStringArrayLiterals=true to throw.";
|
|
@@ -726,15 +927,30 @@ import {
|
|
|
726
927
|
import {
|
|
727
928
|
sql as sql2
|
|
728
929
|
} from "drizzle-orm";
|
|
729
|
-
|
|
730
930
|
class DuckDBDialect extends PgDialect {
|
|
731
931
|
static [entityKind2] = "DuckDBPgDialect";
|
|
732
932
|
hasPgJsonColumn = false;
|
|
933
|
+
savepointsSupported = 0 /* Unknown */;
|
|
934
|
+
resetPgJsonFlag() {
|
|
935
|
+
this.hasPgJsonColumn = false;
|
|
936
|
+
}
|
|
937
|
+
markPgJsonDetected() {
|
|
938
|
+
this.hasPgJsonColumn = true;
|
|
939
|
+
}
|
|
733
940
|
assertNoPgJsonColumns() {
|
|
734
941
|
if (this.hasPgJsonColumn) {
|
|
735
|
-
throw new Error("Pg JSON/JSONB columns are not supported in DuckDB. Replace them with duckDbJson() to use DuckDB
|
|
942
|
+
throw new Error("Pg JSON/JSONB columns are not supported in DuckDB. Replace them with duckDbJson() to use DuckDB's native JSON type.");
|
|
736
943
|
}
|
|
737
944
|
}
|
|
945
|
+
areSavepointsUnsupported() {
|
|
946
|
+
return this.savepointsSupported === 2 /* No */;
|
|
947
|
+
}
|
|
948
|
+
markSavepointsSupported() {
|
|
949
|
+
this.savepointsSupported = 1 /* Yes */;
|
|
950
|
+
}
|
|
951
|
+
markSavepointsUnsupported() {
|
|
952
|
+
this.savepointsSupported = 2 /* No */;
|
|
953
|
+
}
|
|
738
954
|
async migrate(migrations, session, config) {
|
|
739
955
|
const migrationConfig = typeof config === "string" ? { migrationsFolder: config } : config;
|
|
740
956
|
const migrationsSchema = migrationConfig.migrationsSchema ?? "drizzle";
|
|
@@ -771,8 +987,8 @@ class DuckDBDialect extends PgDialect {
|
|
|
771
987
|
}
|
|
772
988
|
prepareTyping(encoder) {
|
|
773
989
|
if (is2(encoder, PgJsonb) || is2(encoder, PgJson)) {
|
|
774
|
-
this.
|
|
775
|
-
|
|
990
|
+
this.markPgJsonDetected();
|
|
991
|
+
throw new Error("Pg JSON/JSONB columns are not supported in DuckDB. Replace them with duckDbJson() to use DuckDB's native JSON type.");
|
|
776
992
|
} else if (is2(encoder, PgNumeric)) {
|
|
777
993
|
return "decimal";
|
|
778
994
|
} else if (is2(encoder, PgTime2)) {
|
|
@@ -884,6 +1100,179 @@ class DuckDBSelectBuilder extends PgSelectBuilder {
|
|
|
884
1100
|
}
|
|
885
1101
|
}
|
|
886
1102
|
|
|
1103
|
+
// src/pool.ts
|
|
1104
|
+
import { DuckDBConnection } from "@duckdb/node-api";
|
|
1105
|
+
var POOL_PRESETS = {
|
|
1106
|
+
pulse: 4,
|
|
1107
|
+
standard: 6,
|
|
1108
|
+
jumbo: 8,
|
|
1109
|
+
mega: 12,
|
|
1110
|
+
giga: 16,
|
|
1111
|
+
local: 8,
|
|
1112
|
+
memory: 4
|
|
1113
|
+
};
|
|
1114
|
+
function resolvePoolSize(pool) {
|
|
1115
|
+
if (pool === false)
|
|
1116
|
+
return false;
|
|
1117
|
+
if (pool === undefined)
|
|
1118
|
+
return 4;
|
|
1119
|
+
if (typeof pool === "string")
|
|
1120
|
+
return POOL_PRESETS[pool];
|
|
1121
|
+
return pool.size ?? 4;
|
|
1122
|
+
}
|
|
1123
|
+
function createDuckDBConnectionPool(instance, options = {}) {
|
|
1124
|
+
const size = options.size && options.size > 0 ? options.size : 4;
|
|
1125
|
+
const acquireTimeout = options.acquireTimeout ?? 30000;
|
|
1126
|
+
const maxWaitingRequests = options.maxWaitingRequests ?? 100;
|
|
1127
|
+
const maxLifetimeMs = options.maxLifetimeMs;
|
|
1128
|
+
const idleTimeoutMs = options.idleTimeoutMs;
|
|
1129
|
+
const metadata = new WeakMap;
|
|
1130
|
+
const idle = [];
|
|
1131
|
+
const waiting = [];
|
|
1132
|
+
let total = 0;
|
|
1133
|
+
let closed = false;
|
|
1134
|
+
let pendingAcquires = 0;
|
|
1135
|
+
const shouldRecycle = (conn, now) => {
|
|
1136
|
+
if (maxLifetimeMs !== undefined && now - conn.createdAt >= maxLifetimeMs) {
|
|
1137
|
+
return true;
|
|
1138
|
+
}
|
|
1139
|
+
if (idleTimeoutMs !== undefined && now - conn.lastUsedAt >= idleTimeoutMs) {
|
|
1140
|
+
return true;
|
|
1141
|
+
}
|
|
1142
|
+
return false;
|
|
1143
|
+
};
|
|
1144
|
+
const acquire = async () => {
|
|
1145
|
+
if (closed) {
|
|
1146
|
+
throw new Error("DuckDB connection pool is closed");
|
|
1147
|
+
}
|
|
1148
|
+
while (idle.length > 0) {
|
|
1149
|
+
const pooled = idle.pop();
|
|
1150
|
+
const now = Date.now();
|
|
1151
|
+
if (shouldRecycle(pooled, now)) {
|
|
1152
|
+
await closeClientConnection(pooled.connection);
|
|
1153
|
+
total = Math.max(0, total - 1);
|
|
1154
|
+
metadata.delete(pooled.connection);
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
1157
|
+
pooled.lastUsedAt = now;
|
|
1158
|
+
metadata.set(pooled.connection, {
|
|
1159
|
+
createdAt: pooled.createdAt,
|
|
1160
|
+
lastUsedAt: pooled.lastUsedAt
|
|
1161
|
+
});
|
|
1162
|
+
return pooled.connection;
|
|
1163
|
+
}
|
|
1164
|
+
if (total < size) {
|
|
1165
|
+
pendingAcquires += 1;
|
|
1166
|
+
total += 1;
|
|
1167
|
+
try {
|
|
1168
|
+
const connection = await DuckDBConnection.create(instance);
|
|
1169
|
+
if (closed) {
|
|
1170
|
+
await closeClientConnection(connection);
|
|
1171
|
+
total -= 1;
|
|
1172
|
+
throw new Error("DuckDB connection pool is closed");
|
|
1173
|
+
}
|
|
1174
|
+
const now = Date.now();
|
|
1175
|
+
metadata.set(connection, { createdAt: now, lastUsedAt: now });
|
|
1176
|
+
return connection;
|
|
1177
|
+
} catch (error) {
|
|
1178
|
+
total -= 1;
|
|
1179
|
+
throw error;
|
|
1180
|
+
} finally {
|
|
1181
|
+
pendingAcquires -= 1;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
if (waiting.length >= maxWaitingRequests) {
|
|
1185
|
+
throw new Error(`DuckDB connection pool queue is full (max ${maxWaitingRequests} waiting requests)`);
|
|
1186
|
+
}
|
|
1187
|
+
return await new Promise((resolve, reject) => {
|
|
1188
|
+
const timeoutId = setTimeout(() => {
|
|
1189
|
+
const idx = waiting.findIndex((w) => w.timeoutId === timeoutId);
|
|
1190
|
+
if (idx !== -1) {
|
|
1191
|
+
waiting.splice(idx, 1);
|
|
1192
|
+
}
|
|
1193
|
+
reject(new Error(`DuckDB connection pool acquire timeout after ${acquireTimeout}ms`));
|
|
1194
|
+
}, acquireTimeout);
|
|
1195
|
+
waiting.push({ resolve, reject, timeoutId });
|
|
1196
|
+
});
|
|
1197
|
+
};
|
|
1198
|
+
const release = async (connection) => {
|
|
1199
|
+
const waiter = waiting.shift();
|
|
1200
|
+
if (waiter) {
|
|
1201
|
+
clearTimeout(waiter.timeoutId);
|
|
1202
|
+
const now2 = Date.now();
|
|
1203
|
+
const meta = metadata.get(connection) ?? { createdAt: now2, lastUsedAt: now2 };
|
|
1204
|
+
const expired = maxLifetimeMs !== undefined && now2 - meta.createdAt >= maxLifetimeMs;
|
|
1205
|
+
if (closed) {
|
|
1206
|
+
await closeClientConnection(connection);
|
|
1207
|
+
total = Math.max(0, total - 1);
|
|
1208
|
+
metadata.delete(connection);
|
|
1209
|
+
waiter.reject(new Error("DuckDB connection pool is closed"));
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
if (expired) {
|
|
1213
|
+
await closeClientConnection(connection);
|
|
1214
|
+
total = Math.max(0, total - 1);
|
|
1215
|
+
metadata.delete(connection);
|
|
1216
|
+
try {
|
|
1217
|
+
const replacement = await acquire();
|
|
1218
|
+
waiter.resolve(replacement);
|
|
1219
|
+
} catch (error) {
|
|
1220
|
+
waiter.reject(error);
|
|
1221
|
+
}
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
meta.lastUsedAt = now2;
|
|
1225
|
+
metadata.set(connection, meta);
|
|
1226
|
+
waiter.resolve(connection);
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
if (closed) {
|
|
1230
|
+
await closeClientConnection(connection);
|
|
1231
|
+
metadata.delete(connection);
|
|
1232
|
+
total = Math.max(0, total - 1);
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
const now = Date.now();
|
|
1236
|
+
const existingMeta = metadata.get(connection) ?? { createdAt: now, lastUsedAt: now };
|
|
1237
|
+
existingMeta.lastUsedAt = now;
|
|
1238
|
+
metadata.set(connection, existingMeta);
|
|
1239
|
+
if (maxLifetimeMs !== undefined && now - existingMeta.createdAt >= maxLifetimeMs) {
|
|
1240
|
+
await closeClientConnection(connection);
|
|
1241
|
+
total -= 1;
|
|
1242
|
+
metadata.delete(connection);
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
idle.push({
|
|
1246
|
+
connection,
|
|
1247
|
+
createdAt: existingMeta.createdAt,
|
|
1248
|
+
lastUsedAt: existingMeta.lastUsedAt
|
|
1249
|
+
});
|
|
1250
|
+
};
|
|
1251
|
+
const close = async () => {
|
|
1252
|
+
closed = true;
|
|
1253
|
+
const waiters = waiting.splice(0, waiting.length);
|
|
1254
|
+
for (const waiter of waiters) {
|
|
1255
|
+
clearTimeout(waiter.timeoutId);
|
|
1256
|
+
waiter.reject(new Error("DuckDB connection pool is closed"));
|
|
1257
|
+
}
|
|
1258
|
+
const toClose = idle.splice(0, idle.length);
|
|
1259
|
+
await Promise.allSettled(toClose.map((item) => closeClientConnection(item.connection)));
|
|
1260
|
+
total = Math.max(0, total - toClose.length);
|
|
1261
|
+
toClose.forEach((item) => metadata.delete(item.connection));
|
|
1262
|
+
const maxWait = 5000;
|
|
1263
|
+
const start = Date.now();
|
|
1264
|
+
while (pendingAcquires > 0 && Date.now() - start < maxWait) {
|
|
1265
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
return {
|
|
1269
|
+
acquire,
|
|
1270
|
+
release,
|
|
1271
|
+
close,
|
|
1272
|
+
size
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
|
|
887
1276
|
// src/driver.ts
|
|
888
1277
|
class DuckDBDriver {
|
|
889
1278
|
client;
|
|
@@ -903,7 +1292,14 @@ class DuckDBDriver {
|
|
|
903
1292
|
});
|
|
904
1293
|
}
|
|
905
1294
|
}
|
|
906
|
-
function
|
|
1295
|
+
function isConfigObject(data) {
|
|
1296
|
+
if (typeof data !== "object" || data === null)
|
|
1297
|
+
return false;
|
|
1298
|
+
if (data.constructor?.name !== "Object")
|
|
1299
|
+
return false;
|
|
1300
|
+
return "connection" in data || "client" in data || "pool" in data || "schema" in data || "logger" in data;
|
|
1301
|
+
}
|
|
1302
|
+
function createFromClient(client, config = {}, instance) {
|
|
907
1303
|
const dialect = new DuckDBDialect;
|
|
908
1304
|
const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
|
|
909
1305
|
let schema;
|
|
@@ -921,17 +1317,71 @@ function drizzle(client, config = {}) {
|
|
|
921
1317
|
rejectStringArrayLiterals: config.rejectStringArrayLiterals
|
|
922
1318
|
});
|
|
923
1319
|
const session = driver.createSession(schema);
|
|
924
|
-
|
|
1320
|
+
const db = new DuckDBDatabase(dialect, session, schema, client, instance);
|
|
1321
|
+
return db;
|
|
1322
|
+
}
|
|
1323
|
+
async function createFromConnectionString(path, instanceOptions, config = {}) {
|
|
1324
|
+
const instance = await DuckDBInstance2.create(path, instanceOptions);
|
|
1325
|
+
const poolSize = resolvePoolSize(config.pool);
|
|
1326
|
+
if (poolSize === false) {
|
|
1327
|
+
const connection = await instance.connect();
|
|
1328
|
+
return createFromClient(connection, config, instance);
|
|
1329
|
+
}
|
|
1330
|
+
const pool = createDuckDBConnectionPool(instance, { size: poolSize });
|
|
1331
|
+
return createFromClient(pool, config, instance);
|
|
1332
|
+
}
|
|
1333
|
+
function drizzle(clientOrConfigOrPath, config) {
|
|
1334
|
+
if (typeof clientOrConfigOrPath === "string") {
|
|
1335
|
+
return createFromConnectionString(clientOrConfigOrPath, undefined, config);
|
|
1336
|
+
}
|
|
1337
|
+
if (isConfigObject(clientOrConfigOrPath)) {
|
|
1338
|
+
const configObj = clientOrConfigOrPath;
|
|
1339
|
+
if ("connection" in configObj) {
|
|
1340
|
+
const connConfig = configObj;
|
|
1341
|
+
const { connection, ...restConfig } = connConfig;
|
|
1342
|
+
if (typeof connection === "string") {
|
|
1343
|
+
return createFromConnectionString(connection, undefined, restConfig);
|
|
1344
|
+
}
|
|
1345
|
+
return createFromConnectionString(connection.path, connection.options, restConfig);
|
|
1346
|
+
}
|
|
1347
|
+
if ("client" in configObj) {
|
|
1348
|
+
const clientConfig = configObj;
|
|
1349
|
+
const { client: clientValue, ...restConfig } = clientConfig;
|
|
1350
|
+
return createFromClient(clientValue, restConfig);
|
|
1351
|
+
}
|
|
1352
|
+
throw new Error("Invalid drizzle config: either connection or client must be provided");
|
|
1353
|
+
}
|
|
1354
|
+
return createFromClient(clientOrConfigOrPath, config);
|
|
925
1355
|
}
|
|
926
1356
|
|
|
927
1357
|
class DuckDBDatabase extends PgDatabase {
|
|
928
1358
|
dialect;
|
|
929
1359
|
session;
|
|
930
1360
|
static [entityKind3] = "DuckDBDatabase";
|
|
931
|
-
|
|
1361
|
+
$client;
|
|
1362
|
+
$instance;
|
|
1363
|
+
constructor(dialect, session, schema, client, instance) {
|
|
932
1364
|
super(dialect, session, schema);
|
|
933
1365
|
this.dialect = dialect;
|
|
934
1366
|
this.session = session;
|
|
1367
|
+
this.$client = client;
|
|
1368
|
+
this.$instance = instance;
|
|
1369
|
+
}
|
|
1370
|
+
async close() {
|
|
1371
|
+
if (isPool(this.$client) && this.$client.close) {
|
|
1372
|
+
await this.$client.close();
|
|
1373
|
+
}
|
|
1374
|
+
if (!isPool(this.$client)) {
|
|
1375
|
+
await closeClientConnection(this.$client);
|
|
1376
|
+
}
|
|
1377
|
+
if (this.$instance) {
|
|
1378
|
+
const maybeClosable = this.$instance;
|
|
1379
|
+
if (typeof maybeClosable.close === "function") {
|
|
1380
|
+
await maybeClosable.close();
|
|
1381
|
+
} else if (typeof maybeClosable.closeSync === "function") {
|
|
1382
|
+
maybeClosable.closeSync();
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
935
1385
|
}
|
|
936
1386
|
select(fields) {
|
|
937
1387
|
const selectedFields = fields ? aliasFields(fields) : undefined;
|
|
@@ -1015,6 +1465,13 @@ function buildStructLiteral(value, schema) {
|
|
|
1015
1465
|
});
|
|
1016
1466
|
return sql4`struct_pack(${sql4.join(parts, sql4.raw(", "))})`;
|
|
1017
1467
|
}
|
|
1468
|
+
function buildMapLiteral(value, valueType) {
|
|
1469
|
+
const keys = Object.keys(value);
|
|
1470
|
+
const vals = Object.values(value);
|
|
1471
|
+
const keyList = buildListLiteral(keys, "TEXT");
|
|
1472
|
+
const valList = buildListLiteral(vals, valueType?.endsWith("[]") ? valueType.slice(0, -2) : valueType);
|
|
1473
|
+
return sql4`map(${keyList}, ${valList})`;
|
|
1474
|
+
}
|
|
1018
1475
|
var duckDbList = (name, elementType) => customType({
|
|
1019
1476
|
dataType() {
|
|
1020
1477
|
return `${elementType}[]`;
|
|
@@ -1028,11 +1485,11 @@ var duckDbList = (name, elementType) => customType({
|
|
|
1028
1485
|
}
|
|
1029
1486
|
if (typeof value === "string") {
|
|
1030
1487
|
const parsed = coerceArrayString(value);
|
|
1031
|
-
if (parsed) {
|
|
1488
|
+
if (parsed !== undefined) {
|
|
1032
1489
|
return parsed;
|
|
1033
1490
|
}
|
|
1034
1491
|
}
|
|
1035
|
-
return
|
|
1492
|
+
return value;
|
|
1036
1493
|
}
|
|
1037
1494
|
})(name);
|
|
1038
1495
|
var duckDbArray = (name, elementType, fixedLength) => customType({
|
|
@@ -1048,11 +1505,11 @@ var duckDbArray = (name, elementType, fixedLength) => customType({
|
|
|
1048
1505
|
}
|
|
1049
1506
|
if (typeof value === "string") {
|
|
1050
1507
|
const parsed = coerceArrayString(value);
|
|
1051
|
-
if (parsed) {
|
|
1508
|
+
if (parsed !== undefined) {
|
|
1052
1509
|
return parsed;
|
|
1053
1510
|
}
|
|
1054
1511
|
}
|
|
1055
|
-
return
|
|
1512
|
+
return value;
|
|
1056
1513
|
}
|
|
1057
1514
|
})(name);
|
|
1058
1515
|
var duckDbMap = (name, valueType) => customType({
|
|
@@ -1220,7 +1677,7 @@ async function migrate(db, config) {
|
|
|
1220
1677
|
// src/introspect.ts
|
|
1221
1678
|
import { sql as sql5 } from "drizzle-orm";
|
|
1222
1679
|
var SYSTEM_SCHEMAS = new Set(["information_schema", "pg_catalog"]);
|
|
1223
|
-
var DEFAULT_IMPORT_BASE = "@leonardovida-md/drizzle-neo-duckdb";
|
|
1680
|
+
var DEFAULT_IMPORT_BASE = "@leonardovida-md/drizzle-neo-duckdb/helpers";
|
|
1224
1681
|
async function introspect(db, opts = {}) {
|
|
1225
1682
|
const database = await resolveDatabase(db, opts.database, opts.allDatabases);
|
|
1226
1683
|
const schemas = await resolveSchemas(db, database, opts.schemas);
|
|
@@ -1556,6 +2013,22 @@ function mapDuckDbType(column, imports, options) {
|
|
|
1556
2013
|
imports.pgCore.add("doublePrecision");
|
|
1557
2014
|
return { builder: `doublePrecision(${columnName(column.name)})` };
|
|
1558
2015
|
}
|
|
2016
|
+
const arrayMatch = /^(.*)\[(\d+)\]$/.exec(upper);
|
|
2017
|
+
if (arrayMatch) {
|
|
2018
|
+
imports.local.add("duckDbArray");
|
|
2019
|
+
const [, base, length] = arrayMatch;
|
|
2020
|
+
return {
|
|
2021
|
+
builder: `duckDbArray(${columnName(column.name)}, ${JSON.stringify(base)}, ${Number(length)})`
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
const listMatch = /^(.*)\[\]$/.exec(upper);
|
|
2025
|
+
if (listMatch) {
|
|
2026
|
+
imports.local.add("duckDbList");
|
|
2027
|
+
const [, base] = listMatch;
|
|
2028
|
+
return {
|
|
2029
|
+
builder: `duckDbList(${columnName(column.name)}, ${JSON.stringify(base)})`
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
1559
2032
|
if (upper.startsWith("CHAR(") || upper === "CHAR") {
|
|
1560
2033
|
imports.pgCore.add("char");
|
|
1561
2034
|
const length = column.characterLength;
|
|
@@ -1584,6 +2057,20 @@ function mapDuckDbType(column, imports, options) {
|
|
|
1584
2057
|
imports.pgCore.add("text");
|
|
1585
2058
|
return { builder: `text(${columnName(column.name)}) /* JSON */` };
|
|
1586
2059
|
}
|
|
2060
|
+
if (upper.startsWith("ENUM")) {
|
|
2061
|
+
imports.pgCore.add("text");
|
|
2062
|
+
const enumLiteral = raw.replace(/^ENUM\s*/i, "").trim();
|
|
2063
|
+
return {
|
|
2064
|
+
builder: `text(${columnName(column.name)}) /* ENUM ${enumLiteral} */`
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
if (upper.startsWith("UNION")) {
|
|
2068
|
+
imports.pgCore.add("text");
|
|
2069
|
+
const unionLiteral = raw.replace(/^UNION\s*/i, "").trim();
|
|
2070
|
+
return {
|
|
2071
|
+
builder: `text(${columnName(column.name)}) /* UNION ${unionLiteral} */`
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
1587
2074
|
if (upper === "INET") {
|
|
1588
2075
|
imports.local.add("duckDbInet");
|
|
1589
2076
|
return { builder: `duckDbInet(${columnName(column.name)})` };
|
|
@@ -1596,22 +2083,6 @@ function mapDuckDbType(column, imports, options) {
|
|
|
1596
2083
|
imports.local.add("duckDbBlob");
|
|
1597
2084
|
return { builder: `duckDbBlob(${columnName(column.name)})` };
|
|
1598
2085
|
}
|
|
1599
|
-
const arrayMatch = /^(.*)\[(\d+)\]$/.exec(upper);
|
|
1600
|
-
if (arrayMatch) {
|
|
1601
|
-
imports.local.add("duckDbArray");
|
|
1602
|
-
const [, base, length] = arrayMatch;
|
|
1603
|
-
return {
|
|
1604
|
-
builder: `duckDbArray(${columnName(column.name)}, ${JSON.stringify(base)}, ${Number(length)})`
|
|
1605
|
-
};
|
|
1606
|
-
}
|
|
1607
|
-
const listMatch = /^(.*)\[\]$/.exec(upper);
|
|
1608
|
-
if (listMatch) {
|
|
1609
|
-
imports.local.add("duckDbList");
|
|
1610
|
-
const [, base] = listMatch;
|
|
1611
|
-
return {
|
|
1612
|
-
builder: `duckDbList(${columnName(column.name)}, ${JSON.stringify(base)})`
|
|
1613
|
-
};
|
|
1614
|
-
}
|
|
1615
2086
|
if (upper.startsWith("STRUCT")) {
|
|
1616
2087
|
imports.local.add("duckDbStruct");
|
|
1617
2088
|
const inner = upper.replace(/^STRUCT\s*\(/i, "").replace(/\)$/, "");
|
|
@@ -1665,7 +2136,7 @@ function mapDuckDbType(column, imports, options) {
|
|
|
1665
2136
|
}
|
|
1666
2137
|
imports.pgCore.add("text");
|
|
1667
2138
|
return {
|
|
1668
|
-
builder: `text(${columnName(column.name)}) /*
|
|
2139
|
+
builder: `text(${columnName(column.name)}) /* unsupported DuckDB type: ${upper} */`
|
|
1669
2140
|
};
|
|
1670
2141
|
}
|
|
1671
2142
|
function parseStructFields(inner) {
|
|
@@ -1888,19 +2359,26 @@ export {
|
|
|
1888
2359
|
wrapJson,
|
|
1889
2360
|
wrapBlob,
|
|
1890
2361
|
wrapArray,
|
|
2362
|
+
toIdentifier,
|
|
1891
2363
|
sumN,
|
|
1892
2364
|
sumDistinctN,
|
|
2365
|
+
splitTopLevel,
|
|
1893
2366
|
rowNumber,
|
|
2367
|
+
resolvePoolSize,
|
|
1894
2368
|
rank,
|
|
1895
2369
|
prepareParams,
|
|
1896
2370
|
percentileCont,
|
|
2371
|
+
parseStructFields,
|
|
2372
|
+
parseMapValue,
|
|
1897
2373
|
olap,
|
|
1898
2374
|
migrate,
|
|
1899
2375
|
median,
|
|
1900
2376
|
lead,
|
|
1901
2377
|
lag,
|
|
2378
|
+
isPool,
|
|
1902
2379
|
isDuckDBWrapper,
|
|
1903
2380
|
introspect,
|
|
2381
|
+
formatLiteral,
|
|
1904
2382
|
executeOnClient,
|
|
1905
2383
|
executeInBatches,
|
|
1906
2384
|
executeArrowOnClient,
|
|
@@ -1920,15 +2398,23 @@ export {
|
|
|
1920
2398
|
duckDbArray,
|
|
1921
2399
|
drizzle,
|
|
1922
2400
|
denseRank,
|
|
2401
|
+
createDuckDBConnectionPool,
|
|
1923
2402
|
countN,
|
|
2403
|
+
coerceArrayString,
|
|
1924
2404
|
closeClientConnection,
|
|
2405
|
+
buildStructLiteral,
|
|
2406
|
+
buildMapLiteral,
|
|
2407
|
+
buildListLiteral,
|
|
2408
|
+
buildDefault,
|
|
1925
2409
|
avgN,
|
|
1926
2410
|
anyValue,
|
|
2411
|
+
POOL_PRESETS,
|
|
1927
2412
|
OlapBuilder,
|
|
1928
2413
|
DuckDBTransaction,
|
|
1929
2414
|
DuckDBSession,
|
|
1930
2415
|
DuckDBPreparedQuery,
|
|
1931
2416
|
DuckDBDriver,
|
|
1932
2417
|
DuckDBDatabase,
|
|
1933
|
-
DUCKDB_VALUE_MARKER
|
|
2418
|
+
DUCKDB_VALUE_MARKER,
|
|
2419
|
+
DEFAULT_IMPORT_BASE
|
|
1934
2420
|
};
|