@duckdbfan/drizzle-duckdb 0.0.7 → 1.3.2
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 +349 -62
- package/dist/bin/duckdb-introspect.d.ts +2 -0
- package/dist/client.d.ts +42 -0
- package/dist/columns.d.ts +100 -9
- package/dist/dialect.d.ts +27 -2
- package/dist/driver.d.ts +53 -37
- package/dist/duckdb-introspect.mjs +2890 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.mjs +360 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.mjs +3015 -228
- package/dist/introspect.d.ts +74 -0
- package/dist/migrator.d.ts +3 -2
- package/dist/olap.d.ts +46 -0
- package/dist/operators.d.ts +8 -0
- package/dist/options.d.ts +7 -0
- package/dist/pool.d.ts +30 -0
- package/dist/select-builder.d.ts +31 -0
- package/dist/session.d.ts +33 -8
- package/dist/sql/ast-transformer.d.ts +33 -0
- package/dist/sql/result-mapper.d.ts +9 -0
- package/dist/sql/selection.d.ts +2 -0
- package/dist/sql/visitors/array-operators.d.ts +5 -0
- package/dist/sql/visitors/column-qualifier.d.ts +10 -0
- package/dist/sql/visitors/generate-series-alias.d.ts +13 -0
- package/dist/sql/visitors/union-with-hoister.d.ts +11 -0
- package/dist/utils.d.ts +2 -5
- package/dist/value-wrappers-core.d.ts +42 -0
- package/dist/value-wrappers.d.ts +8 -0
- package/package.json +53 -16
- package/src/bin/duckdb-introspect.ts +181 -0
- package/src/client.ts +528 -0
- package/src/columns.ts +420 -65
- package/src/dialect.ts +111 -15
- package/src/driver.ts +266 -180
- package/src/helpers.ts +18 -0
- package/src/index.ts +8 -1
- package/src/introspect.ts +935 -0
- package/src/migrator.ts +10 -5
- package/src/olap.ts +190 -0
- package/src/operators.ts +27 -0
- package/src/options.ts +25 -0
- package/src/pool.ts +274 -0
- package/src/select-builder.ts +110 -0
- package/src/session.ts +306 -66
- package/src/sql/ast-transformer.ts +170 -0
- package/src/sql/result-mapper.ts +303 -0
- package/src/sql/selection.ts +60 -0
- package/src/sql/visitors/array-operators.ts +214 -0
- package/src/sql/visitors/column-qualifier.ts +586 -0
- package/src/sql/visitors/generate-series-alias.ts +291 -0
- package/src/sql/visitors/union-with-hoister.ts +106 -0
- package/src/utils.ts +2 -222
- package/src/value-wrappers-core.ts +168 -0
- package/src/value-wrappers.ts +165 -0
package/dist/index.mjs
CHANGED
|
@@ -1,36 +1,160 @@
|
|
|
1
1
|
// src/driver.ts
|
|
2
|
-
import {
|
|
2
|
+
import { DuckDBInstance as DuckDBInstance2 } from "@duckdb/node-api";
|
|
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";
|
|
5
6
|
import {
|
|
6
7
|
createTableRelationsHelpers,
|
|
7
8
|
extractTablesRelationalConfig
|
|
8
9
|
} from "drizzle-orm/relations";
|
|
9
|
-
import {
|
|
10
|
-
getTableColumns
|
|
11
|
-
} from "drizzle-orm/utils";
|
|
12
10
|
|
|
13
11
|
// src/session.ts
|
|
14
12
|
import { entityKind } from "drizzle-orm/entity";
|
|
15
13
|
import { NoopLogger } from "drizzle-orm/logger";
|
|
16
14
|
import { PgTransaction } from "drizzle-orm/pg-core";
|
|
17
15
|
import { PgPreparedQuery, PgSession } from "drizzle-orm/pg-core/session";
|
|
18
|
-
import { fillPlaceholders, sql
|
|
16
|
+
import { fillPlaceholders, sql } from "drizzle-orm/sql/sql";
|
|
19
17
|
|
|
20
|
-
// src/
|
|
18
|
+
// src/sql/result-mapper.ts
|
|
21
19
|
import {
|
|
22
|
-
is,
|
|
23
20
|
Column,
|
|
24
21
|
SQL,
|
|
25
22
|
getTableName,
|
|
26
|
-
|
|
23
|
+
is
|
|
27
24
|
} from "drizzle-orm";
|
|
28
25
|
import {
|
|
29
|
-
PgCustomColumn
|
|
26
|
+
PgCustomColumn,
|
|
27
|
+
PgDate,
|
|
28
|
+
PgDateString,
|
|
29
|
+
PgInterval,
|
|
30
|
+
PgTime,
|
|
31
|
+
PgTimestamp,
|
|
32
|
+
PgTimestampString
|
|
30
33
|
} from "drizzle-orm/pg-core";
|
|
34
|
+
function toDecoderInput(decoder, value) {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
function normalizeInet(value) {
|
|
38
|
+
if (value && typeof value === "object" && "address" in value && typeof value.address !== "undefined") {
|
|
39
|
+
const { address, mask } = value;
|
|
40
|
+
if (typeof address === "bigint" || typeof address === "number") {
|
|
41
|
+
const inet = typeof address === "number" ? BigInt(address) : address;
|
|
42
|
+
const maxIpv4 = (1n << 32n) - 1n;
|
|
43
|
+
if (inet >= 0 && inet <= maxIpv4) {
|
|
44
|
+
const num = Number(inet);
|
|
45
|
+
const octets = [
|
|
46
|
+
num >>> 24 & 255,
|
|
47
|
+
num >>> 16 & 255,
|
|
48
|
+
num >>> 8 & 255,
|
|
49
|
+
num & 255
|
|
50
|
+
];
|
|
51
|
+
const suffix = typeof mask === "number" && mask !== 32 ? `/${mask}` : "";
|
|
52
|
+
return `${octets.join(".")}${suffix}`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const fallback = value.toString?.();
|
|
56
|
+
if (fallback && fallback !== "[object Object]") {
|
|
57
|
+
return fallback;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
function normalizeTimestampString(value, withTimezone) {
|
|
63
|
+
if (value instanceof Date) {
|
|
64
|
+
const iso = value.toISOString().replace("T", " ");
|
|
65
|
+
return withTimezone ? iso.replace("Z", "+00") : iso.replace("Z", "");
|
|
66
|
+
}
|
|
67
|
+
if (typeof value === "string") {
|
|
68
|
+
const normalized = value.replace("T", " ");
|
|
69
|
+
if (withTimezone) {
|
|
70
|
+
return normalized.includes("+") ? normalized : `${normalized}+00`;
|
|
71
|
+
}
|
|
72
|
+
return normalized.replace(/\+00$/, "");
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
function normalizeTimestamp(value, withTimezone) {
|
|
77
|
+
if (value instanceof Date) {
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
if (typeof value === "string") {
|
|
81
|
+
const hasOffset = value.endsWith("Z") || /[+-]\d{2}:?\d{2}$/.test(value.trim());
|
|
82
|
+
const spaced = value.replace(" ", "T");
|
|
83
|
+
const normalized = withTimezone || hasOffset ? spaced : `${spaced}+00`;
|
|
84
|
+
return new Date(normalized);
|
|
85
|
+
}
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
function normalizeDateString(value) {
|
|
89
|
+
if (value instanceof Date) {
|
|
90
|
+
return value.toISOString().slice(0, 10);
|
|
91
|
+
}
|
|
92
|
+
if (typeof value === "string") {
|
|
93
|
+
return value.slice(0, 10);
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
function normalizeDateValue(value) {
|
|
98
|
+
if (value instanceof Date) {
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
if (typeof value === "string") {
|
|
102
|
+
return new Date(`${value.slice(0, 10)}T00:00:00Z`);
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
function normalizeTime(value) {
|
|
107
|
+
if (typeof value === "bigint") {
|
|
108
|
+
const totalMillis = Number(value) / 1000;
|
|
109
|
+
const date = new Date(totalMillis);
|
|
110
|
+
return date.toISOString().split("T")[1].replace("Z", "");
|
|
111
|
+
}
|
|
112
|
+
if (value instanceof Date) {
|
|
113
|
+
return value.toISOString().split("T")[1].replace("Z", "");
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
function normalizeInterval(value) {
|
|
118
|
+
if (value && typeof value === "object" && "days" in value && "months" in value) {
|
|
119
|
+
const { months, days, micros } = value;
|
|
120
|
+
if (months === 0 && days !== undefined) {
|
|
121
|
+
if (micros && Number(micros) !== 0) {
|
|
122
|
+
const seconds = Number(micros) / 1e6;
|
|
123
|
+
return `${days} day${days === 1 ? "" : "s"} ${seconds} seconds`.trim();
|
|
124
|
+
}
|
|
125
|
+
return `${days} day${days === 1 ? "" : "s"}`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
function mapDriverValue(decoder, rawValue) {
|
|
131
|
+
if (is(decoder, PgTimestampString)) {
|
|
132
|
+
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeTimestampString(rawValue, decoder.withTimezone)));
|
|
133
|
+
}
|
|
134
|
+
if (is(decoder, PgTimestamp)) {
|
|
135
|
+
const normalized = normalizeTimestamp(rawValue, decoder.withTimezone);
|
|
136
|
+
if (normalized instanceof Date) {
|
|
137
|
+
return normalized;
|
|
138
|
+
}
|
|
139
|
+
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalized));
|
|
140
|
+
}
|
|
141
|
+
if (is(decoder, PgDateString)) {
|
|
142
|
+
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeDateString(rawValue)));
|
|
143
|
+
}
|
|
144
|
+
if (is(decoder, PgDate)) {
|
|
145
|
+
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeDateValue(rawValue)));
|
|
146
|
+
}
|
|
147
|
+
if (is(decoder, PgTime)) {
|
|
148
|
+
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeTime(rawValue)));
|
|
149
|
+
}
|
|
150
|
+
if (is(decoder, PgInterval)) {
|
|
151
|
+
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeInterval(rawValue)));
|
|
152
|
+
}
|
|
153
|
+
return decoder.mapFromDriverValue(toDecoderInput(decoder, rawValue));
|
|
154
|
+
}
|
|
31
155
|
function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
32
156
|
const nullifyMap = {};
|
|
33
|
-
const result = columns.reduce((
|
|
157
|
+
const result = columns.reduce((acc, { path, field }, columnIndex) => {
|
|
34
158
|
let decoder;
|
|
35
159
|
if (is(field, Column)) {
|
|
36
160
|
decoder = field;
|
|
@@ -44,7 +168,7 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
44
168
|
decoder = field.sql.decoder;
|
|
45
169
|
}
|
|
46
170
|
}
|
|
47
|
-
let node =
|
|
171
|
+
let node = acc;
|
|
48
172
|
for (const [pathChunkIndex, pathChunk] of path.entries()) {
|
|
49
173
|
if (pathChunkIndex < path.length - 1) {
|
|
50
174
|
if (!(pathChunk in node)) {
|
|
@@ -53,8 +177,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
53
177
|
node = node[pathChunk];
|
|
54
178
|
continue;
|
|
55
179
|
}
|
|
56
|
-
const rawValue = row[columnIndex];
|
|
57
|
-
const value = node[pathChunk] = rawValue === null ? null : decoder
|
|
180
|
+
const rawValue = normalizeInet(row[columnIndex]);
|
|
181
|
+
const value = node[pathChunk] = rawValue === null ? null : mapDriverValue(decoder, rawValue);
|
|
58
182
|
if (joinsNotNullableMap && is(field, Column) && path.length === 2) {
|
|
59
183
|
const objectName = path[0];
|
|
60
184
|
if (!(objectName in nullifyMap)) {
|
|
@@ -81,7 +205,7 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
81
205
|
continue;
|
|
82
206
|
}
|
|
83
207
|
}
|
|
84
|
-
return
|
|
208
|
+
return acc;
|
|
85
209
|
}, {});
|
|
86
210
|
if (joinsNotNullableMap && Object.keys(nullifyMap).length > 0) {
|
|
87
211
|
for (const [objectName, tableName] of Object.entries(nullifyMap)) {
|
|
@@ -92,87 +216,493 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
92
216
|
}
|
|
93
217
|
return result;
|
|
94
218
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
219
|
+
|
|
220
|
+
// src/session.ts
|
|
221
|
+
import { TransactionRollbackError } from "drizzle-orm/errors";
|
|
222
|
+
|
|
223
|
+
// src/client.ts
|
|
224
|
+
import {
|
|
225
|
+
listValue as listValue2,
|
|
226
|
+
timestampValue as timestampValue2
|
|
227
|
+
} from "@duckdb/node-api";
|
|
228
|
+
|
|
229
|
+
// src/value-wrappers.ts
|
|
230
|
+
import {
|
|
231
|
+
listValue,
|
|
232
|
+
arrayValue,
|
|
233
|
+
structValue,
|
|
234
|
+
mapValue,
|
|
235
|
+
blobValue,
|
|
236
|
+
timestampValue,
|
|
237
|
+
timestampTZValue
|
|
238
|
+
} from "@duckdb/node-api";
|
|
239
|
+
|
|
240
|
+
// src/value-wrappers-core.ts
|
|
241
|
+
var DUCKDB_VALUE_MARKER = Symbol.for("drizzle-duckdb:value");
|
|
242
|
+
function isDuckDBWrapper(value) {
|
|
243
|
+
return value !== null && typeof value === "object" && DUCKDB_VALUE_MARKER in value && value[DUCKDB_VALUE_MARKER] === true;
|
|
244
|
+
}
|
|
245
|
+
function wrapList(data, elementType) {
|
|
246
|
+
return {
|
|
247
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
248
|
+
kind: "list",
|
|
249
|
+
data,
|
|
250
|
+
elementType
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function wrapArray(data, elementType, fixedLength) {
|
|
254
|
+
return {
|
|
255
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
256
|
+
kind: "array",
|
|
257
|
+
data,
|
|
258
|
+
elementType,
|
|
259
|
+
fixedLength
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function wrapStruct(data, schema) {
|
|
263
|
+
return {
|
|
264
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
265
|
+
kind: "struct",
|
|
266
|
+
data,
|
|
267
|
+
schema
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function wrapMap(data, valueType) {
|
|
271
|
+
return {
|
|
272
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
273
|
+
kind: "map",
|
|
274
|
+
data,
|
|
275
|
+
valueType
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function wrapTimestamp(data, withTimezone, precision) {
|
|
279
|
+
return {
|
|
280
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
281
|
+
kind: "timestamp",
|
|
282
|
+
data,
|
|
283
|
+
withTimezone,
|
|
284
|
+
precision
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function wrapBlob(data) {
|
|
288
|
+
return {
|
|
289
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
290
|
+
kind: "blob",
|
|
291
|
+
data
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function wrapJson(data) {
|
|
295
|
+
return {
|
|
296
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
297
|
+
kind: "json",
|
|
298
|
+
data
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/value-wrappers.ts
|
|
303
|
+
function dateToMicros(value) {
|
|
304
|
+
if (value instanceof Date) {
|
|
305
|
+
return BigInt(value.getTime()) * 1000n;
|
|
306
|
+
}
|
|
307
|
+
if (typeof value === "bigint") {
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
if (typeof value === "number") {
|
|
311
|
+
return BigInt(Math.trunc(value)) * 1000n;
|
|
312
|
+
}
|
|
313
|
+
let normalized = value;
|
|
314
|
+
if (!value.includes("T") && value.includes(" ")) {
|
|
315
|
+
normalized = value.replace(" ", "T");
|
|
316
|
+
}
|
|
317
|
+
if (!normalized.endsWith("Z") && !/[+-]\d{2}:?\d{2}$/.test(normalized)) {
|
|
318
|
+
normalized += "Z";
|
|
319
|
+
}
|
|
320
|
+
const date = new Date(normalized);
|
|
321
|
+
if (isNaN(date.getTime())) {
|
|
322
|
+
throw new Error(`Invalid timestamp string: ${value}`);
|
|
323
|
+
}
|
|
324
|
+
return BigInt(date.getTime()) * 1000n;
|
|
325
|
+
}
|
|
326
|
+
function toUint8Array(data) {
|
|
327
|
+
return data instanceof Uint8Array && !(data instanceof Buffer) ? data : new Uint8Array(data);
|
|
328
|
+
}
|
|
329
|
+
function convertStructEntries(data, toValue) {
|
|
330
|
+
const entries = {};
|
|
331
|
+
for (const [key, val] of Object.entries(data)) {
|
|
332
|
+
entries[key] = toValue(val);
|
|
333
|
+
}
|
|
334
|
+
return entries;
|
|
335
|
+
}
|
|
336
|
+
function convertMapEntries(data, toValue) {
|
|
337
|
+
return Object.entries(data).map(([key, val]) => ({
|
|
338
|
+
key,
|
|
339
|
+
value: toValue(val)
|
|
340
|
+
}));
|
|
341
|
+
}
|
|
342
|
+
function wrapperToNodeApiValue(wrapper, toValue) {
|
|
343
|
+
switch (wrapper.kind) {
|
|
344
|
+
case "list":
|
|
345
|
+
return listValue(wrapper.data.map(toValue));
|
|
346
|
+
case "array":
|
|
347
|
+
return arrayValue(wrapper.data.map(toValue));
|
|
348
|
+
case "struct":
|
|
349
|
+
return structValue(convertStructEntries(wrapper.data, toValue));
|
|
350
|
+
case "map":
|
|
351
|
+
return mapValue(convertMapEntries(wrapper.data, toValue));
|
|
352
|
+
case "timestamp":
|
|
353
|
+
return wrapper.withTimezone ? timestampTZValue(dateToMicros(wrapper.data)) : timestampValue(dateToMicros(wrapper.data));
|
|
354
|
+
case "blob":
|
|
355
|
+
return blobValue(toUint8Array(wrapper.data));
|
|
356
|
+
case "json":
|
|
357
|
+
return JSON.stringify(wrapper.data);
|
|
358
|
+
default: {
|
|
359
|
+
const _exhaustive = wrapper;
|
|
360
|
+
throw new Error(`Unknown wrapper kind: ${_exhaustive.kind}`);
|
|
102
361
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/client.ts
|
|
366
|
+
function isPool(client) {
|
|
367
|
+
return typeof client.acquire === "function";
|
|
368
|
+
}
|
|
369
|
+
var PREPARED_CACHE = Symbol.for("drizzle-duckdb:prepared-cache");
|
|
370
|
+
function isPgArrayLiteral(value) {
|
|
371
|
+
return value.startsWith("{") && value.endsWith("}");
|
|
372
|
+
}
|
|
373
|
+
function parsePgArrayLiteral(value) {
|
|
374
|
+
const json = value.replace(/{/g, "[").replace(/}/g, "]");
|
|
375
|
+
try {
|
|
376
|
+
return JSON.parse(json);
|
|
377
|
+
} catch {
|
|
378
|
+
return value;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function prepareParams(params, options = {}) {
|
|
382
|
+
return params.map((param) => {
|
|
383
|
+
if (typeof param === "string" && param.length > 0) {
|
|
384
|
+
const firstChar = param[0];
|
|
385
|
+
const maybeArrayLiteral = firstChar === "{" || firstChar === "[" || firstChar === " " || firstChar === "\t";
|
|
386
|
+
if (maybeArrayLiteral) {
|
|
387
|
+
const trimmed = firstChar === "{" || firstChar === "[" ? param : param.trim();
|
|
388
|
+
if (trimmed && isPgArrayLiteral(trimmed)) {
|
|
389
|
+
if (options.rejectStringArrayLiterals) {
|
|
390
|
+
throw new Error("Stringified array literals are not supported. Use duckDbList()/duckDbArray() or pass native arrays.");
|
|
391
|
+
}
|
|
392
|
+
if (options.warnOnStringArrayLiteral) {
|
|
393
|
+
options.warnOnStringArrayLiteral();
|
|
394
|
+
}
|
|
395
|
+
return parsePgArrayLiteral(trimmed);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
107
398
|
}
|
|
108
|
-
|
|
109
|
-
|
|
399
|
+
return param;
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
function toNodeApiValue(value) {
|
|
403
|
+
if (value == null)
|
|
404
|
+
return null;
|
|
405
|
+
const t = typeof value;
|
|
406
|
+
if (t === "string" || t === "number" || t === "bigint" || t === "boolean") {
|
|
407
|
+
return value;
|
|
408
|
+
}
|
|
409
|
+
if (t === "object" && DUCKDB_VALUE_MARKER in value) {
|
|
410
|
+
return wrapperToNodeApiValue(value, toNodeApiValue);
|
|
411
|
+
}
|
|
412
|
+
if (Array.isArray(value)) {
|
|
413
|
+
return listValue2(value.map((inner) => toNodeApiValue(inner)));
|
|
414
|
+
}
|
|
415
|
+
if (value instanceof Date) {
|
|
416
|
+
return timestampValue2(BigInt(value.getTime()) * 1000n);
|
|
417
|
+
}
|
|
418
|
+
return value;
|
|
419
|
+
}
|
|
420
|
+
function deduplicateColumns(columns) {
|
|
421
|
+
const counts = new Map;
|
|
422
|
+
let hasDuplicates = false;
|
|
423
|
+
for (const column of columns) {
|
|
424
|
+
const next = (counts.get(column) ?? 0) + 1;
|
|
425
|
+
counts.set(column, next);
|
|
426
|
+
if (next > 1) {
|
|
427
|
+
hasDuplicates = true;
|
|
428
|
+
break;
|
|
110
429
|
}
|
|
111
|
-
|
|
112
|
-
|
|
430
|
+
}
|
|
431
|
+
if (!hasDuplicates) {
|
|
432
|
+
return columns;
|
|
433
|
+
}
|
|
434
|
+
counts.clear();
|
|
435
|
+
return columns.map((column) => {
|
|
436
|
+
const count = counts.get(column) ?? 0;
|
|
437
|
+
counts.set(column, count + 1);
|
|
438
|
+
return count === 0 ? column : `${column}_${count}`;
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function destroyPreparedStatement(entry) {
|
|
442
|
+
if (!entry)
|
|
443
|
+
return;
|
|
444
|
+
try {
|
|
445
|
+
entry.statement.destroySync();
|
|
446
|
+
} catch {}
|
|
447
|
+
}
|
|
448
|
+
function getPreparedCache(connection, size) {
|
|
449
|
+
const store = connection;
|
|
450
|
+
const existing = store[PREPARED_CACHE];
|
|
451
|
+
if (existing) {
|
|
452
|
+
existing.size = size;
|
|
453
|
+
return existing;
|
|
454
|
+
}
|
|
455
|
+
const cache = { size, entries: new Map };
|
|
456
|
+
store[PREPARED_CACHE] = cache;
|
|
457
|
+
return cache;
|
|
458
|
+
}
|
|
459
|
+
function evictOldest(cache) {
|
|
460
|
+
const oldest = cache.entries.keys().next();
|
|
461
|
+
if (!oldest.done) {
|
|
462
|
+
const key = oldest.value;
|
|
463
|
+
const entry = cache.entries.get(key);
|
|
464
|
+
cache.entries.delete(key);
|
|
465
|
+
destroyPreparedStatement(entry);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function evictCacheEntry(cache, key) {
|
|
469
|
+
const entry = cache.entries.get(key);
|
|
470
|
+
cache.entries.delete(key);
|
|
471
|
+
destroyPreparedStatement(entry);
|
|
472
|
+
}
|
|
473
|
+
async function getOrPrepareStatement(connection, query, cacheConfig) {
|
|
474
|
+
const cache = getPreparedCache(connection, cacheConfig.size);
|
|
475
|
+
const cached = cache.entries.get(query);
|
|
476
|
+
if (cached) {
|
|
477
|
+
cache.entries.delete(query);
|
|
478
|
+
cache.entries.set(query, cached);
|
|
479
|
+
return cached.statement;
|
|
480
|
+
}
|
|
481
|
+
const statement = await connection.prepare(query);
|
|
482
|
+
cache.entries.set(query, { statement });
|
|
483
|
+
while (cache.entries.size > cache.size) {
|
|
484
|
+
evictOldest(cache);
|
|
485
|
+
}
|
|
486
|
+
return statement;
|
|
487
|
+
}
|
|
488
|
+
async function materializeResultRows(result) {
|
|
489
|
+
const rows = await result.getRowsJS() ?? [];
|
|
490
|
+
const baseColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
|
|
491
|
+
const columns = typeof result.deduplicatedColumnNames === "function" ? baseColumns : deduplicateColumns(baseColumns);
|
|
492
|
+
return { columns, rows };
|
|
493
|
+
}
|
|
494
|
+
async function materializeRows(client, query, params, options = {}) {
|
|
495
|
+
if (isPool(client)) {
|
|
496
|
+
const connection2 = await client.acquire();
|
|
497
|
+
try {
|
|
498
|
+
return await materializeRows(connection2, query, params, options);
|
|
499
|
+
} finally {
|
|
500
|
+
await client.release(connection2);
|
|
113
501
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
502
|
+
}
|
|
503
|
+
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
504
|
+
const connection = client;
|
|
505
|
+
if (options.prepareCache && typeof connection.prepare === "function") {
|
|
506
|
+
const cache = getPreparedCache(connection, options.prepareCache.size);
|
|
507
|
+
try {
|
|
508
|
+
const statement = await getOrPrepareStatement(connection, query, options.prepareCache);
|
|
509
|
+
if (values) {
|
|
510
|
+
statement.bind(values);
|
|
511
|
+
} else {
|
|
512
|
+
statement.clearBindings?.();
|
|
513
|
+
}
|
|
514
|
+
const result2 = await statement.run();
|
|
515
|
+
cache.entries.delete(query);
|
|
516
|
+
cache.entries.set(query, { statement });
|
|
517
|
+
return await materializeResultRows(result2);
|
|
518
|
+
} catch (error) {
|
|
519
|
+
evictCacheEntry(cache, query);
|
|
520
|
+
throw error;
|
|
123
521
|
}
|
|
124
|
-
|
|
125
|
-
|
|
522
|
+
}
|
|
523
|
+
const result = await connection.run(query, values);
|
|
524
|
+
return await materializeResultRows(result);
|
|
525
|
+
}
|
|
526
|
+
function clearPreparedCache(connection) {
|
|
527
|
+
const store = connection;
|
|
528
|
+
const cache = store[PREPARED_CACHE];
|
|
529
|
+
if (!cache)
|
|
530
|
+
return;
|
|
531
|
+
for (const entry of cache.entries.values()) {
|
|
532
|
+
destroyPreparedStatement(entry);
|
|
533
|
+
}
|
|
534
|
+
cache.entries.clear();
|
|
535
|
+
}
|
|
536
|
+
function mapRowsToObjects(columns, rows) {
|
|
537
|
+
return rows.map((vals) => {
|
|
538
|
+
const obj = {};
|
|
539
|
+
columns.forEach((col, idx) => {
|
|
540
|
+
obj[col] = vals[idx];
|
|
541
|
+
});
|
|
542
|
+
return obj;
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
async function closeClientConnection(connection) {
|
|
546
|
+
clearPreparedCache(connection);
|
|
547
|
+
if ("close" in connection && typeof connection.close === "function") {
|
|
548
|
+
await connection.close();
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if ("closeSync" in connection && typeof connection.closeSync === "function") {
|
|
552
|
+
connection.closeSync();
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if ("disconnectSync" in connection && typeof connection.disconnectSync === "function") {
|
|
556
|
+
connection.disconnectSync();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async function executeOnClient(client, query, params, options = {}) {
|
|
560
|
+
const { columns, rows } = await materializeRows(client, query, params, options);
|
|
561
|
+
if (!rows || rows.length === 0) {
|
|
562
|
+
return [];
|
|
563
|
+
}
|
|
564
|
+
return mapRowsToObjects(columns, rows);
|
|
565
|
+
}
|
|
566
|
+
async function executeArraysOnClient(client, query, params, options = {}) {
|
|
567
|
+
return await materializeRows(client, query, params, options);
|
|
568
|
+
}
|
|
569
|
+
async function* executeInBatches(client, query, params, options = {}) {
|
|
570
|
+
if (isPool(client)) {
|
|
571
|
+
const connection = await client.acquire();
|
|
572
|
+
try {
|
|
573
|
+
yield* executeInBatches(connection, query, params, options);
|
|
574
|
+
return;
|
|
575
|
+
} finally {
|
|
576
|
+
await client.release(connection);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
|
|
580
|
+
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
581
|
+
const result = await client.stream(query, values);
|
|
582
|
+
const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
|
|
583
|
+
const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
|
|
584
|
+
let buffer = [];
|
|
585
|
+
for await (const chunk of result.yieldRowsJs()) {
|
|
586
|
+
const objects = mapRowsToObjects(columns, chunk);
|
|
587
|
+
for (const row of objects) {
|
|
588
|
+
buffer.push(row);
|
|
589
|
+
if (buffer.length >= rowsPerChunk) {
|
|
590
|
+
yield buffer;
|
|
591
|
+
buffer = [];
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (buffer.length > 0) {
|
|
596
|
+
yield buffer;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
async function* executeInBatchesRaw(client, query, params, options = {}) {
|
|
600
|
+
if (isPool(client)) {
|
|
601
|
+
const connection = await client.acquire();
|
|
602
|
+
try {
|
|
603
|
+
yield* executeInBatchesRaw(connection, query, params, options);
|
|
604
|
+
return;
|
|
605
|
+
} finally {
|
|
606
|
+
await client.release(connection);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
|
|
610
|
+
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
611
|
+
const result = await client.stream(query, values);
|
|
612
|
+
const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
|
|
613
|
+
const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
|
|
614
|
+
let buffer = [];
|
|
615
|
+
for await (const chunk of result.yieldRowsJs()) {
|
|
616
|
+
for (const row of chunk) {
|
|
617
|
+
buffer.push(row);
|
|
618
|
+
if (buffer.length >= rowsPerChunk) {
|
|
619
|
+
yield { columns, rows: buffer };
|
|
620
|
+
buffer = [];
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (buffer.length > 0) {
|
|
625
|
+
yield { columns, rows: buffer };
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
async function executeArrowOnClient(client, query, params) {
|
|
629
|
+
if (isPool(client)) {
|
|
630
|
+
const connection = await client.acquire();
|
|
631
|
+
try {
|
|
632
|
+
return await executeArrowOnClient(connection, query, params);
|
|
633
|
+
} finally {
|
|
634
|
+
await client.release(connection);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
638
|
+
const result = await client.run(query, values);
|
|
639
|
+
const maybeArrow = result.toArrow ?? result.getArrowTable;
|
|
640
|
+
if (typeof maybeArrow === "function") {
|
|
641
|
+
return await maybeArrow.call(result);
|
|
642
|
+
}
|
|
643
|
+
return result.getColumnsObjectJS();
|
|
126
644
|
}
|
|
127
|
-
var tableIdPropSelectionRegex = new RegExp([
|
|
128
|
-
`("(.+)"\\."(.+)")`,
|
|
129
|
-
`(\\s+as\\s+'?(.+?)'?\\.'?(.+?)'?)?`
|
|
130
|
-
].join(""), "i");
|
|
131
645
|
|
|
132
646
|
// src/session.ts
|
|
133
|
-
|
|
647
|
+
function isSavepointSyntaxError(error) {
|
|
648
|
+
if (!(error instanceof Error) || !error.message) {
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
return error.message.toLowerCase().includes("savepoint") && error.message.toLowerCase().includes("syntax error");
|
|
652
|
+
}
|
|
134
653
|
|
|
135
654
|
class DuckDBPreparedQuery extends PgPreparedQuery {
|
|
136
655
|
client;
|
|
656
|
+
dialect;
|
|
137
657
|
queryString;
|
|
138
658
|
params;
|
|
139
659
|
logger;
|
|
140
660
|
fields;
|
|
141
661
|
_isResponseInArrayMode;
|
|
142
662
|
customResultMapper;
|
|
663
|
+
rejectStringArrayLiterals;
|
|
664
|
+
prepareCache;
|
|
665
|
+
warnOnStringArrayLiteral;
|
|
143
666
|
static [entityKind] = "DuckDBPreparedQuery";
|
|
144
|
-
constructor(client, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper) {
|
|
667
|
+
constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
|
|
145
668
|
super({ sql: queryString, params });
|
|
146
669
|
this.client = client;
|
|
670
|
+
this.dialect = dialect;
|
|
147
671
|
this.queryString = queryString;
|
|
148
672
|
this.params = params;
|
|
149
673
|
this.logger = logger;
|
|
150
674
|
this.fields = fields;
|
|
151
675
|
this._isResponseInArrayMode = _isResponseInArrayMode;
|
|
152
676
|
this.customResultMapper = customResultMapper;
|
|
677
|
+
this.rejectStringArrayLiterals = rejectStringArrayLiterals;
|
|
678
|
+
this.prepareCache = prepareCache;
|
|
679
|
+
this.warnOnStringArrayLiteral = warnOnStringArrayLiteral;
|
|
153
680
|
}
|
|
154
681
|
async execute(placeholderValues = {}) {
|
|
155
|
-
|
|
682
|
+
this.dialect.assertNoPgJsonColumns();
|
|
683
|
+
const params = prepareParams(fillPlaceholders(this.params, placeholderValues), {
|
|
684
|
+
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
685
|
+
warnOnStringArrayLiteral: this.warnOnStringArrayLiteral ? () => this.warnOnStringArrayLiteral?.(this.queryString) : undefined
|
|
686
|
+
});
|
|
156
687
|
this.logger.logQuery(this.queryString, params);
|
|
157
|
-
const {
|
|
158
|
-
|
|
159
|
-
client,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return customResultMapper ? customResultMapper(rowValues) : rowValues.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
|
|
688
|
+
const { fields, joinsNotNullableMap, customResultMapper } = this;
|
|
689
|
+
if (fields) {
|
|
690
|
+
const { rows: rows2 } = await executeArraysOnClient(this.client, this.queryString, params, { prepareCache: this.prepareCache });
|
|
691
|
+
if (rows2.length === 0) {
|
|
692
|
+
return [];
|
|
693
|
+
}
|
|
694
|
+
return customResultMapper ? customResultMapper(rows2) : rows2.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
|
|
695
|
+
}
|
|
696
|
+
const rows = await executeOnClient(this.client, this.queryString, params, {
|
|
697
|
+
prepareCache: this.prepareCache
|
|
698
|
+
});
|
|
699
|
+
return rows;
|
|
170
700
|
}
|
|
171
701
|
all(placeholderValues = {}) {
|
|
172
702
|
return this.execute(placeholderValues);
|
|
173
703
|
}
|
|
174
704
|
isResponseInArrayMode() {
|
|
175
|
-
return
|
|
705
|
+
return this._isResponseInArrayMode;
|
|
176
706
|
}
|
|
177
707
|
}
|
|
178
708
|
|
|
@@ -181,33 +711,118 @@ class DuckDBSession extends PgSession {
|
|
|
181
711
|
schema;
|
|
182
712
|
options;
|
|
183
713
|
static [entityKind] = "DuckDBSession";
|
|
714
|
+
dialect;
|
|
184
715
|
logger;
|
|
716
|
+
rejectStringArrayLiterals;
|
|
717
|
+
prepareCache;
|
|
718
|
+
hasWarnedArrayLiteral = false;
|
|
719
|
+
rollbackOnly = false;
|
|
185
720
|
constructor(client, dialect, schema, options = {}) {
|
|
186
721
|
super(dialect);
|
|
187
722
|
this.client = client;
|
|
188
723
|
this.schema = schema;
|
|
189
724
|
this.options = options;
|
|
725
|
+
this.dialect = dialect;
|
|
190
726
|
this.logger = options.logger ?? new NoopLogger;
|
|
727
|
+
this.rejectStringArrayLiterals = options.rejectStringArrayLiterals ?? false;
|
|
728
|
+
this.prepareCache = options.prepareCache;
|
|
729
|
+
this.options = {
|
|
730
|
+
...options,
|
|
731
|
+
prepareCache: this.prepareCache
|
|
732
|
+
};
|
|
191
733
|
}
|
|
192
734
|
prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
|
|
193
|
-
return new DuckDBPreparedQuery(this.client, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper);
|
|
735
|
+
return new DuckDBPreparedQuery(this.client, this.dialect, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, this.rejectStringArrayLiterals, this.prepareCache, this.rejectStringArrayLiterals ? undefined : this.warnOnStringArrayLiteral);
|
|
194
736
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
737
|
+
execute(query) {
|
|
738
|
+
this.dialect.resetPgJsonFlag();
|
|
739
|
+
return super.execute(query);
|
|
740
|
+
}
|
|
741
|
+
all(query) {
|
|
742
|
+
this.dialect.resetPgJsonFlag();
|
|
743
|
+
return super.all(query);
|
|
744
|
+
}
|
|
745
|
+
async transaction(transaction, config) {
|
|
746
|
+
let pinnedConnection;
|
|
747
|
+
let pool;
|
|
748
|
+
let clientForTx = this.client;
|
|
749
|
+
if (isPool(this.client)) {
|
|
750
|
+
pool = this.client;
|
|
751
|
+
pinnedConnection = await pool.acquire();
|
|
752
|
+
clientForTx = pinnedConnection;
|
|
753
|
+
}
|
|
754
|
+
const session = new DuckDBSession(clientForTx, this.dialect, this.schema, this.options);
|
|
198
755
|
const tx = new DuckDBTransaction(this.dialect, session, this.schema);
|
|
199
|
-
await tx.execute(sql2`BEGIN TRANSACTION;`);
|
|
200
756
|
try {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
757
|
+
await tx.execute(sql`BEGIN TRANSACTION;`);
|
|
758
|
+
if (config) {
|
|
759
|
+
await tx.setTransaction(config);
|
|
760
|
+
}
|
|
761
|
+
try {
|
|
762
|
+
const result = await transaction(tx);
|
|
763
|
+
if (session.isRollbackOnly()) {
|
|
764
|
+
await tx.execute(sql`rollback`);
|
|
765
|
+
throw new TransactionRollbackError;
|
|
766
|
+
}
|
|
767
|
+
await tx.execute(sql`commit`);
|
|
768
|
+
return result;
|
|
769
|
+
} catch (error) {
|
|
770
|
+
await tx.execute(sql`rollback`);
|
|
771
|
+
throw error;
|
|
772
|
+
}
|
|
207
773
|
} finally {
|
|
208
|
-
|
|
774
|
+
if (pinnedConnection && pool) {
|
|
775
|
+
await pool.release(pinnedConnection);
|
|
776
|
+
}
|
|
209
777
|
}
|
|
210
778
|
}
|
|
779
|
+
warnOnStringArrayLiteral = (query) => {
|
|
780
|
+
if (this.hasWarnedArrayLiteral) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
this.hasWarnedArrayLiteral = true;
|
|
784
|
+
this.logger.logQuery(`[duckdb] ${arrayLiteralWarning}
|
|
785
|
+
query: ${query}`, []);
|
|
786
|
+
};
|
|
787
|
+
executeBatches(query, options = {}) {
|
|
788
|
+
this.dialect.resetPgJsonFlag();
|
|
789
|
+
const builtQuery = this.dialect.sqlToQuery(query);
|
|
790
|
+
this.dialect.assertNoPgJsonColumns();
|
|
791
|
+
const params = prepareParams(builtQuery.params, {
|
|
792
|
+
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
793
|
+
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
794
|
+
});
|
|
795
|
+
this.logger.logQuery(builtQuery.sql, params);
|
|
796
|
+
return executeInBatches(this.client, builtQuery.sql, params, options);
|
|
797
|
+
}
|
|
798
|
+
executeBatchesRaw(query, options = {}) {
|
|
799
|
+
this.dialect.resetPgJsonFlag();
|
|
800
|
+
const builtQuery = this.dialect.sqlToQuery(query);
|
|
801
|
+
this.dialect.assertNoPgJsonColumns();
|
|
802
|
+
const params = prepareParams(builtQuery.params, {
|
|
803
|
+
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
804
|
+
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
805
|
+
});
|
|
806
|
+
this.logger.logQuery(builtQuery.sql, params);
|
|
807
|
+
return executeInBatchesRaw(this.client, builtQuery.sql, params, options);
|
|
808
|
+
}
|
|
809
|
+
async executeArrow(query) {
|
|
810
|
+
this.dialect.resetPgJsonFlag();
|
|
811
|
+
const builtQuery = this.dialect.sqlToQuery(query);
|
|
812
|
+
this.dialect.assertNoPgJsonColumns();
|
|
813
|
+
const params = prepareParams(builtQuery.params, {
|
|
814
|
+
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
815
|
+
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
816
|
+
});
|
|
817
|
+
this.logger.logQuery(builtQuery.sql, params);
|
|
818
|
+
return executeArrowOnClient(this.client, builtQuery.sql, params);
|
|
819
|
+
}
|
|
820
|
+
markRollbackOnly() {
|
|
821
|
+
this.rollbackOnly = true;
|
|
822
|
+
}
|
|
823
|
+
isRollbackOnly() {
|
|
824
|
+
return this.rollbackOnly;
|
|
825
|
+
}
|
|
211
826
|
}
|
|
212
827
|
|
|
213
828
|
class DuckDBTransaction extends PgTransaction {
|
|
@@ -226,99 +841,1271 @@ class DuckDBTransaction extends PgTransaction {
|
|
|
226
841
|
if (typeof config.deferrable === "boolean") {
|
|
227
842
|
chunks.push(config.deferrable ? "deferrable" : "not deferrable");
|
|
228
843
|
}
|
|
229
|
-
return
|
|
844
|
+
return sql.raw(chunks.join(" "));
|
|
230
845
|
}
|
|
231
846
|
setTransaction(config) {
|
|
232
|
-
return this.session.execute(
|
|
847
|
+
return this.session.execute(sql`set transaction ${this.getTransactionConfigSQL(config)}`);
|
|
848
|
+
}
|
|
849
|
+
executeBatches(query, options = {}) {
|
|
850
|
+
return this.session.executeBatches(query, options);
|
|
851
|
+
}
|
|
852
|
+
executeBatchesRaw(query, options = {}) {
|
|
853
|
+
return this.session.executeBatchesRaw(query, options);
|
|
854
|
+
}
|
|
855
|
+
executeArrow(query) {
|
|
856
|
+
return this.session.executeArrow(query);
|
|
233
857
|
}
|
|
234
858
|
async transaction(transaction) {
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
|
|
859
|
+
const internals = this;
|
|
860
|
+
const savepoint = `drizzle_savepoint_${this.nestedIndex + 1}`;
|
|
861
|
+
const savepointSql = sql.raw(`savepoint ${savepoint}`);
|
|
862
|
+
const releaseSql = sql.raw(`release savepoint ${savepoint}`);
|
|
863
|
+
const rollbackSql = sql.raw(`rollback to savepoint ${savepoint}`);
|
|
864
|
+
const nestedTx = new DuckDBTransaction(internals.dialect, internals.session, this.schema, this.nestedIndex + 1);
|
|
865
|
+
if (internals.dialect.areSavepointsUnsupported()) {
|
|
866
|
+
return this.runNestedWithoutSavepoint(transaction, nestedTx, internals);
|
|
867
|
+
}
|
|
868
|
+
let createdSavepoint = false;
|
|
869
|
+
try {
|
|
870
|
+
await internals.session.execute(savepointSql);
|
|
871
|
+
internals.dialect.markSavepointsSupported();
|
|
872
|
+
createdSavepoint = true;
|
|
873
|
+
} catch (error) {
|
|
874
|
+
if (!isSavepointSyntaxError(error)) {
|
|
875
|
+
throw error;
|
|
876
|
+
}
|
|
877
|
+
internals.dialect.markSavepointsUnsupported();
|
|
878
|
+
return this.runNestedWithoutSavepoint(transaction, nestedTx, internals);
|
|
879
|
+
}
|
|
238
880
|
try {
|
|
239
|
-
const result = await transaction(
|
|
240
|
-
|
|
881
|
+
const result = await transaction(nestedTx);
|
|
882
|
+
if (createdSavepoint) {
|
|
883
|
+
await internals.session.execute(releaseSql);
|
|
884
|
+
}
|
|
241
885
|
return result;
|
|
242
|
-
} catch (
|
|
243
|
-
|
|
244
|
-
|
|
886
|
+
} catch (error) {
|
|
887
|
+
if (createdSavepoint) {
|
|
888
|
+
await internals.session.execute(rollbackSql);
|
|
889
|
+
}
|
|
890
|
+
internals.session.markRollbackOnly();
|
|
891
|
+
throw error;
|
|
245
892
|
}
|
|
246
893
|
}
|
|
894
|
+
runNestedWithoutSavepoint(transaction, nestedTx, internals) {
|
|
895
|
+
return transaction(nestedTx).catch((error) => {
|
|
896
|
+
internals.session.markRollbackOnly();
|
|
897
|
+
throw error;
|
|
898
|
+
});
|
|
899
|
+
}
|
|
247
900
|
}
|
|
901
|
+
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.";
|
|
248
902
|
|
|
249
903
|
// src/dialect.ts
|
|
250
904
|
import { entityKind as entityKind2, is as is2 } from "drizzle-orm/entity";
|
|
251
905
|
import {
|
|
252
|
-
PgDate,
|
|
253
|
-
PgDateString,
|
|
906
|
+
PgDate as PgDate2,
|
|
907
|
+
PgDateString as PgDateString2,
|
|
254
908
|
PgDialect,
|
|
255
909
|
PgJson,
|
|
256
910
|
PgJsonb,
|
|
257
911
|
PgNumeric,
|
|
258
|
-
PgTime,
|
|
259
|
-
PgTimestamp,
|
|
260
|
-
PgTimestampString,
|
|
912
|
+
PgTime as PgTime2,
|
|
913
|
+
PgTimestamp as PgTimestamp2,
|
|
914
|
+
PgTimestampString as PgTimestampString2,
|
|
261
915
|
PgUUID
|
|
262
916
|
} from "drizzle-orm/pg-core";
|
|
263
917
|
import {
|
|
264
|
-
sql as
|
|
918
|
+
sql as sql2
|
|
265
919
|
} from "drizzle-orm";
|
|
266
920
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
921
|
+
// src/sql/ast-transformer.ts
|
|
922
|
+
import nodeSqlParser from "node-sql-parser";
|
|
923
|
+
|
|
924
|
+
// src/sql/visitors/array-operators.ts
|
|
925
|
+
var OPERATOR_MAP = {
|
|
926
|
+
"@>": { fn: "array_has_all" },
|
|
927
|
+
"<@": { fn: "array_has_all", swap: true },
|
|
928
|
+
"&&": { fn: "array_has_any" }
|
|
929
|
+
};
|
|
930
|
+
function walkExpression(expr, parent, key) {
|
|
931
|
+
if (!expr || typeof expr !== "object")
|
|
932
|
+
return false;
|
|
933
|
+
let transformed = false;
|
|
934
|
+
const exprObj = expr;
|
|
935
|
+
if ("type" in expr && exprObj.type === "binary_expr") {
|
|
936
|
+
const binary = expr;
|
|
937
|
+
const mapping = OPERATOR_MAP[binary.operator];
|
|
938
|
+
if (mapping) {
|
|
939
|
+
const fnExpr = {
|
|
940
|
+
type: "function",
|
|
941
|
+
name: { name: [{ type: "default", value: mapping.fn }] },
|
|
942
|
+
args: {
|
|
943
|
+
type: "expr_list",
|
|
944
|
+
value: mapping.swap ? [binary.right, binary.left] : [binary.left, binary.right]
|
|
291
945
|
}
|
|
946
|
+
};
|
|
947
|
+
if (parent && key) {
|
|
948
|
+
parent[key] = fnExpr;
|
|
292
949
|
}
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
prepareTyping(encoder) {
|
|
296
|
-
if (is2(encoder, PgJsonb) || is2(encoder, PgJson)) {
|
|
297
|
-
throw new Error("JSON and JSONB types are not supported in DuckDB");
|
|
298
|
-
} else if (is2(encoder, PgNumeric)) {
|
|
299
|
-
return "decimal";
|
|
300
|
-
} else if (is2(encoder, PgTime)) {
|
|
301
|
-
return "time";
|
|
302
|
-
} else if (is2(encoder, PgTimestamp) || is2(encoder, PgTimestampString)) {
|
|
303
|
-
return "timestamp";
|
|
304
|
-
} else if (is2(encoder, PgDate) || is2(encoder, PgDateString)) {
|
|
305
|
-
return "date";
|
|
306
|
-
} else if (is2(encoder, PgUUID)) {
|
|
307
|
-
return "uuid";
|
|
950
|
+
transformed = true;
|
|
308
951
|
} else {
|
|
309
|
-
|
|
952
|
+
transformed = walkExpression(binary.left, binary, "left") || transformed;
|
|
953
|
+
transformed = walkExpression(binary.right, binary, "right") || transformed;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
if ("type" in expr && exprObj.type === "unary_expr") {
|
|
957
|
+
if ("expr" in exprObj) {
|
|
958
|
+
transformed = walkExpression(exprObj.expr, exprObj, "expr") || transformed;
|
|
310
959
|
}
|
|
311
960
|
}
|
|
961
|
+
if ("type" in expr && exprObj.type === "case") {
|
|
962
|
+
if ("expr" in exprObj && exprObj.expr) {
|
|
963
|
+
transformed = walkExpression(exprObj.expr, exprObj, "expr") || transformed;
|
|
964
|
+
}
|
|
965
|
+
if ("args" in exprObj && Array.isArray(exprObj.args)) {
|
|
966
|
+
for (let i = 0;i < exprObj.args.length; i++) {
|
|
967
|
+
const whenClause = exprObj.args[i];
|
|
968
|
+
if (whenClause.cond) {
|
|
969
|
+
transformed = walkExpression(whenClause.cond, whenClause, "cond") || transformed;
|
|
970
|
+
}
|
|
971
|
+
if (whenClause.result) {
|
|
972
|
+
transformed = walkExpression(whenClause.result, whenClause, "result") || transformed;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if ("args" in expr && exprObj.args) {
|
|
978
|
+
const args = exprObj.args;
|
|
979
|
+
if ("value" in args && Array.isArray(args.value)) {
|
|
980
|
+
for (let i = 0;i < args.value.length; i++) {
|
|
981
|
+
transformed = walkExpression(args.value[i], args.value, String(i)) || transformed;
|
|
982
|
+
}
|
|
983
|
+
} else if ("expr" in args) {
|
|
984
|
+
transformed = walkExpression(args.expr, args, "expr") || transformed;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if ("ast" in exprObj && exprObj.ast) {
|
|
988
|
+
const subAst = exprObj.ast;
|
|
989
|
+
if (subAst.type === "select") {
|
|
990
|
+
transformed = walkSelectImpl(subAst) || transformed;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
if ("type" in expr && exprObj.type === "expr_list") {
|
|
994
|
+
if ("value" in exprObj && Array.isArray(exprObj.value)) {
|
|
995
|
+
for (let i = 0;i < exprObj.value.length; i++) {
|
|
996
|
+
transformed = walkExpression(exprObj.value[i], exprObj.value, String(i)) || transformed;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
return transformed;
|
|
312
1001
|
}
|
|
313
|
-
|
|
314
|
-
|
|
1002
|
+
function walkFrom(from) {
|
|
1003
|
+
if (!from || !Array.isArray(from))
|
|
1004
|
+
return false;
|
|
1005
|
+
let transformed = false;
|
|
1006
|
+
for (const f of from) {
|
|
1007
|
+
if ("join" in f) {
|
|
1008
|
+
const join = f;
|
|
1009
|
+
transformed = walkExpression(join.on, join, "on") || transformed;
|
|
1010
|
+
}
|
|
1011
|
+
if ("expr" in f && f.expr && "ast" in f.expr) {
|
|
1012
|
+
transformed = walkSelectImpl(f.expr.ast) || transformed;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return transformed;
|
|
1016
|
+
}
|
|
1017
|
+
function walkSelectImpl(select) {
|
|
1018
|
+
let transformed = false;
|
|
1019
|
+
if (select.with) {
|
|
1020
|
+
for (const cte of select.with) {
|
|
1021
|
+
const cteSelect = cte.stmt?.ast ?? cte.stmt;
|
|
1022
|
+
if (cteSelect && cteSelect.type === "select") {
|
|
1023
|
+
transformed = walkSelectImpl(cteSelect) || transformed;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
if (Array.isArray(select.from)) {
|
|
1028
|
+
transformed = walkFrom(select.from) || transformed;
|
|
1029
|
+
}
|
|
1030
|
+
transformed = walkExpression(select.where, select, "where") || transformed;
|
|
1031
|
+
if (select.having) {
|
|
1032
|
+
if (Array.isArray(select.having)) {
|
|
1033
|
+
for (let i = 0;i < select.having.length; i++) {
|
|
1034
|
+
transformed = walkExpression(select.having[i], select.having, String(i)) || transformed;
|
|
1035
|
+
}
|
|
1036
|
+
} else {
|
|
1037
|
+
transformed = walkExpression(select.having, select, "having") || transformed;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (Array.isArray(select.columns)) {
|
|
1041
|
+
for (const col of select.columns) {
|
|
1042
|
+
if ("expr" in col) {
|
|
1043
|
+
transformed = walkExpression(col.expr, col, "expr") || transformed;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
if (select._next) {
|
|
1048
|
+
transformed = walkSelectImpl(select._next) || transformed;
|
|
1049
|
+
}
|
|
1050
|
+
return transformed;
|
|
1051
|
+
}
|
|
1052
|
+
function transformArrayOperators(ast) {
|
|
1053
|
+
const statements = Array.isArray(ast) ? ast : [ast];
|
|
1054
|
+
let transformed = false;
|
|
1055
|
+
for (const stmt of statements) {
|
|
1056
|
+
if (stmt.type === "select") {
|
|
1057
|
+
transformed = walkSelectImpl(stmt) || transformed;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return transformed;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/sql/visitors/column-qualifier.ts
|
|
1064
|
+
function getTableSource(from) {
|
|
1065
|
+
if ("table" in from && from.table) {
|
|
1066
|
+
return {
|
|
1067
|
+
name: from.table,
|
|
1068
|
+
alias: from.as ?? null,
|
|
1069
|
+
schema: "db" in from ? from.db ?? null : null
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
if ("expr" in from && from.as) {
|
|
1073
|
+
return {
|
|
1074
|
+
name: from.as,
|
|
1075
|
+
alias: from.as,
|
|
1076
|
+
schema: null
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
function getQualifier(source) {
|
|
1082
|
+
return {
|
|
1083
|
+
table: source.alias ?? source.name,
|
|
1084
|
+
schema: source.schema
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
function isUnqualifiedColumnRef(expr) {
|
|
1088
|
+
return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && (!("table" in expr) || !expr.table);
|
|
1089
|
+
}
|
|
1090
|
+
function isQualifiedColumnRef(expr) {
|
|
1091
|
+
return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref" && "table" in expr && !!expr.table;
|
|
1092
|
+
}
|
|
1093
|
+
function getColumnName(col) {
|
|
1094
|
+
if (typeof col.column === "string") {
|
|
1095
|
+
return col.column;
|
|
1096
|
+
}
|
|
1097
|
+
if (col.column && "expr" in col.column && col.column.expr?.value) {
|
|
1098
|
+
return String(col.column.expr.value);
|
|
1099
|
+
}
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
function applyQualifier(col, qualifier) {
|
|
1103
|
+
col.table = qualifier.table;
|
|
1104
|
+
if (!("schema" in col) || !col.schema) {
|
|
1105
|
+
col.schema = qualifier.schema;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
function unwrapColumnRef(expr) {
|
|
1109
|
+
if (!expr || typeof expr !== "object")
|
|
1110
|
+
return null;
|
|
1111
|
+
if ("type" in expr && expr.type === "column_ref") {
|
|
1112
|
+
return expr;
|
|
1113
|
+
}
|
|
1114
|
+
if ("expr" in expr && expr.expr) {
|
|
1115
|
+
return unwrapColumnRef(expr.expr);
|
|
1116
|
+
}
|
|
1117
|
+
if ("ast" in expr && expr.ast && typeof expr.ast === "object") {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
if ("args" in expr && expr.args) {
|
|
1121
|
+
const args = expr.args;
|
|
1122
|
+
if (args.expr) {
|
|
1123
|
+
return unwrapColumnRef(args.expr);
|
|
1124
|
+
}
|
|
1125
|
+
if (args.value && args.value.length === 1) {
|
|
1126
|
+
return unwrapColumnRef(args.value[0]);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
function isBinaryExpr(expr) {
|
|
1132
|
+
return !!expr && typeof expr === "object" && "type" in expr && expr.type === "binary_expr";
|
|
1133
|
+
}
|
|
1134
|
+
function walkOnClause(expr, leftQualifier, rightQualifier, ambiguousColumns) {
|
|
1135
|
+
if (!expr || typeof expr !== "object")
|
|
1136
|
+
return false;
|
|
1137
|
+
let transformed = false;
|
|
1138
|
+
if (isBinaryExpr(expr)) {
|
|
1139
|
+
const left = expr.left;
|
|
1140
|
+
const right = expr.right;
|
|
1141
|
+
const leftCol = unwrapColumnRef(left);
|
|
1142
|
+
const rightCol = unwrapColumnRef(right);
|
|
1143
|
+
const leftUnqualified = leftCol ? isUnqualifiedColumnRef(leftCol) : false;
|
|
1144
|
+
const rightUnqualified = rightCol ? isUnqualifiedColumnRef(rightCol) : false;
|
|
1145
|
+
const leftQualified = leftCol ? isQualifiedColumnRef(leftCol) : false;
|
|
1146
|
+
const rightQualified = rightCol ? isQualifiedColumnRef(rightCol) : false;
|
|
1147
|
+
const leftColName = leftCol ? getColumnName(leftCol) : null;
|
|
1148
|
+
const rightColName = rightCol ? getColumnName(rightCol) : null;
|
|
1149
|
+
if (expr.operator === "=" && leftColName && rightColName && leftColName === rightColName) {
|
|
1150
|
+
if (leftUnqualified && rightUnqualified) {
|
|
1151
|
+
applyQualifier(leftCol, leftQualifier);
|
|
1152
|
+
applyQualifier(rightCol, rightQualifier);
|
|
1153
|
+
ambiguousColumns.add(leftColName);
|
|
1154
|
+
transformed = true;
|
|
1155
|
+
} else if (leftQualified && rightUnqualified) {
|
|
1156
|
+
applyQualifier(rightCol, rightQualifier);
|
|
1157
|
+
ambiguousColumns.add(rightColName);
|
|
1158
|
+
transformed = true;
|
|
1159
|
+
} else if (leftUnqualified && rightQualified) {
|
|
1160
|
+
applyQualifier(leftCol, leftQualifier);
|
|
1161
|
+
ambiguousColumns.add(leftColName);
|
|
1162
|
+
transformed = true;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (expr.operator === "=" && leftCol && rightCol && leftColName && rightColName && leftColName !== rightColName) {
|
|
1166
|
+
if (leftQualified && rightUnqualified && !rightColName.includes(".")) {
|
|
1167
|
+
applyQualifier(rightCol, rightQualifier);
|
|
1168
|
+
transformed = true;
|
|
1169
|
+
} else if (leftUnqualified && rightQualified && !leftColName.includes(".")) {
|
|
1170
|
+
applyQualifier(leftCol, leftQualifier);
|
|
1171
|
+
transformed = true;
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
transformed = walkOnClause(isBinaryExpr(expr.left) ? expr.left : expr.left, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
|
|
1175
|
+
transformed = walkOnClause(isBinaryExpr(expr.right) ? expr.right : expr.right, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
|
|
1176
|
+
}
|
|
1177
|
+
return transformed;
|
|
1178
|
+
}
|
|
1179
|
+
function qualifyAmbiguousInExpression(expr, defaultQualifier, ambiguousColumns) {
|
|
1180
|
+
if (!expr || typeof expr !== "object")
|
|
1181
|
+
return false;
|
|
1182
|
+
let transformed = false;
|
|
1183
|
+
if (isUnqualifiedColumnRef(expr)) {
|
|
1184
|
+
const colName = getColumnName(expr);
|
|
1185
|
+
if (colName && ambiguousColumns.has(colName)) {
|
|
1186
|
+
applyQualifier(expr, defaultQualifier);
|
|
1187
|
+
transformed = true;
|
|
1188
|
+
}
|
|
1189
|
+
return transformed;
|
|
1190
|
+
}
|
|
1191
|
+
if (isBinaryExpr(expr)) {
|
|
1192
|
+
const binary = expr;
|
|
1193
|
+
transformed = qualifyAmbiguousInExpression(binary.left, defaultQualifier, ambiguousColumns) || transformed;
|
|
1194
|
+
transformed = qualifyAmbiguousInExpression(binary.right, defaultQualifier, ambiguousColumns) || transformed;
|
|
1195
|
+
return transformed;
|
|
1196
|
+
}
|
|
1197
|
+
if ("args" in expr && expr.args) {
|
|
1198
|
+
const args = expr.args;
|
|
1199
|
+
if (args.value && Array.isArray(args.value)) {
|
|
1200
|
+
for (const arg of args.value) {
|
|
1201
|
+
transformed = qualifyAmbiguousInExpression(arg, defaultQualifier, ambiguousColumns) || transformed;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
if (args.expr) {
|
|
1205
|
+
transformed = qualifyAmbiguousInExpression(args.expr, defaultQualifier, ambiguousColumns) || transformed;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
if ("over" in expr && expr.over && typeof expr.over === "object") {
|
|
1209
|
+
const over = expr.over;
|
|
1210
|
+
if (Array.isArray(over.partition)) {
|
|
1211
|
+
for (const part of over.partition) {
|
|
1212
|
+
transformed = qualifyAmbiguousInExpression(part, defaultQualifier, ambiguousColumns) || transformed;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
if (Array.isArray(over.orderby)) {
|
|
1216
|
+
for (const order of over.orderby) {
|
|
1217
|
+
transformed = qualifyAmbiguousInExpression(order, defaultQualifier, ambiguousColumns) || transformed;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
return transformed;
|
|
1222
|
+
}
|
|
1223
|
+
function hasUnqualifiedColumns(expr) {
|
|
1224
|
+
if (!expr || typeof expr !== "object")
|
|
1225
|
+
return false;
|
|
1226
|
+
if ("type" in expr && expr.type === "binary_expr") {
|
|
1227
|
+
const left = expr.left;
|
|
1228
|
+
const right = expr.right;
|
|
1229
|
+
const leftCol = unwrapColumnRef(left);
|
|
1230
|
+
const rightCol = unwrapColumnRef(right);
|
|
1231
|
+
if (isUnqualifiedColumnRef(left) || isUnqualifiedColumnRef(right) || leftCol && isUnqualifiedColumnRef(leftCol) || rightCol && isUnqualifiedColumnRef(rightCol)) {
|
|
1232
|
+
return true;
|
|
1233
|
+
}
|
|
1234
|
+
if (isBinaryExpr(expr.left) && hasUnqualifiedColumns(expr.left))
|
|
1235
|
+
return true;
|
|
1236
|
+
if (isBinaryExpr(expr.right) && hasUnqualifiedColumns(expr.right))
|
|
1237
|
+
return true;
|
|
1238
|
+
}
|
|
1239
|
+
if ("args" in expr && expr.args) {
|
|
1240
|
+
const args = expr.args;
|
|
1241
|
+
if (args.expr && isUnqualifiedColumnRef(args.expr))
|
|
1242
|
+
return true;
|
|
1243
|
+
if (args.value) {
|
|
1244
|
+
for (const arg of args.value) {
|
|
1245
|
+
if (isUnqualifiedColumnRef(arg))
|
|
1246
|
+
return true;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
function walkSelect(select) {
|
|
1253
|
+
let transformed = false;
|
|
1254
|
+
const ambiguousColumns = new Set;
|
|
1255
|
+
if (Array.isArray(select.from) && select.from.length >= 2) {
|
|
1256
|
+
const firstSource = getTableSource(select.from[0]);
|
|
1257
|
+
const defaultQualifier = firstSource ? getQualifier(firstSource) : null;
|
|
1258
|
+
let prevSource = firstSource;
|
|
1259
|
+
let hasAnyUnqualified = false;
|
|
1260
|
+
for (const from of select.from) {
|
|
1261
|
+
if ("join" in from) {
|
|
1262
|
+
const join = from;
|
|
1263
|
+
if (join.on && hasUnqualifiedColumns(join.on)) {
|
|
1264
|
+
hasAnyUnqualified = true;
|
|
1265
|
+
break;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
if (!hasAnyUnqualified) {
|
|
1270
|
+
for (const from of select.from) {
|
|
1271
|
+
if ("expr" in from && from.expr && "ast" in from.expr) {
|
|
1272
|
+
transformed = walkSelect(from.expr.ast) || transformed;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
} else {
|
|
1276
|
+
for (const from of select.from) {
|
|
1277
|
+
if ("join" in from) {
|
|
1278
|
+
const join = from;
|
|
1279
|
+
const currentSource = getTableSource(join);
|
|
1280
|
+
if (join.on && prevSource && currentSource) {
|
|
1281
|
+
const leftQualifier = getQualifier(prevSource);
|
|
1282
|
+
const rightQualifier = getQualifier(currentSource);
|
|
1283
|
+
transformed = walkOnClause(join.on, leftQualifier, rightQualifier, ambiguousColumns) || transformed;
|
|
1284
|
+
}
|
|
1285
|
+
if (join.using && prevSource && currentSource) {
|
|
1286
|
+
for (const usingCol of join.using) {
|
|
1287
|
+
if (typeof usingCol === "string") {
|
|
1288
|
+
ambiguousColumns.add(usingCol);
|
|
1289
|
+
} else if ("value" in usingCol) {
|
|
1290
|
+
ambiguousColumns.add(String(usingCol.value));
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
prevSource = currentSource;
|
|
1295
|
+
} else {
|
|
1296
|
+
const source = getTableSource(from);
|
|
1297
|
+
if (source) {
|
|
1298
|
+
prevSource = source;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
if ("expr" in from && from.expr && "ast" in from.expr) {
|
|
1302
|
+
transformed = walkSelect(from.expr.ast) || transformed;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
if (ambiguousColumns.size > 0 && defaultQualifier) {
|
|
1306
|
+
if (Array.isArray(select.columns)) {
|
|
1307
|
+
for (const col of select.columns) {
|
|
1308
|
+
if ("expr" in col) {
|
|
1309
|
+
transformed = qualifyAmbiguousInExpression(col.expr, defaultQualifier, ambiguousColumns) || transformed;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
transformed = qualifyAmbiguousInExpression(select.where, defaultQualifier, ambiguousColumns) || transformed;
|
|
1314
|
+
if (Array.isArray(select.orderby)) {
|
|
1315
|
+
for (const order of select.orderby) {
|
|
1316
|
+
if (order.expr) {
|
|
1317
|
+
transformed = qualifyAmbiguousInExpression(order.expr, defaultQualifier, ambiguousColumns) || transformed;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
if (select.with) {
|
|
1325
|
+
for (const cte of select.with) {
|
|
1326
|
+
const cteSelect = cte.stmt?.ast ?? cte.stmt;
|
|
1327
|
+
if (cteSelect && cteSelect.type === "select") {
|
|
1328
|
+
transformed = walkSelect(cteSelect) || transformed;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
if (select._next) {
|
|
1333
|
+
transformed = walkSelect(select._next) || transformed;
|
|
1334
|
+
}
|
|
1335
|
+
return transformed;
|
|
1336
|
+
}
|
|
1337
|
+
function qualifyJoinColumns(ast) {
|
|
1338
|
+
const statements = Array.isArray(ast) ? ast : [ast];
|
|
1339
|
+
let transformed = false;
|
|
1340
|
+
for (const stmt of statements) {
|
|
1341
|
+
if (stmt.type === "select") {
|
|
1342
|
+
transformed = walkSelect(stmt) || transformed;
|
|
1343
|
+
} else if (stmt.type === "insert") {
|
|
1344
|
+
const insert = stmt;
|
|
1345
|
+
if (insert.values && typeof insert.values === "object" && "type" in insert.values && insert.values.type === "select") {
|
|
1346
|
+
transformed = walkSelect(insert.values) || transformed;
|
|
1347
|
+
}
|
|
1348
|
+
} else if (stmt.type === "update") {
|
|
1349
|
+
const update = stmt;
|
|
1350
|
+
const mainSource = update.table?.[0] ? getTableSource(update.table[0]) : null;
|
|
1351
|
+
const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
|
|
1352
|
+
const fromSources = update.from ?? [];
|
|
1353
|
+
const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
|
|
1354
|
+
if (update.where && defaultQualifier && firstFrom) {
|
|
1355
|
+
const ambiguous = new Set;
|
|
1356
|
+
transformed = walkOnClause(update.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
|
|
1357
|
+
transformed = qualifyAmbiguousInExpression(update.where, defaultQualifier, ambiguous) || transformed;
|
|
1358
|
+
}
|
|
1359
|
+
if (Array.isArray(update.returning) && defaultQualifier) {
|
|
1360
|
+
for (const ret of update.returning) {
|
|
1361
|
+
transformed = qualifyAmbiguousInExpression(ret, defaultQualifier, new Set) || transformed;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
} else if (stmt.type === "delete") {
|
|
1365
|
+
const del = stmt;
|
|
1366
|
+
const mainSource = del.table?.[0] ? getTableSource(del.table[0]) : null;
|
|
1367
|
+
const defaultQualifier = mainSource ? getQualifier(mainSource) : null;
|
|
1368
|
+
const fromSources = del.from ?? [];
|
|
1369
|
+
const firstFrom = fromSources[0] ? getTableSource(fromSources[0]) : null;
|
|
1370
|
+
if (del.where && defaultQualifier && firstFrom) {
|
|
1371
|
+
const ambiguous = new Set;
|
|
1372
|
+
transformed = walkOnClause(del.where, defaultQualifier, getQualifier(firstFrom), ambiguous) || transformed;
|
|
1373
|
+
transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, ambiguous) || transformed;
|
|
1374
|
+
} else if (del.where && defaultQualifier) {
|
|
1375
|
+
transformed = qualifyAmbiguousInExpression(del.where, defaultQualifier, new Set) || transformed;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
return transformed;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// src/sql/visitors/generate-series-alias.ts
|
|
1383
|
+
function getColumnName2(col) {
|
|
1384
|
+
if (typeof col.column === "string") {
|
|
1385
|
+
return col.column;
|
|
1386
|
+
}
|
|
1387
|
+
if (col.column && "expr" in col.column && col.column.expr?.value) {
|
|
1388
|
+
return String(col.column.expr.value);
|
|
1389
|
+
}
|
|
1390
|
+
return null;
|
|
1391
|
+
}
|
|
1392
|
+
function isColumnRef(expr) {
|
|
1393
|
+
return typeof expr === "object" && expr !== null && "type" in expr && expr.type === "column_ref";
|
|
1394
|
+
}
|
|
1395
|
+
function isBinaryExpr2(expr) {
|
|
1396
|
+
return !!expr && typeof expr === "object" && "type" in expr && expr.type === "binary_expr";
|
|
1397
|
+
}
|
|
1398
|
+
function getGenerateSeriesAliases(from) {
|
|
1399
|
+
const aliases = new Set;
|
|
1400
|
+
if (!from || !Array.isArray(from))
|
|
1401
|
+
return aliases;
|
|
1402
|
+
for (const f of from) {
|
|
1403
|
+
if ("expr" in f && f.expr && typeof f.expr === "object") {
|
|
1404
|
+
const exprObj = f.expr;
|
|
1405
|
+
if (exprObj.type === "function" && "name" in exprObj) {
|
|
1406
|
+
const nameObj = exprObj.name;
|
|
1407
|
+
const nameParts = nameObj?.name;
|
|
1408
|
+
const fnName = nameParts?.[0]?.value;
|
|
1409
|
+
if (typeof fnName === "string" && fnName.toLowerCase() === "generate_series") {
|
|
1410
|
+
const alias = typeof f.as === "string" ? f.as : null;
|
|
1411
|
+
if (alias && !alias.includes("(")) {
|
|
1412
|
+
aliases.add(alias);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
return aliases;
|
|
1419
|
+
}
|
|
1420
|
+
function rewriteAliasColumnRef(col, alias) {
|
|
1421
|
+
col.table = alias;
|
|
1422
|
+
col.column = { expr: { type: "default", value: "generate_series" } };
|
|
1423
|
+
}
|
|
1424
|
+
function walkExpression2(expr, aliases) {
|
|
1425
|
+
if (!expr || typeof expr !== "object")
|
|
1426
|
+
return false;
|
|
1427
|
+
let transformed = false;
|
|
1428
|
+
const exprObj = expr;
|
|
1429
|
+
if (isColumnRef(expr)) {
|
|
1430
|
+
if (!("table" in expr) || !expr.table) {
|
|
1431
|
+
const colName = getColumnName2(expr);
|
|
1432
|
+
if (colName && aliases.has(colName)) {
|
|
1433
|
+
rewriteAliasColumnRef(expr, colName);
|
|
1434
|
+
transformed = true;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
return transformed;
|
|
1438
|
+
}
|
|
1439
|
+
if (isBinaryExpr2(expr)) {
|
|
1440
|
+
const binary = expr;
|
|
1441
|
+
transformed = walkExpression2(binary.left, aliases) || transformed;
|
|
1442
|
+
transformed = walkExpression2(binary.right, aliases) || transformed;
|
|
1443
|
+
return transformed;
|
|
1444
|
+
}
|
|
1445
|
+
if (exprObj.type === "unary_expr" && exprObj.expr) {
|
|
1446
|
+
transformed = walkExpression2(exprObj.expr, aliases) || transformed;
|
|
1447
|
+
}
|
|
1448
|
+
if (exprObj.type === "cast" && exprObj.expr) {
|
|
1449
|
+
transformed = walkExpression2(exprObj.expr, aliases) || transformed;
|
|
1450
|
+
}
|
|
1451
|
+
if (exprObj.type === "case") {
|
|
1452
|
+
if (exprObj.expr) {
|
|
1453
|
+
transformed = walkExpression2(exprObj.expr, aliases) || transformed;
|
|
1454
|
+
}
|
|
1455
|
+
if (Array.isArray(exprObj.args)) {
|
|
1456
|
+
for (const whenClause of exprObj.args) {
|
|
1457
|
+
if (whenClause.cond) {
|
|
1458
|
+
transformed = walkExpression2(whenClause.cond, aliases) || transformed;
|
|
1459
|
+
}
|
|
1460
|
+
if (whenClause.result) {
|
|
1461
|
+
transformed = walkExpression2(whenClause.result, aliases) || transformed;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
if ("args" in exprObj && exprObj.args) {
|
|
1467
|
+
const args = exprObj.args;
|
|
1468
|
+
if (Array.isArray(args.value)) {
|
|
1469
|
+
for (const arg of args.value) {
|
|
1470
|
+
transformed = walkExpression2(arg, aliases) || transformed;
|
|
1471
|
+
}
|
|
1472
|
+
} else if (args.expr) {
|
|
1473
|
+
transformed = walkExpression2(args.expr, aliases) || transformed;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
if ("over" in exprObj && exprObj.over && typeof exprObj.over === "object") {
|
|
1477
|
+
const over = exprObj.over;
|
|
1478
|
+
if (Array.isArray(over.partition)) {
|
|
1479
|
+
for (const part of over.partition) {
|
|
1480
|
+
transformed = walkExpression2(part, aliases) || transformed;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
if (Array.isArray(over.orderby)) {
|
|
1484
|
+
for (const order of over.orderby) {
|
|
1485
|
+
transformed = walkExpression2(order, aliases) || transformed;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
if ("ast" in exprObj && exprObj.ast) {
|
|
1490
|
+
const subAst = exprObj.ast;
|
|
1491
|
+
if (subAst.type === "select") {
|
|
1492
|
+
transformed = walkSelect2(subAst) || transformed;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (exprObj.type === "expr_list" && Array.isArray(exprObj.value)) {
|
|
1496
|
+
for (const item of exprObj.value) {
|
|
1497
|
+
transformed = walkExpression2(item, aliases) || transformed;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return transformed;
|
|
1501
|
+
}
|
|
1502
|
+
function walkFrom2(from, aliases) {
|
|
1503
|
+
if (!from || !Array.isArray(from))
|
|
1504
|
+
return false;
|
|
1505
|
+
let transformed = false;
|
|
1506
|
+
for (const f of from) {
|
|
1507
|
+
if ("join" in f) {
|
|
1508
|
+
const join = f;
|
|
1509
|
+
transformed = walkExpression2(join.on, aliases) || transformed;
|
|
1510
|
+
}
|
|
1511
|
+
if ("expr" in f && f.expr && "ast" in f.expr) {
|
|
1512
|
+
transformed = walkSelect2(f.expr.ast) || transformed;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
return transformed;
|
|
1516
|
+
}
|
|
1517
|
+
function walkSelect2(select) {
|
|
1518
|
+
let transformed = false;
|
|
1519
|
+
const aliases = getGenerateSeriesAliases(select.from);
|
|
1520
|
+
if (select.with) {
|
|
1521
|
+
for (const cte of select.with) {
|
|
1522
|
+
const cteSelect = cte.stmt?.ast ?? cte.stmt;
|
|
1523
|
+
if (cteSelect && cteSelect.type === "select") {
|
|
1524
|
+
transformed = walkSelect2(cteSelect) || transformed;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
transformed = walkFrom2(select.from, aliases) || transformed;
|
|
1529
|
+
transformed = walkExpression2(select.where, aliases) || transformed;
|
|
1530
|
+
if (select.having) {
|
|
1531
|
+
if (Array.isArray(select.having)) {
|
|
1532
|
+
for (const h of select.having) {
|
|
1533
|
+
transformed = walkExpression2(h, aliases) || transformed;
|
|
1534
|
+
}
|
|
1535
|
+
} else {
|
|
1536
|
+
transformed = walkExpression2(select.having, aliases) || transformed;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
if (Array.isArray(select.columns)) {
|
|
1540
|
+
for (const col of select.columns) {
|
|
1541
|
+
if ("expr" in col) {
|
|
1542
|
+
transformed = walkExpression2(col.expr, aliases) || transformed;
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
if (Array.isArray(select.groupby)) {
|
|
1547
|
+
for (const g of select.groupby) {
|
|
1548
|
+
transformed = walkExpression2(g, aliases) || transformed;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
if (Array.isArray(select.orderby)) {
|
|
1552
|
+
for (const order of select.orderby) {
|
|
1553
|
+
if (order.expr) {
|
|
1554
|
+
transformed = walkExpression2(order.expr, aliases) || transformed;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
if (select._orderby) {
|
|
1559
|
+
for (const order of select._orderby) {
|
|
1560
|
+
if (order.expr) {
|
|
1561
|
+
transformed = walkExpression2(order.expr, aliases) || transformed;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
if (select._next) {
|
|
1566
|
+
transformed = walkSelect2(select._next) || transformed;
|
|
1567
|
+
}
|
|
1568
|
+
return transformed;
|
|
1569
|
+
}
|
|
1570
|
+
function rewriteGenerateSeriesAliases(ast) {
|
|
1571
|
+
const statements = Array.isArray(ast) ? ast : [ast];
|
|
1572
|
+
let transformed = false;
|
|
1573
|
+
for (const stmt of statements) {
|
|
1574
|
+
if (stmt.type === "select") {
|
|
1575
|
+
transformed = walkSelect2(stmt) || transformed;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return transformed;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// src/sql/visitors/union-with-hoister.ts
|
|
1582
|
+
function getCteName(cte) {
|
|
1583
|
+
const nameObj = cte.name;
|
|
1584
|
+
if (!nameObj)
|
|
1585
|
+
return null;
|
|
1586
|
+
const value = nameObj.value;
|
|
1587
|
+
if (typeof value === "string")
|
|
1588
|
+
return value;
|
|
1589
|
+
return null;
|
|
1590
|
+
}
|
|
1591
|
+
function hoistWithInSelect(select) {
|
|
1592
|
+
if (!select.set_op || !select._next)
|
|
1593
|
+
return false;
|
|
1594
|
+
const arms = [];
|
|
1595
|
+
let current = select;
|
|
1596
|
+
while (current && current.type === "select") {
|
|
1597
|
+
arms.push(current);
|
|
1598
|
+
current = current._next;
|
|
1599
|
+
}
|
|
1600
|
+
const mergedWith = [];
|
|
1601
|
+
const seen = new Set;
|
|
1602
|
+
let hasWithBeyondFirst = false;
|
|
1603
|
+
for (const arm of arms) {
|
|
1604
|
+
if (arm.with && arm.with.length > 0) {
|
|
1605
|
+
if (arm !== arms[0]) {
|
|
1606
|
+
hasWithBeyondFirst = true;
|
|
1607
|
+
}
|
|
1608
|
+
for (const cte of arm.with) {
|
|
1609
|
+
const cteName = getCteName(cte);
|
|
1610
|
+
if (!cteName)
|
|
1611
|
+
return false;
|
|
1612
|
+
if (seen.has(cteName)) {
|
|
1613
|
+
return false;
|
|
1614
|
+
}
|
|
1615
|
+
seen.add(cteName);
|
|
1616
|
+
mergedWith.push(cte);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
if (!hasWithBeyondFirst)
|
|
1621
|
+
return false;
|
|
1622
|
+
arms[0].with = mergedWith;
|
|
1623
|
+
if ("parentheses_symbol" in arms[0]) {
|
|
1624
|
+
arms[0].parentheses_symbol = false;
|
|
1625
|
+
}
|
|
1626
|
+
for (let i = 1;i < arms.length; i++) {
|
|
1627
|
+
arms[i].with = null;
|
|
1628
|
+
}
|
|
1629
|
+
return true;
|
|
1630
|
+
}
|
|
1631
|
+
function walkSelect3(select) {
|
|
1632
|
+
let transformed = false;
|
|
1633
|
+
if (select.with) {
|
|
1634
|
+
for (const cte of select.with) {
|
|
1635
|
+
const cteSelect = cte.stmt?.ast ?? cte.stmt;
|
|
1636
|
+
if (cteSelect && cteSelect.type === "select") {
|
|
1637
|
+
transformed = walkSelect3(cteSelect) || transformed;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
if (Array.isArray(select.from)) {
|
|
1642
|
+
for (const from of select.from) {
|
|
1643
|
+
if ("expr" in from && from.expr && "ast" in from.expr) {
|
|
1644
|
+
transformed = walkSelect3(from.expr.ast) || transformed;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
transformed = hoistWithInSelect(select) || transformed;
|
|
1649
|
+
if (select._next) {
|
|
1650
|
+
transformed = walkSelect3(select._next) || transformed;
|
|
1651
|
+
}
|
|
1652
|
+
return transformed;
|
|
1653
|
+
}
|
|
1654
|
+
function hoistUnionWith(ast) {
|
|
1655
|
+
const statements = Array.isArray(ast) ? ast : [ast];
|
|
1656
|
+
let transformed = false;
|
|
1657
|
+
for (const stmt of statements) {
|
|
1658
|
+
if (stmt.type === "select") {
|
|
1659
|
+
transformed = walkSelect3(stmt) || transformed;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
return transformed;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
// src/sql/ast-transformer.ts
|
|
1666
|
+
var { Parser } = nodeSqlParser;
|
|
1667
|
+
var parser = new Parser;
|
|
1668
|
+
var CACHE_SIZE = 500;
|
|
1669
|
+
var transformCache = new Map;
|
|
1670
|
+
function getCachedOrTransform(query, transform) {
|
|
1671
|
+
const cached = transformCache.get(query);
|
|
1672
|
+
if (cached) {
|
|
1673
|
+
transformCache.delete(query);
|
|
1674
|
+
transformCache.set(query, cached);
|
|
1675
|
+
return cached;
|
|
1676
|
+
}
|
|
1677
|
+
const result = transform();
|
|
1678
|
+
if (transformCache.size >= CACHE_SIZE) {
|
|
1679
|
+
const oldestKey = transformCache.keys().next().value;
|
|
1680
|
+
if (oldestKey) {
|
|
1681
|
+
transformCache.delete(oldestKey);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
transformCache.set(query, result);
|
|
1685
|
+
return result;
|
|
1686
|
+
}
|
|
1687
|
+
var DEBUG_ENV = "DRIZZLE_DUCKDB_DEBUG_AST";
|
|
1688
|
+
function hasJoin(query) {
|
|
1689
|
+
return /\bjoin\b/i.test(query);
|
|
1690
|
+
}
|
|
1691
|
+
function debugLog(message, payload) {
|
|
1692
|
+
if (process?.env?.[DEBUG_ENV]) {
|
|
1693
|
+
console.debug("[duckdb-ast]", message, payload ?? "");
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
function transformSQL(query) {
|
|
1697
|
+
const needsArrayTransform = query.includes("@>") || query.includes("<@") || query.includes("&&");
|
|
1698
|
+
const needsJoinTransform = hasJoin(query) || /\bupdate\b/i.test(query) || /\bdelete\b/i.test(query);
|
|
1699
|
+
const needsUnionTransform = /\bunion\b/i.test(query) || /\bintersect\b/i.test(query) || /\bexcept\b/i.test(query);
|
|
1700
|
+
const needsGenerateSeriesTransform = /\bgenerate_series\b/i.test(query);
|
|
1701
|
+
if (!needsArrayTransform && !needsJoinTransform && !needsUnionTransform && !needsGenerateSeriesTransform) {
|
|
1702
|
+
return { sql: query, transformed: false };
|
|
1703
|
+
}
|
|
1704
|
+
return getCachedOrTransform(query, () => {
|
|
1705
|
+
try {
|
|
1706
|
+
const ast = parser.astify(query, { database: "PostgreSQL" });
|
|
1707
|
+
let transformed = false;
|
|
1708
|
+
if (needsArrayTransform) {
|
|
1709
|
+
transformed = transformArrayOperators(ast) || transformed;
|
|
1710
|
+
}
|
|
1711
|
+
if (needsJoinTransform) {
|
|
1712
|
+
transformed = qualifyJoinColumns(ast) || transformed;
|
|
1713
|
+
}
|
|
1714
|
+
if (needsGenerateSeriesTransform) {
|
|
1715
|
+
transformed = rewriteGenerateSeriesAliases(ast) || transformed;
|
|
1716
|
+
}
|
|
1717
|
+
if (needsUnionTransform) {
|
|
1718
|
+
transformed = hoistUnionWith(ast) || transformed;
|
|
1719
|
+
}
|
|
1720
|
+
if (!transformed) {
|
|
1721
|
+
debugLog("AST parsed but no transformation applied", {
|
|
1722
|
+
join: needsJoinTransform
|
|
1723
|
+
});
|
|
1724
|
+
return { sql: query, transformed: false };
|
|
1725
|
+
}
|
|
1726
|
+
const transformedSql = parser.sqlify(ast, { database: "PostgreSQL" });
|
|
1727
|
+
return { sql: transformedSql, transformed: true };
|
|
1728
|
+
} catch (err) {
|
|
1729
|
+
debugLog("AST transform failed; returning original SQL", {
|
|
1730
|
+
error: err.message
|
|
1731
|
+
});
|
|
1732
|
+
return { sql: query, transformed: false };
|
|
1733
|
+
}
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// src/dialect.ts
|
|
1738
|
+
class DuckDBDialect extends PgDialect {
|
|
1739
|
+
static [entityKind2] = "DuckDBPgDialect";
|
|
1740
|
+
hasPgJsonColumn = false;
|
|
1741
|
+
savepointsSupported = 0 /* Unknown */;
|
|
1742
|
+
resetPgJsonFlag() {
|
|
1743
|
+
this.hasPgJsonColumn = false;
|
|
1744
|
+
}
|
|
1745
|
+
markPgJsonDetected() {
|
|
1746
|
+
this.hasPgJsonColumn = true;
|
|
1747
|
+
}
|
|
1748
|
+
assertNoPgJsonColumns() {
|
|
1749
|
+
if (this.hasPgJsonColumn) {
|
|
1750
|
+
throw new Error("Pg JSON/JSONB columns are not supported in DuckDB. Replace them with duckDbJson() to use DuckDB's native JSON type.");
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
areSavepointsUnsupported() {
|
|
1754
|
+
return this.savepointsSupported === 2 /* No */;
|
|
1755
|
+
}
|
|
1756
|
+
markSavepointsSupported() {
|
|
1757
|
+
this.savepointsSupported = 1 /* Yes */;
|
|
1758
|
+
}
|
|
1759
|
+
markSavepointsUnsupported() {
|
|
1760
|
+
this.savepointsSupported = 2 /* No */;
|
|
1761
|
+
}
|
|
1762
|
+
async migrate(migrations, session, config) {
|
|
1763
|
+
const migrationConfig = typeof config === "string" ? { migrationsFolder: config } : config;
|
|
1764
|
+
const migrationsSchema = migrationConfig.migrationsSchema ?? "drizzle";
|
|
1765
|
+
const migrationsTable = migrationConfig.migrationsTable ?? "__drizzle_migrations";
|
|
1766
|
+
const migrationsSequence = `${migrationsTable}_id_seq`;
|
|
1767
|
+
const legacySequence = "migrations_pk_seq";
|
|
1768
|
+
const escapeIdentifier = (value) => value.replace(/"/g, '""');
|
|
1769
|
+
const sequenceLiteral = `"${escapeIdentifier(migrationsSchema)}"."${escapeIdentifier(migrationsSequence)}"`;
|
|
1770
|
+
const migrationTableCreate = sql2`
|
|
1771
|
+
CREATE TABLE IF NOT EXISTS ${sql2.identifier(migrationsSchema)}.${sql2.identifier(migrationsTable)} (
|
|
1772
|
+
id integer PRIMARY KEY default nextval('${sql2.raw(sequenceLiteral)}'),
|
|
1773
|
+
hash text NOT NULL,
|
|
1774
|
+
created_at bigint
|
|
1775
|
+
)
|
|
1776
|
+
`;
|
|
1777
|
+
await session.execute(sql2`CREATE SCHEMA IF NOT EXISTS ${sql2.identifier(migrationsSchema)}`);
|
|
1778
|
+
await session.execute(sql2`CREATE SEQUENCE IF NOT EXISTS ${sql2.identifier(migrationsSchema)}.${sql2.identifier(migrationsSequence)}`);
|
|
1779
|
+
if (legacySequence !== migrationsSequence) {
|
|
1780
|
+
await session.execute(sql2`CREATE SEQUENCE IF NOT EXISTS ${sql2.identifier(migrationsSchema)}.${sql2.identifier(legacySequence)}`);
|
|
1781
|
+
}
|
|
1782
|
+
await session.execute(migrationTableCreate);
|
|
1783
|
+
const dbMigrations = await session.all(sql2`select id, hash, created_at from ${sql2.identifier(migrationsSchema)}.${sql2.identifier(migrationsTable)} order by created_at desc limit 1`);
|
|
1784
|
+
const lastDbMigration = dbMigrations[0];
|
|
1785
|
+
await session.transaction(async (tx) => {
|
|
1786
|
+
for await (const migration of migrations) {
|
|
1787
|
+
if (!lastDbMigration || Number(lastDbMigration.created_at) < migration.folderMillis) {
|
|
1788
|
+
for (const stmt of migration.sql) {
|
|
1789
|
+
await tx.execute(sql2.raw(stmt));
|
|
1790
|
+
}
|
|
1791
|
+
await tx.execute(sql2`insert into ${sql2.identifier(migrationsSchema)}.${sql2.identifier(migrationsTable)} ("hash", "created_at") values(${migration.hash}, ${migration.folderMillis})`);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
prepareTyping(encoder) {
|
|
1797
|
+
if (is2(encoder, PgJsonb) || is2(encoder, PgJson)) {
|
|
1798
|
+
this.markPgJsonDetected();
|
|
1799
|
+
throw new Error("Pg JSON/JSONB columns are not supported in DuckDB. Replace them with duckDbJson() to use DuckDB's native JSON type.");
|
|
1800
|
+
} else if (is2(encoder, PgNumeric)) {
|
|
1801
|
+
return "decimal";
|
|
1802
|
+
} else if (is2(encoder, PgTime2)) {
|
|
1803
|
+
return "time";
|
|
1804
|
+
} else if (is2(encoder, PgTimestamp2) || is2(encoder, PgTimestampString2)) {
|
|
1805
|
+
return "timestamp";
|
|
1806
|
+
} else if (is2(encoder, PgDate2) || is2(encoder, PgDateString2)) {
|
|
1807
|
+
return "date";
|
|
1808
|
+
} else if (is2(encoder, PgUUID)) {
|
|
1809
|
+
return "uuid";
|
|
1810
|
+
} else {
|
|
1811
|
+
return "none";
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
sqlToQuery(sqlObj, invokeSource) {
|
|
1815
|
+
const result = super.sqlToQuery(sqlObj, invokeSource);
|
|
1816
|
+
const transformed = transformSQL(result.sql);
|
|
1817
|
+
return {
|
|
1818
|
+
...result,
|
|
1819
|
+
sql: transformed.sql
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// src/select-builder.ts
|
|
1825
|
+
import { is as is4 } from "drizzle-orm/entity";
|
|
315
1826
|
import {
|
|
316
1827
|
PgSelectBase,
|
|
317
1828
|
PgSelectBuilder
|
|
318
1829
|
} from "drizzle-orm/pg-core/query-builders";
|
|
319
|
-
import { SQL as SQL3 } from "drizzle-orm/sql/sql";
|
|
320
1830
|
import { Subquery, ViewBaseConfig } from "drizzle-orm";
|
|
321
1831
|
import { PgViewBase } from "drizzle-orm/pg-core/view-base";
|
|
1832
|
+
import { SQL as SQL5 } from "drizzle-orm/sql/sql";
|
|
1833
|
+
|
|
1834
|
+
// src/sql/selection.ts
|
|
1835
|
+
import { Column as Column2, SQL as SQL4, getTableName as getTableName2, is as is3, sql as sql3 } from "drizzle-orm";
|
|
1836
|
+
function mapEntries(obj, prefix, fullJoin = false) {
|
|
1837
|
+
return Object.fromEntries(Object.entries(obj).filter(([key]) => key !== "enableRLS").map(([key, value]) => {
|
|
1838
|
+
const qualified = prefix ? `${prefix}.${key}` : key;
|
|
1839
|
+
if (fullJoin && is3(value, Column2)) {
|
|
1840
|
+
return [
|
|
1841
|
+
key,
|
|
1842
|
+
sql3`${value}`.mapWith(value).as(`${getTableName2(value.table)}.${value.name}`)
|
|
1843
|
+
];
|
|
1844
|
+
}
|
|
1845
|
+
if (fullJoin && is3(value, SQL4)) {
|
|
1846
|
+
const col = value.getSQL().queryChunks.find((chunk) => is3(chunk, Column2));
|
|
1847
|
+
const tableName = col?.table && getTableName2(col?.table);
|
|
1848
|
+
return [key, value.as(tableName ? `${tableName}.${key}` : key)];
|
|
1849
|
+
}
|
|
1850
|
+
if (is3(value, SQL4) || is3(value, Column2)) {
|
|
1851
|
+
const aliased = is3(value, SQL4) ? value : sql3`${value}`.mapWith(value);
|
|
1852
|
+
return [key, aliased.as(qualified)];
|
|
1853
|
+
}
|
|
1854
|
+
if (is3(value, SQL4.Aliased)) {
|
|
1855
|
+
return [key, value];
|
|
1856
|
+
}
|
|
1857
|
+
if (typeof value === "object" && value !== null) {
|
|
1858
|
+
return [
|
|
1859
|
+
key,
|
|
1860
|
+
mapEntries(value, qualified, fullJoin)
|
|
1861
|
+
];
|
|
1862
|
+
}
|
|
1863
|
+
return [key, value];
|
|
1864
|
+
}));
|
|
1865
|
+
}
|
|
1866
|
+
function aliasFields(fields, fullJoin = false) {
|
|
1867
|
+
return mapEntries(fields, undefined, fullJoin);
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
// src/select-builder.ts
|
|
1871
|
+
import { getTableColumns } from "drizzle-orm/utils";
|
|
1872
|
+
|
|
1873
|
+
class DuckDBSelectBuilder extends PgSelectBuilder {
|
|
1874
|
+
_fields;
|
|
1875
|
+
_session;
|
|
1876
|
+
_dialect;
|
|
1877
|
+
_withList = [];
|
|
1878
|
+
_distinct;
|
|
1879
|
+
constructor(config) {
|
|
1880
|
+
super(config);
|
|
1881
|
+
this._fields = config.fields;
|
|
1882
|
+
this._session = config.session;
|
|
1883
|
+
this._dialect = config.dialect;
|
|
1884
|
+
if (config.withList) {
|
|
1885
|
+
this._withList = config.withList;
|
|
1886
|
+
}
|
|
1887
|
+
this._distinct = config.distinct;
|
|
1888
|
+
}
|
|
1889
|
+
from(source) {
|
|
1890
|
+
const isPartialSelect = !!this._fields;
|
|
1891
|
+
const src = source;
|
|
1892
|
+
let fields;
|
|
1893
|
+
if (this._fields) {
|
|
1894
|
+
fields = this._fields;
|
|
1895
|
+
} else if (is4(src, Subquery)) {
|
|
1896
|
+
fields = Object.fromEntries(Object.keys(src._.selectedFields).map((key) => [
|
|
1897
|
+
key,
|
|
1898
|
+
src[key]
|
|
1899
|
+
]));
|
|
1900
|
+
} else if (is4(src, PgViewBase)) {
|
|
1901
|
+
fields = src[ViewBaseConfig]?.selectedFields;
|
|
1902
|
+
} else if (is4(src, SQL5)) {
|
|
1903
|
+
fields = {};
|
|
1904
|
+
} else {
|
|
1905
|
+
fields = aliasFields(getTableColumns(src), !isPartialSelect);
|
|
1906
|
+
}
|
|
1907
|
+
return new PgSelectBase({
|
|
1908
|
+
table: src,
|
|
1909
|
+
fields,
|
|
1910
|
+
isPartialSelect,
|
|
1911
|
+
session: this._session,
|
|
1912
|
+
dialect: this._dialect,
|
|
1913
|
+
withList: this._withList,
|
|
1914
|
+
distinct: this._distinct
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
// src/pool.ts
|
|
1920
|
+
import { DuckDBConnection } from "@duckdb/node-api";
|
|
1921
|
+
var POOL_PRESETS = {
|
|
1922
|
+
pulse: 4,
|
|
1923
|
+
standard: 6,
|
|
1924
|
+
jumbo: 8,
|
|
1925
|
+
mega: 12,
|
|
1926
|
+
giga: 16,
|
|
1927
|
+
local: 8,
|
|
1928
|
+
memory: 4
|
|
1929
|
+
};
|
|
1930
|
+
function resolvePoolSize(pool) {
|
|
1931
|
+
if (pool === false)
|
|
1932
|
+
return false;
|
|
1933
|
+
if (pool === undefined)
|
|
1934
|
+
return 4;
|
|
1935
|
+
if (typeof pool === "string")
|
|
1936
|
+
return POOL_PRESETS[pool];
|
|
1937
|
+
return pool.size ?? 4;
|
|
1938
|
+
}
|
|
1939
|
+
function createDuckDBConnectionPool(instance, options = {}) {
|
|
1940
|
+
const size = options.size && options.size > 0 ? options.size : 4;
|
|
1941
|
+
const acquireTimeout = options.acquireTimeout ?? 30000;
|
|
1942
|
+
const maxWaitingRequests = options.maxWaitingRequests ?? 100;
|
|
1943
|
+
const maxLifetimeMs = options.maxLifetimeMs;
|
|
1944
|
+
const idleTimeoutMs = options.idleTimeoutMs;
|
|
1945
|
+
const metadata = new WeakMap;
|
|
1946
|
+
const idle = [];
|
|
1947
|
+
const waiting = [];
|
|
1948
|
+
let total = 0;
|
|
1949
|
+
let closed = false;
|
|
1950
|
+
let pendingAcquires = 0;
|
|
1951
|
+
const shouldRecycle = (conn, now) => {
|
|
1952
|
+
if (maxLifetimeMs !== undefined && now - conn.createdAt >= maxLifetimeMs) {
|
|
1953
|
+
return true;
|
|
1954
|
+
}
|
|
1955
|
+
if (idleTimeoutMs !== undefined && now - conn.lastUsedAt >= idleTimeoutMs) {
|
|
1956
|
+
return true;
|
|
1957
|
+
}
|
|
1958
|
+
return false;
|
|
1959
|
+
};
|
|
1960
|
+
const acquire = async () => {
|
|
1961
|
+
if (closed) {
|
|
1962
|
+
throw new Error("DuckDB connection pool is closed");
|
|
1963
|
+
}
|
|
1964
|
+
while (idle.length > 0) {
|
|
1965
|
+
const pooled = idle.pop();
|
|
1966
|
+
const now = Date.now();
|
|
1967
|
+
if (shouldRecycle(pooled, now)) {
|
|
1968
|
+
await closeClientConnection(pooled.connection);
|
|
1969
|
+
total = Math.max(0, total - 1);
|
|
1970
|
+
metadata.delete(pooled.connection);
|
|
1971
|
+
continue;
|
|
1972
|
+
}
|
|
1973
|
+
pooled.lastUsedAt = now;
|
|
1974
|
+
metadata.set(pooled.connection, {
|
|
1975
|
+
createdAt: pooled.createdAt,
|
|
1976
|
+
lastUsedAt: pooled.lastUsedAt
|
|
1977
|
+
});
|
|
1978
|
+
return pooled.connection;
|
|
1979
|
+
}
|
|
1980
|
+
if (total < size) {
|
|
1981
|
+
pendingAcquires += 1;
|
|
1982
|
+
total += 1;
|
|
1983
|
+
try {
|
|
1984
|
+
const connection = await DuckDBConnection.create(instance);
|
|
1985
|
+
if (closed) {
|
|
1986
|
+
await closeClientConnection(connection);
|
|
1987
|
+
total -= 1;
|
|
1988
|
+
throw new Error("DuckDB connection pool is closed");
|
|
1989
|
+
}
|
|
1990
|
+
const now = Date.now();
|
|
1991
|
+
metadata.set(connection, { createdAt: now, lastUsedAt: now });
|
|
1992
|
+
return connection;
|
|
1993
|
+
} catch (error) {
|
|
1994
|
+
total -= 1;
|
|
1995
|
+
throw error;
|
|
1996
|
+
} finally {
|
|
1997
|
+
pendingAcquires -= 1;
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
if (waiting.length >= maxWaitingRequests) {
|
|
2001
|
+
throw new Error(`DuckDB connection pool queue is full (max ${maxWaitingRequests} waiting requests)`);
|
|
2002
|
+
}
|
|
2003
|
+
return await new Promise((resolve, reject) => {
|
|
2004
|
+
const timeoutId = setTimeout(() => {
|
|
2005
|
+
const idx = waiting.findIndex((w) => w.timeoutId === timeoutId);
|
|
2006
|
+
if (idx !== -1) {
|
|
2007
|
+
waiting.splice(idx, 1);
|
|
2008
|
+
}
|
|
2009
|
+
reject(new Error(`DuckDB connection pool acquire timeout after ${acquireTimeout}ms`));
|
|
2010
|
+
}, acquireTimeout);
|
|
2011
|
+
waiting.push({ resolve, reject, timeoutId });
|
|
2012
|
+
});
|
|
2013
|
+
};
|
|
2014
|
+
const release = async (connection) => {
|
|
2015
|
+
const waiter = waiting.shift();
|
|
2016
|
+
if (waiter) {
|
|
2017
|
+
clearTimeout(waiter.timeoutId);
|
|
2018
|
+
const now2 = Date.now();
|
|
2019
|
+
const meta = metadata.get(connection) ?? { createdAt: now2, lastUsedAt: now2 };
|
|
2020
|
+
const expired = maxLifetimeMs !== undefined && now2 - meta.createdAt >= maxLifetimeMs;
|
|
2021
|
+
if (closed) {
|
|
2022
|
+
await closeClientConnection(connection);
|
|
2023
|
+
total = Math.max(0, total - 1);
|
|
2024
|
+
metadata.delete(connection);
|
|
2025
|
+
waiter.reject(new Error("DuckDB connection pool is closed"));
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
if (expired) {
|
|
2029
|
+
await closeClientConnection(connection);
|
|
2030
|
+
total = Math.max(0, total - 1);
|
|
2031
|
+
metadata.delete(connection);
|
|
2032
|
+
try {
|
|
2033
|
+
const replacement = await acquire();
|
|
2034
|
+
waiter.resolve(replacement);
|
|
2035
|
+
} catch (error) {
|
|
2036
|
+
waiter.reject(error);
|
|
2037
|
+
}
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
meta.lastUsedAt = now2;
|
|
2041
|
+
metadata.set(connection, meta);
|
|
2042
|
+
waiter.resolve(connection);
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
if (closed) {
|
|
2046
|
+
await closeClientConnection(connection);
|
|
2047
|
+
metadata.delete(connection);
|
|
2048
|
+
total = Math.max(0, total - 1);
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
const now = Date.now();
|
|
2052
|
+
const existingMeta = metadata.get(connection) ?? { createdAt: now, lastUsedAt: now };
|
|
2053
|
+
existingMeta.lastUsedAt = now;
|
|
2054
|
+
metadata.set(connection, existingMeta);
|
|
2055
|
+
if (maxLifetimeMs !== undefined && now - existingMeta.createdAt >= maxLifetimeMs) {
|
|
2056
|
+
await closeClientConnection(connection);
|
|
2057
|
+
total -= 1;
|
|
2058
|
+
metadata.delete(connection);
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
idle.push({
|
|
2062
|
+
connection,
|
|
2063
|
+
createdAt: existingMeta.createdAt,
|
|
2064
|
+
lastUsedAt: existingMeta.lastUsedAt
|
|
2065
|
+
});
|
|
2066
|
+
};
|
|
2067
|
+
const close = async () => {
|
|
2068
|
+
closed = true;
|
|
2069
|
+
const waiters = waiting.splice(0, waiting.length);
|
|
2070
|
+
for (const waiter of waiters) {
|
|
2071
|
+
clearTimeout(waiter.timeoutId);
|
|
2072
|
+
waiter.reject(new Error("DuckDB connection pool is closed"));
|
|
2073
|
+
}
|
|
2074
|
+
const toClose = idle.splice(0, idle.length);
|
|
2075
|
+
await Promise.allSettled(toClose.map((item) => closeClientConnection(item.connection)));
|
|
2076
|
+
total = Math.max(0, total - toClose.length);
|
|
2077
|
+
toClose.forEach((item) => metadata.delete(item.connection));
|
|
2078
|
+
const maxWait = 5000;
|
|
2079
|
+
const start = Date.now();
|
|
2080
|
+
while (pendingAcquires > 0 && Date.now() - start < maxWait) {
|
|
2081
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
2082
|
+
}
|
|
2083
|
+
};
|
|
2084
|
+
return {
|
|
2085
|
+
acquire,
|
|
2086
|
+
release,
|
|
2087
|
+
close,
|
|
2088
|
+
size
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
// src/options.ts
|
|
2093
|
+
var DEFAULT_PREPARED_CACHE_SIZE = 32;
|
|
2094
|
+
function resolvePrepareCacheOption(option) {
|
|
2095
|
+
if (!option)
|
|
2096
|
+
return;
|
|
2097
|
+
if (option === true) {
|
|
2098
|
+
return { size: DEFAULT_PREPARED_CACHE_SIZE };
|
|
2099
|
+
}
|
|
2100
|
+
if (typeof option === "number") {
|
|
2101
|
+
const size2 = Math.max(1, Math.floor(option));
|
|
2102
|
+
return { size: size2 };
|
|
2103
|
+
}
|
|
2104
|
+
const size = option.size ?? DEFAULT_PREPARED_CACHE_SIZE;
|
|
2105
|
+
return { size: Math.max(1, Math.floor(size)) };
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// src/driver.ts
|
|
322
2109
|
class DuckDBDriver {
|
|
323
2110
|
client;
|
|
324
2111
|
dialect;
|
|
@@ -331,12 +2118,22 @@ class DuckDBDriver {
|
|
|
331
2118
|
}
|
|
332
2119
|
createSession(schema) {
|
|
333
2120
|
return new DuckDBSession(this.client, this.dialect, schema, {
|
|
334
|
-
logger: this.options.logger
|
|
2121
|
+
logger: this.options.logger,
|
|
2122
|
+
rejectStringArrayLiterals: this.options.rejectStringArrayLiterals,
|
|
2123
|
+
prepareCache: this.options.prepareCache
|
|
335
2124
|
});
|
|
336
2125
|
}
|
|
337
2126
|
}
|
|
338
|
-
function
|
|
2127
|
+
function isConfigObject(data) {
|
|
2128
|
+
if (typeof data !== "object" || data === null)
|
|
2129
|
+
return false;
|
|
2130
|
+
if (data.constructor?.name !== "Object")
|
|
2131
|
+
return false;
|
|
2132
|
+
return "connection" in data || "client" in data || "pool" in data || "schema" in data || "logger" in data;
|
|
2133
|
+
}
|
|
2134
|
+
function createFromClient(client, config = {}, instance) {
|
|
339
2135
|
const dialect = new DuckDBDialect;
|
|
2136
|
+
const prepareCache = resolvePrepareCacheOption(config.prepareCache);
|
|
340
2137
|
const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
|
|
341
2138
|
let schema;
|
|
342
2139
|
if (config.schema) {
|
|
@@ -347,118 +2144,222 @@ function drizzle(client, config = {}) {
|
|
|
347
2144
|
tableNamesMap: tablesConfig.tableNamesMap
|
|
348
2145
|
};
|
|
349
2146
|
}
|
|
350
|
-
const driver = new DuckDBDriver(client, dialect, {
|
|
2147
|
+
const driver = new DuckDBDriver(client, dialect, {
|
|
2148
|
+
logger,
|
|
2149
|
+
rejectStringArrayLiterals: config.rejectStringArrayLiterals,
|
|
2150
|
+
prepareCache
|
|
2151
|
+
});
|
|
351
2152
|
const session = driver.createSession(schema);
|
|
352
|
-
|
|
2153
|
+
const db = new DuckDBDatabase(dialect, session, schema, client, instance);
|
|
2154
|
+
return db;
|
|
2155
|
+
}
|
|
2156
|
+
async function createFromConnectionString(path, instanceOptions, config = {}) {
|
|
2157
|
+
const instance = await DuckDBInstance2.create(path, instanceOptions);
|
|
2158
|
+
const poolSize = resolvePoolSize(config.pool);
|
|
2159
|
+
if (poolSize === false) {
|
|
2160
|
+
const connection = await instance.connect();
|
|
2161
|
+
return createFromClient(connection, config, instance);
|
|
2162
|
+
}
|
|
2163
|
+
const pool = createDuckDBConnectionPool(instance, { size: poolSize });
|
|
2164
|
+
return createFromClient(pool, config, instance);
|
|
2165
|
+
}
|
|
2166
|
+
function drizzle(clientOrConfigOrPath, config) {
|
|
2167
|
+
if (typeof clientOrConfigOrPath === "string") {
|
|
2168
|
+
return createFromConnectionString(clientOrConfigOrPath, undefined, config);
|
|
2169
|
+
}
|
|
2170
|
+
if (isConfigObject(clientOrConfigOrPath)) {
|
|
2171
|
+
const configObj = clientOrConfigOrPath;
|
|
2172
|
+
if ("connection" in configObj) {
|
|
2173
|
+
const connConfig = configObj;
|
|
2174
|
+
const { connection, ...restConfig } = connConfig;
|
|
2175
|
+
if (typeof connection === "string") {
|
|
2176
|
+
return createFromConnectionString(connection, undefined, restConfig);
|
|
2177
|
+
}
|
|
2178
|
+
return createFromConnectionString(connection.path, connection.options, restConfig);
|
|
2179
|
+
}
|
|
2180
|
+
if ("client" in configObj) {
|
|
2181
|
+
const clientConfig = configObj;
|
|
2182
|
+
const { client: clientValue, ...restConfig } = clientConfig;
|
|
2183
|
+
return createFromClient(clientValue, restConfig);
|
|
2184
|
+
}
|
|
2185
|
+
throw new Error("Invalid drizzle config: either connection or client must be provided");
|
|
2186
|
+
}
|
|
2187
|
+
return createFromClient(clientOrConfigOrPath, config);
|
|
353
2188
|
}
|
|
354
2189
|
|
|
355
2190
|
class DuckDBDatabase extends PgDatabase {
|
|
356
2191
|
dialect;
|
|
357
2192
|
session;
|
|
358
2193
|
static [entityKind3] = "DuckDBDatabase";
|
|
359
|
-
|
|
2194
|
+
$client;
|
|
2195
|
+
$instance;
|
|
2196
|
+
constructor(dialect, session, schema, client, instance) {
|
|
360
2197
|
super(dialect, session, schema);
|
|
361
2198
|
this.dialect = dialect;
|
|
362
2199
|
this.session = session;
|
|
2200
|
+
this.$client = client;
|
|
2201
|
+
this.$instance = instance;
|
|
363
2202
|
}
|
|
364
|
-
|
|
365
|
-
if (
|
|
366
|
-
|
|
367
|
-
fields: fields ?? undefined,
|
|
368
|
-
session: this.session,
|
|
369
|
-
dialect: this.dialect
|
|
370
|
-
});
|
|
2203
|
+
async close() {
|
|
2204
|
+
if (isPool(this.$client) && this.$client.close) {
|
|
2205
|
+
await this.$client.close();
|
|
371
2206
|
}
|
|
372
|
-
|
|
2207
|
+
if (!isPool(this.$client)) {
|
|
2208
|
+
await closeClientConnection(this.$client);
|
|
2209
|
+
}
|
|
2210
|
+
if (this.$instance) {
|
|
2211
|
+
const maybeClosable = this.$instance;
|
|
2212
|
+
if (typeof maybeClosable.close === "function") {
|
|
2213
|
+
await maybeClosable.close();
|
|
2214
|
+
} else if (typeof maybeClosable.closeSync === "function") {
|
|
2215
|
+
maybeClosable.closeSync();
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
select(fields) {
|
|
2220
|
+
const selectedFields = fields ? aliasFields(fields) : undefined;
|
|
373
2221
|
return new DuckDBSelectBuilder({
|
|
374
|
-
fields:
|
|
2222
|
+
fields: selectedFields ?? undefined,
|
|
375
2223
|
session: this.session,
|
|
376
2224
|
dialect: this.dialect
|
|
377
2225
|
});
|
|
378
2226
|
}
|
|
2227
|
+
executeBatches(query, options = {}) {
|
|
2228
|
+
return this.session.executeBatches(query, options);
|
|
2229
|
+
}
|
|
2230
|
+
executeBatchesRaw(query, options = {}) {
|
|
2231
|
+
return this.session.executeBatchesRaw(query, options);
|
|
2232
|
+
}
|
|
2233
|
+
executeArrow(query) {
|
|
2234
|
+
return this.session.executeArrow(query);
|
|
2235
|
+
}
|
|
379
2236
|
async transaction(transaction) {
|
|
380
2237
|
return await this.session.transaction(transaction);
|
|
381
2238
|
}
|
|
382
2239
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
2240
|
+
// src/columns.ts
|
|
2241
|
+
import { sql as sql4 } from "drizzle-orm";
|
|
2242
|
+
import { customType } from "drizzle-orm/pg-core";
|
|
2243
|
+
function coerceArrayString(value) {
|
|
2244
|
+
const trimmed = value.trim();
|
|
2245
|
+
if (!trimmed) {
|
|
2246
|
+
return [];
|
|
2247
|
+
}
|
|
2248
|
+
if (trimmed.startsWith("[")) {
|
|
2249
|
+
try {
|
|
2250
|
+
return JSON.parse(trimmed);
|
|
2251
|
+
} catch {
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
2256
|
+
try {
|
|
2257
|
+
const json = trimmed.replace(/{/g, "[").replace(/}/g, "]");
|
|
2258
|
+
return JSON.parse(json);
|
|
2259
|
+
} catch {
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
function formatLiteral(value, typeHint) {
|
|
2266
|
+
if (value === null || value === undefined) {
|
|
2267
|
+
return "NULL";
|
|
2268
|
+
}
|
|
2269
|
+
const upperType = typeHint?.toUpperCase() ?? "";
|
|
2270
|
+
if (value instanceof Date) {
|
|
2271
|
+
return `'${value.toISOString()}'`;
|
|
2272
|
+
}
|
|
2273
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
2274
|
+
return value.toString();
|
|
2275
|
+
}
|
|
2276
|
+
if (typeof value === "boolean") {
|
|
2277
|
+
return value ? "TRUE" : "FALSE";
|
|
2278
|
+
}
|
|
2279
|
+
const str = typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
|
|
2280
|
+
const escaped = str.replace(/'/g, "''");
|
|
2281
|
+
if (upperType.includes("CHAR") || upperType.includes("TEXT") || upperType.includes("STRING") || upperType.includes("VARCHAR")) {
|
|
2282
|
+
return `'${escaped}'`;
|
|
2283
|
+
}
|
|
2284
|
+
return `'${escaped}'`;
|
|
2285
|
+
}
|
|
2286
|
+
function buildListLiteral(values, elementType) {
|
|
2287
|
+
if (values.length === 0) {
|
|
2288
|
+
return sql4`[]`;
|
|
2289
|
+
}
|
|
2290
|
+
const chunks = values.map((v) => typeof v === "object" && !Array.isArray(v) ? sql4`${v}` : sql4.raw(formatLiteral(v, elementType)));
|
|
2291
|
+
return sql4`list_value(${sql4.join(chunks, sql4.raw(", "))})`;
|
|
2292
|
+
}
|
|
2293
|
+
function buildStructLiteral(value, schema) {
|
|
2294
|
+
const parts = Object.entries(value).map(([key, val]) => {
|
|
2295
|
+
const typeHint = schema?.[key];
|
|
2296
|
+
if (Array.isArray(val)) {
|
|
2297
|
+
const inner = typeof typeHint === "string" && typeHint.endsWith("[]") ? typeHint.slice(0, -2) : undefined;
|
|
2298
|
+
return sql4`${sql4.identifier(key)} := ${buildListLiteral(val, inner)}`;
|
|
2299
|
+
}
|
|
2300
|
+
return sql4`${sql4.identifier(key)} := ${val}`;
|
|
2301
|
+
});
|
|
2302
|
+
return sql4`struct_pack(${sql4.join(parts, sql4.raw(", "))})`;
|
|
2303
|
+
}
|
|
2304
|
+
function buildMapLiteral(value, valueType) {
|
|
2305
|
+
const keys = Object.keys(value);
|
|
2306
|
+
const vals = Object.values(value);
|
|
2307
|
+
const keyList = buildListLiteral(keys, "TEXT");
|
|
2308
|
+
const valList = buildListLiteral(vals, valueType?.endsWith("[]") ? valueType.slice(0, -2) : valueType);
|
|
2309
|
+
return sql4`map(${keyList}, ${valList})`;
|
|
2310
|
+
}
|
|
2311
|
+
var duckDbList = (name, elementType) => customType({
|
|
2312
|
+
dataType() {
|
|
2313
|
+
return `${elementType}[]`;
|
|
2314
|
+
},
|
|
2315
|
+
toDriver(value) {
|
|
2316
|
+
return wrapList(value, elementType);
|
|
2317
|
+
},
|
|
2318
|
+
fromDriver(value) {
|
|
2319
|
+
if (Array.isArray(value)) {
|
|
2320
|
+
return value;
|
|
2321
|
+
}
|
|
2322
|
+
if (typeof value === "string") {
|
|
2323
|
+
const parsed = coerceArrayString(value);
|
|
2324
|
+
if (parsed !== undefined) {
|
|
2325
|
+
return parsed;
|
|
2326
|
+
}
|
|
397
2327
|
}
|
|
398
|
-
|
|
2328
|
+
return value;
|
|
399
2329
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
} else if (is3(src, PgViewBase)) {
|
|
412
|
-
fields = src[ViewBaseConfig]?.selectedFields;
|
|
413
|
-
} else if (is3(src, SQL3)) {
|
|
414
|
-
fields = {};
|
|
415
|
-
} else {
|
|
416
|
-
fields = aliasFields(getTableColumns(src), !isPartialSelect);
|
|
2330
|
+
})(name);
|
|
2331
|
+
var duckDbArray = (name, elementType, fixedLength) => customType({
|
|
2332
|
+
dataType() {
|
|
2333
|
+
return fixedLength ? `${elementType}[${fixedLength}]` : `${elementType}[]`;
|
|
2334
|
+
},
|
|
2335
|
+
toDriver(value) {
|
|
2336
|
+
return wrapArray(value, elementType, fixedLength);
|
|
2337
|
+
},
|
|
2338
|
+
fromDriver(value) {
|
|
2339
|
+
if (Array.isArray(value)) {
|
|
2340
|
+
return value;
|
|
417
2341
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
distinct: this._distinct
|
|
426
|
-
});
|
|
2342
|
+
if (typeof value === "string") {
|
|
2343
|
+
const parsed = coerceArrayString(value);
|
|
2344
|
+
if (parsed !== undefined) {
|
|
2345
|
+
return parsed;
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
return value;
|
|
427
2349
|
}
|
|
428
|
-
}
|
|
429
|
-
// src/columns.ts
|
|
430
|
-
import { sql as sql5 } from "drizzle-orm";
|
|
431
|
-
import { customType } from "drizzle-orm/pg-core";
|
|
2350
|
+
})(name);
|
|
432
2351
|
var duckDbMap = (name, valueType) => customType({
|
|
433
2352
|
dataType() {
|
|
434
|
-
console.log("dataType");
|
|
435
2353
|
return `MAP (STRING, ${valueType})`;
|
|
436
2354
|
},
|
|
437
2355
|
toDriver(value) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (["STRING", "TEXT", "VARCHAR"].includes(valueType)) {
|
|
441
|
-
return `'${value2}'`;
|
|
442
|
-
}
|
|
443
|
-
return JSON.stringify(value2);
|
|
444
|
-
};
|
|
445
|
-
const values = Object.entries(value).map(([key, value2]) => {
|
|
446
|
-
return sql5.raw(`'${key}': ${valueFormatter(value2)}`);
|
|
447
|
-
});
|
|
448
|
-
const sqlChunks = [];
|
|
449
|
-
for (const value2 of values) {
|
|
450
|
-
sqlChunks.push(value2);
|
|
2356
|
+
if (Object.keys(value).length === 0) {
|
|
2357
|
+
return buildMapLiteral(value, valueType);
|
|
451
2358
|
}
|
|
452
|
-
return
|
|
2359
|
+
return wrapMap(value, valueType);
|
|
453
2360
|
},
|
|
454
2361
|
fromDriver(value) {
|
|
455
|
-
|
|
456
|
-
const replacedValue = value.replaceAll(/(?:^{)?([^=]+?)=(.+)(?:}$)?/g, '"$1":"$2"');
|
|
457
|
-
const formattedValue = `{${replacedValue}}`;
|
|
458
|
-
const valueObj = JSON.parse(formattedValue);
|
|
459
|
-
return Object.fromEntries(Object.entries(valueObj).map(([key, value2]) => {
|
|
460
|
-
return [key, JSON.parse(value2)];
|
|
461
|
-
}));
|
|
2362
|
+
return value;
|
|
462
2363
|
}
|
|
463
2364
|
})(name);
|
|
464
2365
|
var duckDbStruct = (name, schema) => customType({
|
|
@@ -467,45 +2368,931 @@ var duckDbStruct = (name, schema) => customType({
|
|
|
467
2368
|
return `STRUCT (${fields.join(", ")})`;
|
|
468
2369
|
},
|
|
469
2370
|
toDriver(value) {
|
|
470
|
-
|
|
471
|
-
const values = Object.entries(value).map(([key, value2]) => {
|
|
472
|
-
return sql5.raw(`'${key}': ${valueFormatter(value2)}`);
|
|
473
|
-
});
|
|
474
|
-
const sqlChunks = [];
|
|
475
|
-
for (const value2 of values) {
|
|
476
|
-
sqlChunks.push(value2);
|
|
477
|
-
}
|
|
478
|
-
return sql5`(SELECT {${sql5.join(sqlChunks, sql5.raw(", "))}})`;
|
|
2371
|
+
return buildStructLiteral(value, schema);
|
|
479
2372
|
},
|
|
480
2373
|
fromDriver(value) {
|
|
2374
|
+
if (typeof value === "string") {
|
|
2375
|
+
try {
|
|
2376
|
+
return JSON.parse(value);
|
|
2377
|
+
} catch {
|
|
2378
|
+
return value;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
481
2381
|
return value;
|
|
482
2382
|
}
|
|
483
2383
|
})(name);
|
|
2384
|
+
var duckDbJson = (name) => customType({
|
|
2385
|
+
dataType() {
|
|
2386
|
+
return "JSON";
|
|
2387
|
+
},
|
|
2388
|
+
toDriver(value) {
|
|
2389
|
+
if (typeof value === "string") {
|
|
2390
|
+
return value;
|
|
2391
|
+
}
|
|
2392
|
+
if (value !== null && typeof value === "object" && "queryChunks" in value) {
|
|
2393
|
+
return value;
|
|
2394
|
+
}
|
|
2395
|
+
return wrapJson(value);
|
|
2396
|
+
},
|
|
2397
|
+
fromDriver(value) {
|
|
2398
|
+
if (typeof value !== "string") {
|
|
2399
|
+
return value;
|
|
2400
|
+
}
|
|
2401
|
+
const trimmed = value.trim();
|
|
2402
|
+
if (!trimmed) {
|
|
2403
|
+
return value;
|
|
2404
|
+
}
|
|
2405
|
+
try {
|
|
2406
|
+
return JSON.parse(trimmed);
|
|
2407
|
+
} catch {
|
|
2408
|
+
return value;
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
})(name);
|
|
484
2412
|
var duckDbBlob = customType({
|
|
485
2413
|
dataType() {
|
|
486
2414
|
return "BLOB";
|
|
487
2415
|
},
|
|
488
2416
|
toDriver(value) {
|
|
489
|
-
|
|
490
|
-
return sql5`from_hex(${hexString})`;
|
|
2417
|
+
return wrapBlob(value);
|
|
491
2418
|
}
|
|
492
2419
|
});
|
|
2420
|
+
var duckDbInet = (name) => customType({
|
|
2421
|
+
dataType() {
|
|
2422
|
+
return "INET";
|
|
2423
|
+
},
|
|
2424
|
+
toDriver(value) {
|
|
2425
|
+
return value;
|
|
2426
|
+
}
|
|
2427
|
+
})(name);
|
|
2428
|
+
var duckDbInterval = (name) => customType({
|
|
2429
|
+
dataType() {
|
|
2430
|
+
return "INTERVAL";
|
|
2431
|
+
},
|
|
2432
|
+
toDriver(value) {
|
|
2433
|
+
return value;
|
|
2434
|
+
}
|
|
2435
|
+
})(name);
|
|
2436
|
+
function shouldBindTimestamp(options) {
|
|
2437
|
+
const bindMode = options.bindMode ?? "auto";
|
|
2438
|
+
if (bindMode === "bind")
|
|
2439
|
+
return true;
|
|
2440
|
+
if (bindMode === "literal")
|
|
2441
|
+
return false;
|
|
2442
|
+
const isBun = typeof process !== "undefined" && typeof process.versions?.bun !== "undefined";
|
|
2443
|
+
if (isBun)
|
|
2444
|
+
return false;
|
|
2445
|
+
const forceLiteral = typeof process !== "undefined" ? process.env.DRIZZLE_DUCKDB_FORCE_LITERAL_TIMESTAMPS : undefined;
|
|
2446
|
+
if (forceLiteral && forceLiteral !== "0") {
|
|
2447
|
+
return false;
|
|
2448
|
+
}
|
|
2449
|
+
return true;
|
|
2450
|
+
}
|
|
2451
|
+
var duckDbTimestamp = (name, options = {}) => customType({
|
|
2452
|
+
dataType() {
|
|
2453
|
+
if (options.withTimezone) {
|
|
2454
|
+
return "TIMESTAMPTZ";
|
|
2455
|
+
}
|
|
2456
|
+
const precision = options.precision ? `(${options.precision})` : "";
|
|
2457
|
+
return `TIMESTAMP${precision}`;
|
|
2458
|
+
},
|
|
2459
|
+
toDriver(value) {
|
|
2460
|
+
if (shouldBindTimestamp(options)) {
|
|
2461
|
+
return wrapTimestamp(value, options.withTimezone ?? false, options.precision);
|
|
2462
|
+
}
|
|
2463
|
+
const iso = value instanceof Date ? value.toISOString() : value;
|
|
2464
|
+
const normalized = iso.replace("T", " ").replace("Z", "+00");
|
|
2465
|
+
const typeKeyword = options.withTimezone ? "TIMESTAMPTZ" : "TIMESTAMP";
|
|
2466
|
+
return sql4.raw(`${typeKeyword} '${normalized}'`);
|
|
2467
|
+
},
|
|
2468
|
+
fromDriver(value) {
|
|
2469
|
+
if (value && typeof value === "object" && "kind" in value && value.kind === "timestamp") {
|
|
2470
|
+
const wrapped = value;
|
|
2471
|
+
return wrapped.data instanceof Date ? wrapped.data : typeof wrapped.data === "number" || typeof wrapped.data === "bigint" ? new Date(Number(wrapped.data) / 1000) : wrapped.data;
|
|
2472
|
+
}
|
|
2473
|
+
if (options.mode === "string") {
|
|
2474
|
+
if (value instanceof Date) {
|
|
2475
|
+
return value.toISOString().replace("T", " ").replace("Z", "+00");
|
|
2476
|
+
}
|
|
2477
|
+
return typeof value === "string" ? value : value.toString();
|
|
2478
|
+
}
|
|
2479
|
+
if (value instanceof Date) {
|
|
2480
|
+
return value;
|
|
2481
|
+
}
|
|
2482
|
+
const stringValue = typeof value === "string" ? value : value.toString();
|
|
2483
|
+
const hasOffset = stringValue.endsWith("Z") || /[+-]\d{2}:?\d{2}$/.test(stringValue);
|
|
2484
|
+
const normalized = hasOffset ? stringValue.replace(" ", "T") : `${stringValue.replace(" ", "T")}Z`;
|
|
2485
|
+
return new Date(normalized);
|
|
2486
|
+
}
|
|
2487
|
+
})(name);
|
|
2488
|
+
var duckDbDate = (name) => customType({
|
|
2489
|
+
dataType() {
|
|
2490
|
+
return "DATE";
|
|
2491
|
+
},
|
|
2492
|
+
toDriver(value) {
|
|
2493
|
+
return value;
|
|
2494
|
+
},
|
|
2495
|
+
fromDriver(value) {
|
|
2496
|
+
const str = value instanceof Date ? value.toISOString().slice(0, 10) : value;
|
|
2497
|
+
return str;
|
|
2498
|
+
}
|
|
2499
|
+
})(name);
|
|
2500
|
+
var duckDbTime = (name) => customType({
|
|
2501
|
+
dataType() {
|
|
2502
|
+
return "TIME";
|
|
2503
|
+
},
|
|
2504
|
+
toDriver(value) {
|
|
2505
|
+
return value;
|
|
2506
|
+
},
|
|
2507
|
+
fromDriver(value) {
|
|
2508
|
+
if (typeof value === "bigint") {
|
|
2509
|
+
const totalMillis = Number(value) / 1000;
|
|
2510
|
+
const date = new Date(totalMillis);
|
|
2511
|
+
return date.toISOString().split("T")[1].replace("Z", "");
|
|
2512
|
+
}
|
|
2513
|
+
return value;
|
|
2514
|
+
}
|
|
2515
|
+
})(name);
|
|
2516
|
+
function toListValue(values) {
|
|
2517
|
+
return buildListLiteral(values);
|
|
2518
|
+
}
|
|
2519
|
+
function duckDbArrayContains(column, values) {
|
|
2520
|
+
const rhs = Array.isArray(values) ? toListValue(values) : values;
|
|
2521
|
+
return sql4`array_has_all(${column}, ${rhs})`;
|
|
2522
|
+
}
|
|
2523
|
+
function duckDbArrayContained(column, values) {
|
|
2524
|
+
const rhs = Array.isArray(values) ? toListValue(values) : values;
|
|
2525
|
+
return sql4`array_has_all(${rhs}, ${column})`;
|
|
2526
|
+
}
|
|
2527
|
+
function duckDbArrayOverlaps(column, values) {
|
|
2528
|
+
const rhs = Array.isArray(values) ? toListValue(values) : values;
|
|
2529
|
+
return sql4`array_has_any(${column}, ${rhs})`;
|
|
2530
|
+
}
|
|
493
2531
|
// src/migrator.ts
|
|
494
2532
|
import { readMigrationFiles } from "drizzle-orm/migrator";
|
|
495
2533
|
async function migrate(db, config) {
|
|
496
|
-
const
|
|
497
|
-
|
|
2534
|
+
const migrationConfig = typeof config === "string" ? { migrationsFolder: config } : config;
|
|
2535
|
+
const migrations = readMigrationFiles(migrationConfig);
|
|
2536
|
+
await db.dialect.migrate(migrations, db.session, migrationConfig);
|
|
2537
|
+
}
|
|
2538
|
+
// src/introspect.ts
|
|
2539
|
+
import { sql as sql5 } from "drizzle-orm";
|
|
2540
|
+
var SYSTEM_SCHEMAS = new Set(["information_schema", "pg_catalog"]);
|
|
2541
|
+
var DEFAULT_IMPORT_BASE = "@leonardovida-md/drizzle-neo-duckdb/helpers";
|
|
2542
|
+
async function introspect(db, opts = {}) {
|
|
2543
|
+
const database = await resolveDatabase(db, opts.database, opts.allDatabases);
|
|
2544
|
+
const schemas = await resolveSchemas(db, database, opts.schemas);
|
|
2545
|
+
const includeViews = opts.includeViews ?? false;
|
|
2546
|
+
const tables = await loadTables(db, database, schemas, includeViews);
|
|
2547
|
+
const columns = await loadColumns(db, database, schemas);
|
|
2548
|
+
const constraints = await loadConstraints(db, database, schemas);
|
|
2549
|
+
const indexes = await loadIndexes(db, database, schemas);
|
|
2550
|
+
const grouped = buildTables(tables, columns, constraints, indexes);
|
|
2551
|
+
const schemaTs = emitSchema(grouped, {
|
|
2552
|
+
useCustomTimeTypes: opts.useCustomTimeTypes ?? true,
|
|
2553
|
+
mapJsonAsDuckDbJson: opts.mapJsonAsDuckDbJson ?? true,
|
|
2554
|
+
importBasePath: opts.importBasePath ?? DEFAULT_IMPORT_BASE
|
|
2555
|
+
});
|
|
2556
|
+
return {
|
|
2557
|
+
files: {
|
|
2558
|
+
schemaTs,
|
|
2559
|
+
metaJson: grouped
|
|
2560
|
+
}
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
async function resolveDatabase(db, targetDatabase, allDatabases) {
|
|
2564
|
+
if (allDatabases) {
|
|
2565
|
+
return null;
|
|
2566
|
+
}
|
|
2567
|
+
if (targetDatabase) {
|
|
2568
|
+
return targetDatabase;
|
|
2569
|
+
}
|
|
2570
|
+
const rows = await db.execute(sql5`SELECT current_database() as current_database`);
|
|
2571
|
+
return rows[0]?.current_database ?? null;
|
|
2572
|
+
}
|
|
2573
|
+
async function resolveSchemas(db, database, targetSchemas) {
|
|
2574
|
+
if (targetSchemas?.length) {
|
|
2575
|
+
return targetSchemas;
|
|
2576
|
+
}
|
|
2577
|
+
const databaseFilter = database ? sql5`catalog_name = ${database}` : sql5`1 = 1`;
|
|
2578
|
+
const rows = await db.execute(sql5`SELECT schema_name FROM information_schema.schemata WHERE ${databaseFilter}`);
|
|
2579
|
+
return rows.map((row) => row.schema_name).filter((name) => !SYSTEM_SCHEMAS.has(name));
|
|
2580
|
+
}
|
|
2581
|
+
async function loadTables(db, database, schemas, includeViews) {
|
|
2582
|
+
const schemaFragments = schemas.map((schema) => sql5`${schema}`);
|
|
2583
|
+
const databaseFilter = database ? sql5`table_catalog = ${database}` : sql5`1 = 1`;
|
|
2584
|
+
return await db.execute(sql5`
|
|
2585
|
+
SELECT table_schema as schema_name, table_name, table_type
|
|
2586
|
+
FROM information_schema.tables
|
|
2587
|
+
WHERE ${databaseFilter}
|
|
2588
|
+
AND table_schema IN (${sql5.join(schemaFragments, sql5.raw(", "))})
|
|
2589
|
+
AND ${includeViews ? sql5`1 = 1` : sql5`table_type = 'BASE TABLE'`}
|
|
2590
|
+
ORDER BY table_schema, table_name
|
|
2591
|
+
`);
|
|
2592
|
+
}
|
|
2593
|
+
async function loadColumns(db, database, schemas) {
|
|
2594
|
+
const schemaFragments = schemas.map((schema) => sql5`${schema}`);
|
|
2595
|
+
const databaseFilter = database ? sql5`database_name = ${database}` : sql5`1 = 1`;
|
|
2596
|
+
return await db.execute(sql5`
|
|
2597
|
+
SELECT
|
|
2598
|
+
schema_name,
|
|
2599
|
+
table_name,
|
|
2600
|
+
column_name,
|
|
2601
|
+
column_index,
|
|
2602
|
+
column_default,
|
|
2603
|
+
is_nullable,
|
|
2604
|
+
data_type,
|
|
2605
|
+
character_maximum_length,
|
|
2606
|
+
numeric_precision,
|
|
2607
|
+
numeric_scale,
|
|
2608
|
+
internal
|
|
2609
|
+
FROM duckdb_columns()
|
|
2610
|
+
WHERE ${databaseFilter}
|
|
2611
|
+
AND schema_name IN (${sql5.join(schemaFragments, sql5.raw(", "))})
|
|
2612
|
+
ORDER BY schema_name, table_name, column_index
|
|
2613
|
+
`);
|
|
2614
|
+
}
|
|
2615
|
+
async function loadConstraints(db, database, schemas) {
|
|
2616
|
+
const schemaFragments = schemas.map((schema) => sql5`${schema}`);
|
|
2617
|
+
const databaseFilter = database ? sql5`database_name = ${database}` : sql5`1 = 1`;
|
|
2618
|
+
return await db.execute(sql5`
|
|
2619
|
+
SELECT
|
|
2620
|
+
schema_name,
|
|
2621
|
+
table_name,
|
|
2622
|
+
constraint_name,
|
|
2623
|
+
constraint_type,
|
|
2624
|
+
constraint_text,
|
|
2625
|
+
constraint_column_names,
|
|
2626
|
+
referenced_table,
|
|
2627
|
+
referenced_column_names
|
|
2628
|
+
FROM duckdb_constraints()
|
|
2629
|
+
WHERE ${databaseFilter}
|
|
2630
|
+
AND schema_name IN (${sql5.join(schemaFragments, sql5.raw(", "))})
|
|
2631
|
+
ORDER BY schema_name, table_name, constraint_index
|
|
2632
|
+
`);
|
|
2633
|
+
}
|
|
2634
|
+
async function loadIndexes(db, database, schemas) {
|
|
2635
|
+
const schemaFragments = schemas.map((schema) => sql5`${schema}`);
|
|
2636
|
+
const databaseFilter = database ? sql5`database_name = ${database}` : sql5`1 = 1`;
|
|
2637
|
+
return await db.execute(sql5`
|
|
2638
|
+
SELECT
|
|
2639
|
+
schema_name,
|
|
2640
|
+
table_name,
|
|
2641
|
+
index_name,
|
|
2642
|
+
is_unique,
|
|
2643
|
+
expressions
|
|
2644
|
+
FROM duckdb_indexes()
|
|
2645
|
+
WHERE ${databaseFilter}
|
|
2646
|
+
AND schema_name IN (${sql5.join(schemaFragments, sql5.raw(", "))})
|
|
2647
|
+
ORDER BY schema_name, table_name, index_name
|
|
2648
|
+
`);
|
|
2649
|
+
}
|
|
2650
|
+
function buildTables(tables, columns, constraints, indexes) {
|
|
2651
|
+
const byTable = {};
|
|
2652
|
+
for (const table of tables) {
|
|
2653
|
+
const key = tableKey(table.schema_name, table.table_name);
|
|
2654
|
+
byTable[key] = {
|
|
2655
|
+
schema: table.schema_name,
|
|
2656
|
+
name: table.table_name,
|
|
2657
|
+
kind: table.table_type === "VIEW" ? "view" : "table",
|
|
2658
|
+
columns: [],
|
|
2659
|
+
constraints: [],
|
|
2660
|
+
indexes: []
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
for (const column of columns) {
|
|
2664
|
+
if (column.internal) {
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
const key = tableKey(column.schema_name, column.table_name);
|
|
2668
|
+
const table = byTable[key];
|
|
2669
|
+
if (!table) {
|
|
2670
|
+
continue;
|
|
2671
|
+
}
|
|
2672
|
+
table.columns.push({
|
|
2673
|
+
name: column.column_name,
|
|
2674
|
+
dataType: column.data_type,
|
|
2675
|
+
columnDefault: column.column_default,
|
|
2676
|
+
nullable: column.is_nullable,
|
|
2677
|
+
characterLength: column.character_maximum_length,
|
|
2678
|
+
numericPrecision: column.numeric_precision,
|
|
2679
|
+
numericScale: column.numeric_scale
|
|
2680
|
+
});
|
|
2681
|
+
}
|
|
2682
|
+
for (const constraint of constraints) {
|
|
2683
|
+
const key = tableKey(constraint.schema_name, constraint.table_name);
|
|
2684
|
+
const table = byTable[key];
|
|
2685
|
+
if (!table) {
|
|
2686
|
+
continue;
|
|
2687
|
+
}
|
|
2688
|
+
if (!constraint.constraint_column_names?.length) {
|
|
2689
|
+
continue;
|
|
2690
|
+
}
|
|
2691
|
+
table.constraints.push({
|
|
2692
|
+
name: constraint.constraint_name,
|
|
2693
|
+
type: constraint.constraint_type,
|
|
2694
|
+
columns: constraint.constraint_column_names ?? [],
|
|
2695
|
+
referencedTable: constraint.referenced_table && constraint.referenced_column_names ? {
|
|
2696
|
+
schema: constraint.schema_name,
|
|
2697
|
+
name: constraint.referenced_table,
|
|
2698
|
+
columns: constraint.referenced_column_names
|
|
2699
|
+
} : undefined,
|
|
2700
|
+
rawExpression: constraint.constraint_text
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
for (const index of indexes) {
|
|
2704
|
+
const key = tableKey(index.schema_name, index.table_name);
|
|
2705
|
+
const table = byTable[key];
|
|
2706
|
+
if (!table) {
|
|
2707
|
+
continue;
|
|
2708
|
+
}
|
|
2709
|
+
table.indexes.push(index);
|
|
2710
|
+
}
|
|
2711
|
+
return Object.values(byTable);
|
|
2712
|
+
}
|
|
2713
|
+
function emitSchema(catalog, options) {
|
|
2714
|
+
const imports = {
|
|
2715
|
+
drizzle: new Set,
|
|
2716
|
+
pgCore: new Set,
|
|
2717
|
+
local: new Set
|
|
2718
|
+
};
|
|
2719
|
+
imports.pgCore.add("pgSchema");
|
|
2720
|
+
const sorted = [...catalog].sort((a, b) => a.schema === b.schema ? a.name.localeCompare(b.name) : a.schema.localeCompare(b.schema));
|
|
2721
|
+
const lines = [];
|
|
2722
|
+
for (const schema of uniqueSchemas(sorted)) {
|
|
2723
|
+
imports.pgCore.add("pgSchema");
|
|
2724
|
+
const schemaVar = toSchemaIdentifier(schema);
|
|
2725
|
+
lines.push(`export const ${schemaVar} = pgSchema(${JSON.stringify(schema)});`, "");
|
|
2726
|
+
const tables = sorted.filter((table) => table.schema === schema);
|
|
2727
|
+
for (const table of tables) {
|
|
2728
|
+
lines.push(...emitTable(schemaVar, table, imports, options));
|
|
2729
|
+
lines.push("");
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
const importsBlock = renderImports(imports, options.importBasePath);
|
|
2733
|
+
return [importsBlock, ...lines].join(`
|
|
2734
|
+
`).trim() + `
|
|
2735
|
+
`;
|
|
2736
|
+
}
|
|
2737
|
+
function emitTable(schemaVar, table, imports, options) {
|
|
2738
|
+
const tableVar = toIdentifier(table.name);
|
|
2739
|
+
const columnLines = [];
|
|
2740
|
+
for (const column of table.columns) {
|
|
2741
|
+
columnLines.push(` ${columnProperty(column.name)}: ${emitColumn(column, imports, options)},`);
|
|
2742
|
+
}
|
|
2743
|
+
const constraintBlock = emitConstraints(table, imports);
|
|
2744
|
+
const tableLines = [];
|
|
2745
|
+
tableLines.push(`export const ${tableVar} = ${schemaVar}.table(${JSON.stringify(table.name)}, {`);
|
|
2746
|
+
tableLines.push(...columnLines);
|
|
2747
|
+
tableLines.push(`}${constraintBlock ? "," : ""}${constraintBlock ? ` ${constraintBlock}` : ""});`);
|
|
2748
|
+
return tableLines;
|
|
2749
|
+
}
|
|
2750
|
+
function emitConstraints(table, imports) {
|
|
2751
|
+
const constraints = table.constraints.filter((constraint) => ["PRIMARY KEY", "FOREIGN KEY", "UNIQUE"].includes(constraint.type));
|
|
2752
|
+
if (!constraints.length) {
|
|
2753
|
+
return "";
|
|
2754
|
+
}
|
|
2755
|
+
const entries = [];
|
|
2756
|
+
for (const constraint of constraints) {
|
|
2757
|
+
const key = toIdentifier(constraint.name || `${table.name}_constraint`);
|
|
2758
|
+
if (constraint.type === "PRIMARY KEY") {
|
|
2759
|
+
imports.pgCore.add("primaryKey");
|
|
2760
|
+
entries.push(`${key}: primaryKey({ columns: [${constraint.columns.map((col) => `t.${toIdentifier(col)}`).join(", ")}], name: ${JSON.stringify(constraint.name)} })`);
|
|
2761
|
+
} else if (constraint.type === "UNIQUE" && constraint.columns.length > 1) {
|
|
2762
|
+
imports.pgCore.add("unique");
|
|
2763
|
+
entries.push(`${key}: unique(${JSON.stringify(constraint.name)}).on(${constraint.columns.map((col) => `t.${toIdentifier(col)}`).join(", ")})`);
|
|
2764
|
+
} else if (constraint.type === "FOREIGN KEY" && constraint.referencedTable) {
|
|
2765
|
+
imports.pgCore.add("foreignKey");
|
|
2766
|
+
const targetTable = toIdentifier(constraint.referencedTable.name);
|
|
2767
|
+
entries.push(`${key}: foreignKey({ columns: [${constraint.columns.map((col) => `t.${toIdentifier(col)}`).join(", ")}], foreignColumns: [${constraint.referencedTable.columns.map((col) => `${targetTable}.${toIdentifier(col)}`).join(", ")}], name: ${JSON.stringify(constraint.name)} })`);
|
|
2768
|
+
} else if (constraint.type === "UNIQUE" && constraint.columns.length === 1) {
|
|
2769
|
+
const columnName = constraint.columns[0];
|
|
2770
|
+
entries.push(`${key}: t.${toIdentifier(columnName)}.unique(${JSON.stringify(constraint.name)})`);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
if (!entries.length) {
|
|
2774
|
+
return "";
|
|
2775
|
+
}
|
|
2776
|
+
const lines = ["(t) => ({"];
|
|
2777
|
+
for (const entry of entries) {
|
|
2778
|
+
lines.push(` ${entry},`);
|
|
2779
|
+
}
|
|
2780
|
+
lines.push("})");
|
|
2781
|
+
return lines.join(`
|
|
2782
|
+
`);
|
|
2783
|
+
}
|
|
2784
|
+
function emitColumn(column, imports, options) {
|
|
2785
|
+
const mapping = mapDuckDbType(column, imports, options);
|
|
2786
|
+
let builder = mapping.builder;
|
|
2787
|
+
if (!column.nullable) {
|
|
2788
|
+
builder += ".notNull()";
|
|
2789
|
+
}
|
|
2790
|
+
const defaultFragment = buildDefault(column.columnDefault);
|
|
2791
|
+
if (defaultFragment) {
|
|
2792
|
+
imports.drizzle.add("sql");
|
|
2793
|
+
builder += defaultFragment;
|
|
2794
|
+
}
|
|
2795
|
+
return builder;
|
|
2796
|
+
}
|
|
2797
|
+
function buildDefault(defaultValue) {
|
|
2798
|
+
if (!defaultValue) {
|
|
2799
|
+
return "";
|
|
2800
|
+
}
|
|
2801
|
+
const trimmed = defaultValue.trim();
|
|
2802
|
+
if (!trimmed || trimmed.toUpperCase() === "NULL") {
|
|
2803
|
+
return "";
|
|
2804
|
+
}
|
|
2805
|
+
if (/^nextval\(/i.test(trimmed)) {
|
|
2806
|
+
return `.default(sql\`${trimmed}\`)`;
|
|
2807
|
+
}
|
|
2808
|
+
if (/^current_timestamp(?:\(\))?$/i.test(trimmed) || /^now\(\)$/i.test(trimmed)) {
|
|
2809
|
+
return `.defaultNow()`;
|
|
2810
|
+
}
|
|
2811
|
+
if (trimmed === "true" || trimmed === "false") {
|
|
2812
|
+
return `.default(${trimmed})`;
|
|
2813
|
+
}
|
|
2814
|
+
const numberValue = Number(trimmed);
|
|
2815
|
+
if (!Number.isNaN(numberValue)) {
|
|
2816
|
+
return `.default(${trimmed})`;
|
|
2817
|
+
}
|
|
2818
|
+
const stringLiteralMatch = /^'(.*)'$/.exec(trimmed);
|
|
2819
|
+
if (stringLiteralMatch) {
|
|
2820
|
+
const value = stringLiteralMatch[1]?.replace(/''/g, "'");
|
|
2821
|
+
return `.default(${JSON.stringify(value)})`;
|
|
2822
|
+
}
|
|
2823
|
+
return "";
|
|
2824
|
+
}
|
|
2825
|
+
function mapDuckDbType(column, imports, options) {
|
|
2826
|
+
const raw = column.dataType.trim();
|
|
2827
|
+
const upper = raw.toUpperCase();
|
|
2828
|
+
if (upper === "BOOLEAN" || upper === "BOOL") {
|
|
2829
|
+
imports.pgCore.add("boolean");
|
|
2830
|
+
return { builder: `boolean(${columnName(column.name)})` };
|
|
2831
|
+
}
|
|
2832
|
+
if (upper === "SMALLINT" || upper === "INT2" || upper === "INT16" || upper === "TINYINT") {
|
|
2833
|
+
imports.pgCore.add("integer");
|
|
2834
|
+
return { builder: `integer(${columnName(column.name)})` };
|
|
2835
|
+
}
|
|
2836
|
+
if (upper === "INTEGER" || upper === "INT" || upper === "INT4" || upper === "SIGNED") {
|
|
2837
|
+
imports.pgCore.add("integer");
|
|
2838
|
+
return { builder: `integer(${columnName(column.name)})` };
|
|
2839
|
+
}
|
|
2840
|
+
if (upper === "BIGINT" || upper === "INT8" || upper === "UBIGINT") {
|
|
2841
|
+
imports.pgCore.add("bigint");
|
|
2842
|
+
return {
|
|
2843
|
+
builder: `bigint(${columnName(column.name)}, { mode: 'number' })`
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
const decimalMatch = /^DECIMAL\((\d+),(\d+)\)/i.exec(upper);
|
|
2847
|
+
const numericMatch = /^NUMERIC\((\d+),(\d+)\)/i.exec(upper);
|
|
2848
|
+
if (decimalMatch || numericMatch) {
|
|
2849
|
+
imports.pgCore.add("numeric");
|
|
2850
|
+
const [, precision, scale] = decimalMatch ?? numericMatch;
|
|
2851
|
+
return {
|
|
2852
|
+
builder: `numeric(${columnName(column.name)}, { precision: ${precision}, scale: ${scale} })`
|
|
2853
|
+
};
|
|
2854
|
+
}
|
|
2855
|
+
if (upper.startsWith("DECIMAL") || upper.startsWith("NUMERIC")) {
|
|
2856
|
+
imports.pgCore.add("numeric");
|
|
2857
|
+
const precision = column.numericPrecision;
|
|
2858
|
+
const scale = column.numericScale;
|
|
2859
|
+
const options2 = [];
|
|
2860
|
+
if (precision !== null && precision !== undefined) {
|
|
2861
|
+
options2.push(`precision: ${precision}`);
|
|
2862
|
+
}
|
|
2863
|
+
if (scale !== null && scale !== undefined) {
|
|
2864
|
+
options2.push(`scale: ${scale}`);
|
|
2865
|
+
}
|
|
2866
|
+
const suffix = options2.length ? `, { ${options2.join(", ")} }` : "";
|
|
2867
|
+
return { builder: `numeric(${columnName(column.name)}${suffix})` };
|
|
2868
|
+
}
|
|
2869
|
+
if (upper === "REAL" || upper === "FLOAT4") {
|
|
2870
|
+
imports.pgCore.add("real");
|
|
2871
|
+
return { builder: `real(${columnName(column.name)})` };
|
|
2872
|
+
}
|
|
2873
|
+
if (upper === "DOUBLE" || upper === "DOUBLE PRECISION" || upper === "FLOAT") {
|
|
2874
|
+
imports.pgCore.add("doublePrecision");
|
|
2875
|
+
return { builder: `doublePrecision(${columnName(column.name)})` };
|
|
2876
|
+
}
|
|
2877
|
+
const arrayMatch = /^(.*)\[(\d+)\]$/.exec(upper);
|
|
2878
|
+
if (arrayMatch) {
|
|
2879
|
+
imports.local.add("duckDbArray");
|
|
2880
|
+
const [, base, length] = arrayMatch;
|
|
2881
|
+
return {
|
|
2882
|
+
builder: `duckDbArray(${columnName(column.name)}, ${JSON.stringify(base)}, ${Number(length)})`
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
const listMatch = /^(.*)\[\]$/.exec(upper);
|
|
2886
|
+
if (listMatch) {
|
|
2887
|
+
imports.local.add("duckDbList");
|
|
2888
|
+
const [, base] = listMatch;
|
|
2889
|
+
return {
|
|
2890
|
+
builder: `duckDbList(${columnName(column.name)}, ${JSON.stringify(base)})`
|
|
2891
|
+
};
|
|
2892
|
+
}
|
|
2893
|
+
if (upper.startsWith("CHAR(") || upper === "CHAR") {
|
|
2894
|
+
imports.pgCore.add("char");
|
|
2895
|
+
const length = column.characterLength;
|
|
2896
|
+
const lengthPart = typeof length === "number" ? `, { length: ${length} }` : "";
|
|
2897
|
+
return { builder: `char(${columnName(column.name)}${lengthPart})` };
|
|
2898
|
+
}
|
|
2899
|
+
if (upper.startsWith("VARCHAR")) {
|
|
2900
|
+
imports.pgCore.add("varchar");
|
|
2901
|
+
const length = column.characterLength;
|
|
2902
|
+
const lengthPart = typeof length === "number" ? `, { length: ${length} }` : "";
|
|
2903
|
+
return { builder: `varchar(${columnName(column.name)}${lengthPart})` };
|
|
2904
|
+
}
|
|
2905
|
+
if (upper === "TEXT" || upper === "STRING") {
|
|
2906
|
+
imports.pgCore.add("text");
|
|
2907
|
+
return { builder: `text(${columnName(column.name)})` };
|
|
2908
|
+
}
|
|
2909
|
+
if (upper === "UUID") {
|
|
2910
|
+
imports.pgCore.add("uuid");
|
|
2911
|
+
return { builder: `uuid(${columnName(column.name)})` };
|
|
2912
|
+
}
|
|
2913
|
+
if (upper === "JSON") {
|
|
2914
|
+
if (options.mapJsonAsDuckDbJson) {
|
|
2915
|
+
imports.local.add("duckDbJson");
|
|
2916
|
+
return { builder: `duckDbJson(${columnName(column.name)})` };
|
|
2917
|
+
}
|
|
2918
|
+
imports.pgCore.add("text");
|
|
2919
|
+
return { builder: `text(${columnName(column.name)}) /* JSON */` };
|
|
2920
|
+
}
|
|
2921
|
+
if (upper.startsWith("ENUM")) {
|
|
2922
|
+
imports.pgCore.add("text");
|
|
2923
|
+
const enumLiteral = raw.replace(/^ENUM\s*/i, "").trim();
|
|
2924
|
+
return {
|
|
2925
|
+
builder: `text(${columnName(column.name)}) /* ENUM ${enumLiteral} */`
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
if (upper.startsWith("UNION")) {
|
|
2929
|
+
imports.pgCore.add("text");
|
|
2930
|
+
const unionLiteral = raw.replace(/^UNION\s*/i, "").trim();
|
|
2931
|
+
return {
|
|
2932
|
+
builder: `text(${columnName(column.name)}) /* UNION ${unionLiteral} */`
|
|
2933
|
+
};
|
|
2934
|
+
}
|
|
2935
|
+
if (upper === "INET") {
|
|
2936
|
+
imports.local.add("duckDbInet");
|
|
2937
|
+
return { builder: `duckDbInet(${columnName(column.name)})` };
|
|
2938
|
+
}
|
|
2939
|
+
if (upper === "INTERVAL") {
|
|
2940
|
+
imports.local.add("duckDbInterval");
|
|
2941
|
+
return { builder: `duckDbInterval(${columnName(column.name)})` };
|
|
2942
|
+
}
|
|
2943
|
+
if (upper === "BLOB" || upper === "BYTEA" || upper === "VARBINARY") {
|
|
2944
|
+
imports.local.add("duckDbBlob");
|
|
2945
|
+
return { builder: `duckDbBlob(${columnName(column.name)})` };
|
|
2946
|
+
}
|
|
2947
|
+
if (upper.startsWith("STRUCT")) {
|
|
2948
|
+
imports.local.add("duckDbStruct");
|
|
2949
|
+
const inner = upper.replace(/^STRUCT\s*\(/i, "").replace(/\)$/, "");
|
|
2950
|
+
const fields = parseStructFields(inner);
|
|
2951
|
+
const entries = fields.map(({ name, type }) => `${JSON.stringify(name)}: ${JSON.stringify(type)}`);
|
|
2952
|
+
return {
|
|
2953
|
+
builder: `duckDbStruct(${columnName(column.name)}, { ${entries.join(", ")} })`
|
|
2954
|
+
};
|
|
2955
|
+
}
|
|
2956
|
+
if (upper.startsWith("MAP(")) {
|
|
2957
|
+
imports.local.add("duckDbMap");
|
|
2958
|
+
const valueType = parseMapValue(upper);
|
|
2959
|
+
return {
|
|
2960
|
+
builder: `duckDbMap(${columnName(column.name)}, ${JSON.stringify(valueType)})`
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
if (upper.startsWith("TIMESTAMP WITH TIME ZONE")) {
|
|
2964
|
+
if (options.useCustomTimeTypes) {
|
|
2965
|
+
imports.local.add("duckDbTimestamp");
|
|
2966
|
+
} else {
|
|
2967
|
+
imports.pgCore.add("timestamp");
|
|
2968
|
+
}
|
|
2969
|
+
const factory = options.useCustomTimeTypes ? `duckDbTimestamp(${columnName(column.name)}, { withTimezone: true })` : `timestamp(${columnName(column.name)}, { withTimezone: true })`;
|
|
2970
|
+
return { builder: factory };
|
|
2971
|
+
}
|
|
2972
|
+
if (upper.startsWith("TIMESTAMP")) {
|
|
2973
|
+
if (options.useCustomTimeTypes) {
|
|
2974
|
+
imports.local.add("duckDbTimestamp");
|
|
2975
|
+
return {
|
|
2976
|
+
builder: `duckDbTimestamp(${columnName(column.name)})`
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
imports.pgCore.add("timestamp");
|
|
2980
|
+
return { builder: `timestamp(${columnName(column.name)})` };
|
|
2981
|
+
}
|
|
2982
|
+
if (upper === "TIME") {
|
|
2983
|
+
if (options.useCustomTimeTypes) {
|
|
2984
|
+
imports.local.add("duckDbTime");
|
|
2985
|
+
return { builder: `duckDbTime(${columnName(column.name)})` };
|
|
2986
|
+
}
|
|
2987
|
+
imports.pgCore.add("time");
|
|
2988
|
+
return { builder: `time(${columnName(column.name)})` };
|
|
2989
|
+
}
|
|
2990
|
+
if (upper === "DATE") {
|
|
2991
|
+
if (options.useCustomTimeTypes) {
|
|
2992
|
+
imports.local.add("duckDbDate");
|
|
2993
|
+
return { builder: `duckDbDate(${columnName(column.name)})` };
|
|
2994
|
+
}
|
|
2995
|
+
imports.pgCore.add("date");
|
|
2996
|
+
return { builder: `date(${columnName(column.name)})` };
|
|
2997
|
+
}
|
|
2998
|
+
imports.pgCore.add("text");
|
|
2999
|
+
return {
|
|
3000
|
+
builder: `text(${columnName(column.name)}) /* unsupported DuckDB type: ${upper} */`
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
function parseStructFields(inner) {
|
|
3004
|
+
const result = [];
|
|
3005
|
+
for (const part of splitTopLevel(inner, ",")) {
|
|
3006
|
+
const trimmed = part.trim();
|
|
3007
|
+
if (!trimmed)
|
|
3008
|
+
continue;
|
|
3009
|
+
const match = /^"?([^"]+)"?\s+(.*)$/i.exec(trimmed);
|
|
3010
|
+
if (!match) {
|
|
3011
|
+
continue;
|
|
3012
|
+
}
|
|
3013
|
+
const [, name, type] = match;
|
|
3014
|
+
result.push({ name, type: type.trim() });
|
|
3015
|
+
}
|
|
3016
|
+
return result;
|
|
3017
|
+
}
|
|
3018
|
+
function parseMapValue(raw) {
|
|
3019
|
+
const inner = raw.replace(/^MAP\(/i, "").replace(/\)$/, "");
|
|
3020
|
+
const parts = splitTopLevel(inner, ",");
|
|
3021
|
+
if (parts.length < 2) {
|
|
3022
|
+
return "TEXT";
|
|
3023
|
+
}
|
|
3024
|
+
return parts[1]?.trim() ?? "TEXT";
|
|
3025
|
+
}
|
|
3026
|
+
function splitTopLevel(input, delimiter) {
|
|
3027
|
+
const parts = [];
|
|
3028
|
+
let depth = 0;
|
|
3029
|
+
let current = "";
|
|
3030
|
+
for (let i = 0;i < input.length; i += 1) {
|
|
3031
|
+
const char = input[i];
|
|
3032
|
+
if (char === "(")
|
|
3033
|
+
depth += 1;
|
|
3034
|
+
if (char === ")")
|
|
3035
|
+
depth = Math.max(0, depth - 1);
|
|
3036
|
+
if (char === delimiter && depth === 0) {
|
|
3037
|
+
parts.push(current);
|
|
3038
|
+
current = "";
|
|
3039
|
+
continue;
|
|
3040
|
+
}
|
|
3041
|
+
current += char;
|
|
3042
|
+
}
|
|
3043
|
+
if (current) {
|
|
3044
|
+
parts.push(current);
|
|
3045
|
+
}
|
|
3046
|
+
return parts;
|
|
3047
|
+
}
|
|
3048
|
+
function tableKey(schema, table) {
|
|
3049
|
+
return `${schema}.${table}`;
|
|
3050
|
+
}
|
|
3051
|
+
function toIdentifier(name) {
|
|
3052
|
+
const cleaned = name.replace(/[^A-Za-z0-9_]/g, "_");
|
|
3053
|
+
const parts = cleaned.split("_").filter(Boolean);
|
|
3054
|
+
const base = parts.map((part, index) => index === 0 ? part.toLowerCase() : capitalize(part.toLowerCase())).join("");
|
|
3055
|
+
const candidate = base || "item";
|
|
3056
|
+
return /^[A-Za-z_]/.test(candidate) ? candidate : `t${candidate}`;
|
|
3057
|
+
}
|
|
3058
|
+
function toSchemaIdentifier(schema) {
|
|
3059
|
+
const base = toIdentifier(schema);
|
|
3060
|
+
return base.endsWith("Schema") ? base : `${base}Schema`;
|
|
3061
|
+
}
|
|
3062
|
+
function columnProperty(column) {
|
|
3063
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(column)) {
|
|
3064
|
+
return toIdentifier(column);
|
|
3065
|
+
}
|
|
3066
|
+
return JSON.stringify(column);
|
|
3067
|
+
}
|
|
3068
|
+
function columnName(name) {
|
|
3069
|
+
return JSON.stringify(name);
|
|
3070
|
+
}
|
|
3071
|
+
function capitalize(value) {
|
|
3072
|
+
if (!value)
|
|
3073
|
+
return value;
|
|
3074
|
+
return value[0].toUpperCase() + value.slice(1);
|
|
3075
|
+
}
|
|
3076
|
+
function uniqueSchemas(tables) {
|
|
3077
|
+
const seen = new Set;
|
|
3078
|
+
const result = [];
|
|
3079
|
+
for (const table of tables) {
|
|
3080
|
+
if (!seen.has(table.schema)) {
|
|
3081
|
+
seen.add(table.schema);
|
|
3082
|
+
result.push(table.schema);
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
return result;
|
|
3086
|
+
}
|
|
3087
|
+
function renderImports(imports, importBasePath) {
|
|
3088
|
+
const lines = [];
|
|
3089
|
+
const drizzle2 = [...imports.drizzle];
|
|
3090
|
+
if (drizzle2.length) {
|
|
3091
|
+
lines.push(`import { ${drizzle2.sort().join(", ")} } from 'drizzle-orm';`);
|
|
3092
|
+
}
|
|
3093
|
+
const pgCore = [...imports.pgCore];
|
|
3094
|
+
if (pgCore.length) {
|
|
3095
|
+
lines.push(`import { ${pgCore.sort().join(", ")} } from 'drizzle-orm/pg-core';`);
|
|
3096
|
+
}
|
|
3097
|
+
const local = [...imports.local];
|
|
3098
|
+
if (local.length) {
|
|
3099
|
+
lines.push(`import { ${local.sort().join(", ")} } from '${importBasePath}';`);
|
|
3100
|
+
}
|
|
3101
|
+
lines.push("");
|
|
3102
|
+
return lines.join(`
|
|
3103
|
+
`);
|
|
3104
|
+
}
|
|
3105
|
+
// src/olap.ts
|
|
3106
|
+
import { is as is5 } from "drizzle-orm/entity";
|
|
3107
|
+
import { sql as sql6 } from "drizzle-orm";
|
|
3108
|
+
import { SQL as SQL6 } from "drizzle-orm/sql/sql";
|
|
3109
|
+
import { Column as Column3, getTableName as getTableName3 } from "drizzle-orm";
|
|
3110
|
+
var countN = (expr = sql6`*`) => sql6`count(${expr})`.mapWith(Number);
|
|
3111
|
+
var sumN = (expr) => sql6`sum(${expr})`.mapWith(Number);
|
|
3112
|
+
var avgN = (expr) => sql6`avg(${expr})`.mapWith(Number);
|
|
3113
|
+
var sumDistinctN = (expr) => sql6`sum(distinct ${expr})`.mapWith(Number);
|
|
3114
|
+
var percentileCont = (p, expr) => sql6`percentile_cont(${p}) within group (order by ${expr})`.mapWith(Number);
|
|
3115
|
+
var median = (expr) => percentileCont(0.5, expr);
|
|
3116
|
+
var anyValue = (expr) => sql6`any_value(${expr})`;
|
|
3117
|
+
function normalizeArray(value) {
|
|
3118
|
+
if (!value)
|
|
3119
|
+
return [];
|
|
3120
|
+
return Array.isArray(value) ? value : [value];
|
|
3121
|
+
}
|
|
3122
|
+
function overClause(options) {
|
|
3123
|
+
const partitions = normalizeArray(options?.partitionBy);
|
|
3124
|
+
const orders = normalizeArray(options?.orderBy);
|
|
3125
|
+
const chunks = [];
|
|
3126
|
+
if (partitions.length > 0) {
|
|
3127
|
+
chunks.push(sql6`partition by ${sql6.join(partitions, sql6`, `)}`);
|
|
3128
|
+
}
|
|
3129
|
+
if (orders.length > 0) {
|
|
3130
|
+
chunks.push(sql6`order by ${sql6.join(orders, sql6`, `)}`);
|
|
3131
|
+
}
|
|
3132
|
+
if (chunks.length === 0) {
|
|
3133
|
+
return sql6``;
|
|
3134
|
+
}
|
|
3135
|
+
return sql6`over (${sql6.join(chunks, sql6` `)})`;
|
|
3136
|
+
}
|
|
3137
|
+
var rowNumber = (options) => sql6`row_number() ${overClause(options)}`.mapWith(Number);
|
|
3138
|
+
var rank = (options) => sql6`rank() ${overClause(options)}`.mapWith(Number);
|
|
3139
|
+
var denseRank = (options) => sql6`dense_rank() ${overClause(options)}`.mapWith(Number);
|
|
3140
|
+
var lag = (expr, offset = 1, defaultValue, options) => defaultValue ? sql6`lag(${expr}, ${offset}, ${defaultValue}) ${overClause(options)}` : sql6`lag(${expr}, ${offset}) ${overClause(options)}`;
|
|
3141
|
+
var lead = (expr, offset = 1, defaultValue, options) => defaultValue ? sql6`lead(${expr}, ${offset}, ${defaultValue}) ${overClause(options)}` : sql6`lead(${expr}, ${offset}) ${overClause(options)}`;
|
|
3142
|
+
function keyAlias(key, fallback) {
|
|
3143
|
+
if (is5(key, SQL6.Aliased)) {
|
|
3144
|
+
return key.fieldAlias ?? fallback;
|
|
3145
|
+
}
|
|
3146
|
+
if (is5(key, Column3)) {
|
|
3147
|
+
return `${getTableName3(key.table)}.${key.name}`;
|
|
3148
|
+
}
|
|
3149
|
+
return fallback;
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
class OlapBuilder {
|
|
3153
|
+
db;
|
|
3154
|
+
source;
|
|
3155
|
+
keys = [];
|
|
3156
|
+
measureMap = {};
|
|
3157
|
+
nonAggregates = {};
|
|
3158
|
+
wrapNonAggWithAnyValue = false;
|
|
3159
|
+
orderByClauses = [];
|
|
3160
|
+
constructor(db) {
|
|
3161
|
+
this.db = db;
|
|
3162
|
+
}
|
|
3163
|
+
from(source) {
|
|
3164
|
+
this.source = source;
|
|
3165
|
+
return this;
|
|
3166
|
+
}
|
|
3167
|
+
groupBy(keys) {
|
|
3168
|
+
this.keys = keys;
|
|
3169
|
+
return this;
|
|
3170
|
+
}
|
|
3171
|
+
measures(measures) {
|
|
3172
|
+
this.measureMap = measures;
|
|
3173
|
+
return this;
|
|
3174
|
+
}
|
|
3175
|
+
selectNonAggregates(fields, options = {}) {
|
|
3176
|
+
this.nonAggregates = fields;
|
|
3177
|
+
this.wrapNonAggWithAnyValue = options.anyValue ?? false;
|
|
3178
|
+
return this;
|
|
3179
|
+
}
|
|
3180
|
+
orderBy(...clauses) {
|
|
3181
|
+
this.orderByClauses = clauses;
|
|
3182
|
+
return this;
|
|
3183
|
+
}
|
|
3184
|
+
build() {
|
|
3185
|
+
if (!this.source) {
|
|
3186
|
+
throw new Error("olap: .from() is required");
|
|
3187
|
+
}
|
|
3188
|
+
if (this.keys.length === 0) {
|
|
3189
|
+
throw new Error("olap: .groupBy() is required");
|
|
3190
|
+
}
|
|
3191
|
+
if (Object.keys(this.measureMap).length === 0) {
|
|
3192
|
+
throw new Error("olap: .measures() is required");
|
|
3193
|
+
}
|
|
3194
|
+
const selection = {};
|
|
3195
|
+
this.keys.forEach((key, idx) => {
|
|
3196
|
+
const alias = keyAlias(key, `key_${idx}`);
|
|
3197
|
+
selection[alias] = key;
|
|
3198
|
+
});
|
|
3199
|
+
Object.entries(this.nonAggregates).forEach(([alias, expr]) => {
|
|
3200
|
+
selection[alias] = this.wrapNonAggWithAnyValue ? anyValue(expr) : expr;
|
|
3201
|
+
});
|
|
3202
|
+
Object.assign(selection, this.measureMap);
|
|
3203
|
+
let query = this.db.select(selection).from(this.source).groupBy(...this.keys);
|
|
3204
|
+
if (this.orderByClauses.length > 0) {
|
|
3205
|
+
query = query.orderBy(...this.orderByClauses);
|
|
3206
|
+
}
|
|
3207
|
+
return query;
|
|
3208
|
+
}
|
|
3209
|
+
run() {
|
|
3210
|
+
return this.build();
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
var olap = (db) => new OlapBuilder(db);
|
|
3214
|
+
// src/operators.ts
|
|
3215
|
+
import { sql as sql7 } from "drizzle-orm";
|
|
3216
|
+
function arrayHasAll(column, values) {
|
|
3217
|
+
return sql7`array_has_all(${column}, ${values})`;
|
|
3218
|
+
}
|
|
3219
|
+
function arrayHasAny(column, values) {
|
|
3220
|
+
return sql7`array_has_any(${column}, ${values})`;
|
|
3221
|
+
}
|
|
3222
|
+
function arrayContainedBy(column, values) {
|
|
3223
|
+
return sql7`array_has_all(${values}, ${column})`;
|
|
498
3224
|
}
|
|
499
3225
|
export {
|
|
3226
|
+
wrapperToNodeApiValue,
|
|
3227
|
+
wrapTimestamp,
|
|
3228
|
+
wrapStruct,
|
|
3229
|
+
wrapMap,
|
|
3230
|
+
wrapList,
|
|
3231
|
+
wrapJson,
|
|
3232
|
+
wrapBlob,
|
|
3233
|
+
wrapArray,
|
|
3234
|
+
toIdentifier,
|
|
3235
|
+
sumN,
|
|
3236
|
+
sumDistinctN,
|
|
3237
|
+
splitTopLevel,
|
|
3238
|
+
rowNumber,
|
|
3239
|
+
resolvePrepareCacheOption,
|
|
3240
|
+
resolvePoolSize,
|
|
3241
|
+
rank,
|
|
3242
|
+
prepareParams,
|
|
3243
|
+
percentileCont,
|
|
3244
|
+
parseStructFields,
|
|
3245
|
+
parseMapValue,
|
|
3246
|
+
olap,
|
|
500
3247
|
migrate,
|
|
3248
|
+
median,
|
|
3249
|
+
lead,
|
|
3250
|
+
lag,
|
|
3251
|
+
isPool,
|
|
3252
|
+
isDuckDBWrapper,
|
|
3253
|
+
introspect,
|
|
3254
|
+
formatLiteral,
|
|
3255
|
+
executeOnClient,
|
|
3256
|
+
executeInBatchesRaw,
|
|
3257
|
+
executeInBatches,
|
|
3258
|
+
executeArrowOnClient,
|
|
3259
|
+
executeArraysOnClient,
|
|
3260
|
+
duckDbTimestamp,
|
|
3261
|
+
duckDbTime,
|
|
501
3262
|
duckDbStruct,
|
|
502
3263
|
duckDbMap,
|
|
3264
|
+
duckDbList,
|
|
3265
|
+
duckDbJson,
|
|
3266
|
+
duckDbInterval,
|
|
3267
|
+
duckDbInet,
|
|
3268
|
+
duckDbDate,
|
|
503
3269
|
duckDbBlob,
|
|
3270
|
+
duckDbArrayOverlaps,
|
|
3271
|
+
duckDbArrayContains,
|
|
3272
|
+
duckDbArrayContained,
|
|
3273
|
+
duckDbArray,
|
|
504
3274
|
drizzle,
|
|
3275
|
+
denseRank,
|
|
3276
|
+
createDuckDBConnectionPool,
|
|
3277
|
+
countN,
|
|
3278
|
+
coerceArrayString,
|
|
3279
|
+
closeClientConnection,
|
|
3280
|
+
buildStructLiteral,
|
|
3281
|
+
buildMapLiteral,
|
|
3282
|
+
buildListLiteral,
|
|
3283
|
+
buildDefault,
|
|
3284
|
+
avgN,
|
|
3285
|
+
arrayHasAny,
|
|
3286
|
+
arrayHasAll,
|
|
3287
|
+
arrayContainedBy,
|
|
3288
|
+
anyValue,
|
|
3289
|
+
POOL_PRESETS,
|
|
3290
|
+
OlapBuilder,
|
|
505
3291
|
DuckDBTransaction,
|
|
506
3292
|
DuckDBSession,
|
|
507
|
-
DuckDBSelectBuilder,
|
|
508
3293
|
DuckDBPreparedQuery,
|
|
509
3294
|
DuckDBDriver,
|
|
510
|
-
DuckDBDatabase
|
|
3295
|
+
DuckDBDatabase,
|
|
3296
|
+
DUCKDB_VALUE_MARKER,
|
|
3297
|
+
DEFAULT_IMPORT_BASE
|
|
511
3298
|
};
|