@leonardovida-md/drizzle-neo-duckdb 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -18
- package/dist/client.d.ts +12 -0
- package/dist/columns.d.ts +18 -10
- package/dist/driver.d.ts +4 -0
- package/dist/duckdb-introspect.mjs +171 -23
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +387 -37
- package/dist/olap.d.ts +46 -0
- package/dist/session.d.ts +5 -0
- package/dist/value-wrappers.d.ts +104 -0
- package/package.json +7 -3
- package/src/bin/duckdb-introspect.ts +12 -3
- package/src/client.ts +135 -18
- package/src/columns.ts +65 -36
- package/src/dialect.ts +2 -2
- package/src/driver.ts +20 -6
- package/src/index.ts +3 -0
- package/src/introspect.ts +15 -10
- package/src/migrator.ts +1 -3
- package/src/olap.ts +189 -0
- package/src/select-builder.ts +3 -7
- package/src/session.ts +87 -18
- package/src/sql/query-rewriters.ts +5 -8
- package/src/sql/result-mapper.ts +6 -6
- package/src/sql/selection.ts +2 -9
- package/src/value-wrappers.ts +324 -0
package/dist/index.mjs
CHANGED
|
@@ -303,8 +303,137 @@ import { TransactionRollbackError } from "drizzle-orm/errors";
|
|
|
303
303
|
|
|
304
304
|
// src/client.ts
|
|
305
305
|
import {
|
|
306
|
-
listValue
|
|
306
|
+
listValue as listValue2,
|
|
307
|
+
timestampValue as timestampValue2
|
|
307
308
|
} from "@duckdb/node-api";
|
|
309
|
+
|
|
310
|
+
// src/value-wrappers.ts
|
|
311
|
+
import {
|
|
312
|
+
listValue,
|
|
313
|
+
arrayValue,
|
|
314
|
+
structValue,
|
|
315
|
+
mapValue,
|
|
316
|
+
blobValue,
|
|
317
|
+
timestampValue,
|
|
318
|
+
timestampTZValue
|
|
319
|
+
} from "@duckdb/node-api";
|
|
320
|
+
var DUCKDB_VALUE_MARKER = Symbol.for("drizzle-duckdb:value");
|
|
321
|
+
function isDuckDBWrapper(value) {
|
|
322
|
+
return value !== null && typeof value === "object" && DUCKDB_VALUE_MARKER in value && value[DUCKDB_VALUE_MARKER] === true;
|
|
323
|
+
}
|
|
324
|
+
function wrapList(data, elementType) {
|
|
325
|
+
return {
|
|
326
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
327
|
+
kind: "list",
|
|
328
|
+
data,
|
|
329
|
+
elementType
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function wrapArray(data, elementType, fixedLength) {
|
|
333
|
+
return {
|
|
334
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
335
|
+
kind: "array",
|
|
336
|
+
data,
|
|
337
|
+
elementType,
|
|
338
|
+
fixedLength
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function wrapStruct(data, schema) {
|
|
342
|
+
return {
|
|
343
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
344
|
+
kind: "struct",
|
|
345
|
+
data,
|
|
346
|
+
schema
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function wrapMap(data, valueType) {
|
|
350
|
+
return {
|
|
351
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
352
|
+
kind: "map",
|
|
353
|
+
data,
|
|
354
|
+
valueType
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function wrapTimestamp(data, withTimezone, precision) {
|
|
358
|
+
return {
|
|
359
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
360
|
+
kind: "timestamp",
|
|
361
|
+
data,
|
|
362
|
+
withTimezone,
|
|
363
|
+
precision
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function wrapBlob(data) {
|
|
367
|
+
return {
|
|
368
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
369
|
+
kind: "blob",
|
|
370
|
+
data
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function wrapJson(data) {
|
|
374
|
+
return {
|
|
375
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
376
|
+
kind: "json",
|
|
377
|
+
data
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function dateToMicros(value) {
|
|
381
|
+
if (value instanceof Date) {
|
|
382
|
+
return BigInt(value.getTime()) * 1000n;
|
|
383
|
+
}
|
|
384
|
+
let normalized = value;
|
|
385
|
+
if (!value.includes("T") && value.includes(" ")) {
|
|
386
|
+
normalized = value.replace(" ", "T");
|
|
387
|
+
}
|
|
388
|
+
if (!normalized.endsWith("Z") && !/[+-]\d{2}:?\d{2}$/.test(normalized)) {
|
|
389
|
+
normalized += "Z";
|
|
390
|
+
}
|
|
391
|
+
const date = new Date(normalized);
|
|
392
|
+
if (isNaN(date.getTime())) {
|
|
393
|
+
throw new Error(`Invalid timestamp string: ${value}`);
|
|
394
|
+
}
|
|
395
|
+
return BigInt(date.getTime()) * 1000n;
|
|
396
|
+
}
|
|
397
|
+
function toUint8Array(data) {
|
|
398
|
+
return data instanceof Uint8Array && !(data instanceof Buffer) ? data : new Uint8Array(data);
|
|
399
|
+
}
|
|
400
|
+
function convertStructEntries(data, toValue) {
|
|
401
|
+
const entries = {};
|
|
402
|
+
for (const [key, val] of Object.entries(data)) {
|
|
403
|
+
entries[key] = toValue(val);
|
|
404
|
+
}
|
|
405
|
+
return entries;
|
|
406
|
+
}
|
|
407
|
+
function convertMapEntries(data, toValue) {
|
|
408
|
+
return Object.entries(data).map(([key, val]) => ({
|
|
409
|
+
key,
|
|
410
|
+
value: toValue(val)
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
function wrapperToNodeApiValue(wrapper, toValue) {
|
|
414
|
+
switch (wrapper.kind) {
|
|
415
|
+
case "list":
|
|
416
|
+
return listValue(wrapper.data.map(toValue));
|
|
417
|
+
case "array":
|
|
418
|
+
return arrayValue(wrapper.data.map(toValue));
|
|
419
|
+
case "struct":
|
|
420
|
+
return structValue(convertStructEntries(wrapper.data, toValue));
|
|
421
|
+
case "map":
|
|
422
|
+
return mapValue(convertMapEntries(wrapper.data, toValue));
|
|
423
|
+
case "timestamp":
|
|
424
|
+
return wrapper.withTimezone ? timestampTZValue(dateToMicros(wrapper.data)) : timestampValue(dateToMicros(wrapper.data));
|
|
425
|
+
case "blob":
|
|
426
|
+
return blobValue(toUint8Array(wrapper.data));
|
|
427
|
+
case "json":
|
|
428
|
+
return JSON.stringify(wrapper.data);
|
|
429
|
+
default: {
|
|
430
|
+
const _exhaustive = wrapper;
|
|
431
|
+
throw new Error(`Unknown wrapper kind: ${_exhaustive.kind}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/client.ts
|
|
308
437
|
function isPgArrayLiteral(value) {
|
|
309
438
|
return value.startsWith("{") && value.endsWith("}");
|
|
310
439
|
}
|
|
@@ -333,30 +462,91 @@ function prepareParams(params, options = {}) {
|
|
|
333
462
|
});
|
|
334
463
|
}
|
|
335
464
|
function toNodeApiValue(value) {
|
|
465
|
+
if (value == null)
|
|
466
|
+
return null;
|
|
467
|
+
const t = typeof value;
|
|
468
|
+
if (t === "string" || t === "number" || t === "bigint" || t === "boolean") {
|
|
469
|
+
return value;
|
|
470
|
+
}
|
|
471
|
+
if (t === "object" && DUCKDB_VALUE_MARKER in value) {
|
|
472
|
+
return wrapperToNodeApiValue(value, toNodeApiValue);
|
|
473
|
+
}
|
|
336
474
|
if (Array.isArray(value)) {
|
|
337
|
-
return
|
|
475
|
+
return listValue2(value.map((inner) => toNodeApiValue(inner)));
|
|
476
|
+
}
|
|
477
|
+
if (value instanceof Date) {
|
|
478
|
+
return timestampValue2(BigInt(value.getTime()) * 1000n);
|
|
338
479
|
}
|
|
339
480
|
return value;
|
|
340
481
|
}
|
|
341
|
-
|
|
342
|
-
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
343
|
-
const result = await client.run(query, values);
|
|
344
|
-
const rows = await result.getRowsJS();
|
|
345
|
-
const columns = result.columnNames();
|
|
482
|
+
function deduplicateColumns(columns) {
|
|
346
483
|
const seen = {};
|
|
347
|
-
|
|
484
|
+
return columns.map((col) => {
|
|
348
485
|
const count = seen[col] ?? 0;
|
|
349
486
|
seen[col] = count + 1;
|
|
350
487
|
return count === 0 ? col : `${col}_${count}`;
|
|
351
488
|
});
|
|
352
|
-
|
|
489
|
+
}
|
|
490
|
+
function mapRowsToObjects(columns, rows) {
|
|
491
|
+
return rows.map((vals) => {
|
|
353
492
|
const obj = {};
|
|
354
|
-
|
|
493
|
+
columns.forEach((col, idx) => {
|
|
355
494
|
obj[col] = vals[idx];
|
|
356
495
|
});
|
|
357
496
|
return obj;
|
|
358
497
|
});
|
|
359
498
|
}
|
|
499
|
+
async function closeClientConnection(connection) {
|
|
500
|
+
if ("close" in connection && typeof connection.close === "function") {
|
|
501
|
+
await connection.close();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if ("closeSync" in connection && typeof connection.closeSync === "function") {
|
|
505
|
+
connection.closeSync();
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if ("disconnectSync" in connection && typeof connection.disconnectSync === "function") {
|
|
509
|
+
connection.disconnectSync();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async function executeOnClient(client, query, params) {
|
|
513
|
+
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
514
|
+
const result = await client.run(query, values);
|
|
515
|
+
const rows = await result.getRowsJS();
|
|
516
|
+
const columns = result.deduplicatedColumnNames?.() ?? result.columnNames();
|
|
517
|
+
const uniqueColumns = deduplicateColumns(columns);
|
|
518
|
+
return rows ? mapRowsToObjects(uniqueColumns, rows) : [];
|
|
519
|
+
}
|
|
520
|
+
async function* executeInBatches(client, query, params, options = {}) {
|
|
521
|
+
const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
|
|
522
|
+
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
523
|
+
const result = await client.stream(query, values);
|
|
524
|
+
const columns = result.deduplicatedColumnNames?.() ?? result.columnNames();
|
|
525
|
+
const uniqueColumns = deduplicateColumns(columns);
|
|
526
|
+
let buffer = [];
|
|
527
|
+
for await (const chunk of result.yieldRowsJs()) {
|
|
528
|
+
const objects = mapRowsToObjects(uniqueColumns, chunk);
|
|
529
|
+
for (const row of objects) {
|
|
530
|
+
buffer.push(row);
|
|
531
|
+
if (buffer.length >= rowsPerChunk) {
|
|
532
|
+
yield buffer;
|
|
533
|
+
buffer = [];
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (buffer.length > 0) {
|
|
538
|
+
yield buffer;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function executeArrowOnClient(client, query, params) {
|
|
542
|
+
const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
|
|
543
|
+
const result = await client.run(query, values);
|
|
544
|
+
const maybeArrow = result.toArrow ?? result.getArrowTable;
|
|
545
|
+
if (typeof maybeArrow === "function") {
|
|
546
|
+
return await maybeArrow.call(result);
|
|
547
|
+
}
|
|
548
|
+
return result.getColumnsObjectJS();
|
|
549
|
+
}
|
|
360
550
|
|
|
361
551
|
// src/session.ts
|
|
362
552
|
class DuckDBPreparedQuery extends PgPreparedQuery {
|
|
@@ -397,11 +587,7 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
|
|
|
397
587
|
this.logger.logQuery(`[duckdb] original query before array rewrite: ${this.queryString}`, params);
|
|
398
588
|
}
|
|
399
589
|
this.logger.logQuery(rewrittenQuery, params);
|
|
400
|
-
const {
|
|
401
|
-
fields,
|
|
402
|
-
joinsNotNullableMap,
|
|
403
|
-
customResultMapper
|
|
404
|
-
} = this;
|
|
590
|
+
const { fields, joinsNotNullableMap, customResultMapper } = this;
|
|
405
591
|
const rows = await executeOnClient(this.client, rewrittenQuery, params);
|
|
406
592
|
if (rows.length === 0 || !fields) {
|
|
407
593
|
return rows;
|
|
@@ -461,6 +647,32 @@ class DuckDBSession extends PgSession {
|
|
|
461
647
|
this.logger.logQuery(`[duckdb] ${arrayLiteralWarning}
|
|
462
648
|
query: ${query}`, []);
|
|
463
649
|
};
|
|
650
|
+
executeBatches(query, options = {}) {
|
|
651
|
+
const builtQuery = this.dialect.sqlToQuery(query);
|
|
652
|
+
const params = prepareParams(builtQuery.params, {
|
|
653
|
+
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
654
|
+
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
655
|
+
});
|
|
656
|
+
const rewrittenQuery = this.rewriteArrays ? adaptArrayOperators(builtQuery.sql) : builtQuery.sql;
|
|
657
|
+
if (this.rewriteArrays && rewrittenQuery !== builtQuery.sql) {
|
|
658
|
+
this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
|
|
659
|
+
}
|
|
660
|
+
this.logger.logQuery(rewrittenQuery, params);
|
|
661
|
+
return executeInBatches(this.client, rewrittenQuery, params, options);
|
|
662
|
+
}
|
|
663
|
+
async executeArrow(query) {
|
|
664
|
+
const builtQuery = this.dialect.sqlToQuery(query);
|
|
665
|
+
const params = prepareParams(builtQuery.params, {
|
|
666
|
+
rejectStringArrayLiterals: this.rejectStringArrayLiterals,
|
|
667
|
+
warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
|
|
668
|
+
});
|
|
669
|
+
const rewrittenQuery = this.rewriteArrays ? adaptArrayOperators(builtQuery.sql) : builtQuery.sql;
|
|
670
|
+
if (this.rewriteArrays && rewrittenQuery !== builtQuery.sql) {
|
|
671
|
+
this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
|
|
672
|
+
}
|
|
673
|
+
this.logger.logQuery(rewrittenQuery, params);
|
|
674
|
+
return executeArrowOnClient(this.client, rewrittenQuery, params);
|
|
675
|
+
}
|
|
464
676
|
}
|
|
465
677
|
|
|
466
678
|
class DuckDBTransaction extends PgTransaction {
|
|
@@ -484,6 +696,12 @@ class DuckDBTransaction extends PgTransaction {
|
|
|
484
696
|
setTransaction(config) {
|
|
485
697
|
return this.session.execute(sql`set transaction ${this.getTransactionConfigSQL(config)}`);
|
|
486
698
|
}
|
|
699
|
+
executeBatches(query, options = {}) {
|
|
700
|
+
return this.session.executeBatches(query, options);
|
|
701
|
+
}
|
|
702
|
+
executeArrow(query) {
|
|
703
|
+
return this.session.executeArrow(query);
|
|
704
|
+
}
|
|
487
705
|
async transaction(transaction) {
|
|
488
706
|
const nestedTx = new DuckDBTransaction(this.dialect, this.session, this.schema, this.nestedIndex + 1);
|
|
489
707
|
return transaction(nestedTx);
|
|
@@ -582,13 +800,7 @@ import { PgViewBase } from "drizzle-orm/pg-core/view-base";
|
|
|
582
800
|
import { SQL as SQL4 } from "drizzle-orm/sql/sql";
|
|
583
801
|
|
|
584
802
|
// src/sql/selection.ts
|
|
585
|
-
import {
|
|
586
|
-
Column as Column2,
|
|
587
|
-
SQL as SQL3,
|
|
588
|
-
getTableName as getTableName2,
|
|
589
|
-
is as is3,
|
|
590
|
-
sql as sql3
|
|
591
|
-
} from "drizzle-orm";
|
|
803
|
+
import { Column as Column2, SQL as SQL3, getTableName as getTableName2, is as is3, sql as sql3 } from "drizzle-orm";
|
|
592
804
|
function mapEntries(obj, prefix, fullJoin = false) {
|
|
593
805
|
return Object.fromEntries(Object.entries(obj).filter(([key]) => key !== "enableRLS").map(([key, value]) => {
|
|
594
806
|
const qualified = prefix ? `${prefix}.${key}` : key;
|
|
@@ -729,6 +941,12 @@ class DuckDBDatabase extends PgDatabase {
|
|
|
729
941
|
dialect: this.dialect
|
|
730
942
|
});
|
|
731
943
|
}
|
|
944
|
+
executeBatches(query, options = {}) {
|
|
945
|
+
return this.session.executeBatches(query, options);
|
|
946
|
+
}
|
|
947
|
+
executeArrow(query) {
|
|
948
|
+
return this.session.executeArrow(query);
|
|
949
|
+
}
|
|
732
950
|
async transaction(transaction) {
|
|
733
951
|
return await this.session.transaction(transaction);
|
|
734
952
|
}
|
|
@@ -797,19 +1015,12 @@ function buildStructLiteral(value, schema) {
|
|
|
797
1015
|
});
|
|
798
1016
|
return sql4`struct_pack(${sql4.join(parts, sql4.raw(", "))})`;
|
|
799
1017
|
}
|
|
800
|
-
function buildMapLiteral(value, valueType) {
|
|
801
|
-
const keys = Object.keys(value);
|
|
802
|
-
const vals = Object.values(value);
|
|
803
|
-
const keyList = buildListLiteral(keys, "TEXT");
|
|
804
|
-
const valList = buildListLiteral(vals, valueType?.endsWith("[]") ? valueType.slice(0, -2) : valueType);
|
|
805
|
-
return sql4`map(${keyList}, ${valList})`;
|
|
806
|
-
}
|
|
807
1018
|
var duckDbList = (name, elementType) => customType({
|
|
808
1019
|
dataType() {
|
|
809
1020
|
return `${elementType}[]`;
|
|
810
1021
|
},
|
|
811
1022
|
toDriver(value) {
|
|
812
|
-
return
|
|
1023
|
+
return wrapList(value, elementType);
|
|
813
1024
|
},
|
|
814
1025
|
fromDriver(value) {
|
|
815
1026
|
if (Array.isArray(value)) {
|
|
@@ -829,7 +1040,7 @@ var duckDbArray = (name, elementType, fixedLength) => customType({
|
|
|
829
1040
|
return fixedLength ? `${elementType}[${fixedLength}]` : `${elementType}[]`;
|
|
830
1041
|
},
|
|
831
1042
|
toDriver(value) {
|
|
832
|
-
return
|
|
1043
|
+
return wrapArray(value, elementType, fixedLength);
|
|
833
1044
|
},
|
|
834
1045
|
fromDriver(value) {
|
|
835
1046
|
if (Array.isArray(value)) {
|
|
@@ -849,7 +1060,7 @@ var duckDbMap = (name, valueType) => customType({
|
|
|
849
1060
|
return `MAP (STRING, ${valueType})`;
|
|
850
1061
|
},
|
|
851
1062
|
toDriver(value) {
|
|
852
|
-
return
|
|
1063
|
+
return wrapMap(value, valueType);
|
|
853
1064
|
},
|
|
854
1065
|
fromDriver(value) {
|
|
855
1066
|
return value;
|
|
@@ -885,7 +1096,7 @@ var duckDbJson = (name) => customType({
|
|
|
885
1096
|
if (value !== null && typeof value === "object" && "queryChunks" in value) {
|
|
886
1097
|
return value;
|
|
887
1098
|
}
|
|
888
|
-
return
|
|
1099
|
+
return wrapJson(value);
|
|
889
1100
|
},
|
|
890
1101
|
fromDriver(value) {
|
|
891
1102
|
if (typeof value !== "string") {
|
|
@@ -907,8 +1118,7 @@ var duckDbBlob = customType({
|
|
|
907
1118
|
return "BLOB";
|
|
908
1119
|
},
|
|
909
1120
|
toDriver(value) {
|
|
910
|
-
|
|
911
|
-
return sql4`from_hex(${hexString})`;
|
|
1121
|
+
return wrapBlob(value);
|
|
912
1122
|
}
|
|
913
1123
|
});
|
|
914
1124
|
var duckDbInet = (name) => customType({
|
|
@@ -1311,7 +1521,9 @@ function mapDuckDbType(column, imports, options) {
|
|
|
1311
1521
|
}
|
|
1312
1522
|
if (upper === "BIGINT" || upper === "INT8" || upper === "UBIGINT") {
|
|
1313
1523
|
imports.pgCore.add("bigint");
|
|
1314
|
-
return {
|
|
1524
|
+
return {
|
|
1525
|
+
builder: `bigint(${columnName(column.name)}, { mode: 'number' })`
|
|
1526
|
+
};
|
|
1315
1527
|
}
|
|
1316
1528
|
const decimalMatch = /^DECIMAL\((\d+),(\d+)\)/i.exec(upper);
|
|
1317
1529
|
const numericMatch = /^NUMERIC\((\d+),(\d+)\)/i.exec(upper);
|
|
@@ -1558,9 +1770,140 @@ function renderImports(imports, importBasePath) {
|
|
|
1558
1770
|
return lines.join(`
|
|
1559
1771
|
`);
|
|
1560
1772
|
}
|
|
1773
|
+
// src/olap.ts
|
|
1774
|
+
import { is as is5 } from "drizzle-orm/entity";
|
|
1775
|
+
import { sql as sql6 } from "drizzle-orm";
|
|
1776
|
+
import { SQL as SQL5 } from "drizzle-orm/sql/sql";
|
|
1777
|
+
import { Column as Column3, getTableName as getTableName3 } from "drizzle-orm";
|
|
1778
|
+
var countN = (expr = sql6`*`) => sql6`count(${expr})`.mapWith(Number);
|
|
1779
|
+
var sumN = (expr) => sql6`sum(${expr})`.mapWith(Number);
|
|
1780
|
+
var avgN = (expr) => sql6`avg(${expr})`.mapWith(Number);
|
|
1781
|
+
var sumDistinctN = (expr) => sql6`sum(distinct ${expr})`.mapWith(Number);
|
|
1782
|
+
var percentileCont = (p, expr) => sql6`percentile_cont(${p}) within group (order by ${expr})`.mapWith(Number);
|
|
1783
|
+
var median = (expr) => percentileCont(0.5, expr);
|
|
1784
|
+
var anyValue = (expr) => sql6`any_value(${expr})`;
|
|
1785
|
+
function normalizeArray(value) {
|
|
1786
|
+
if (!value)
|
|
1787
|
+
return [];
|
|
1788
|
+
return Array.isArray(value) ? value : [value];
|
|
1789
|
+
}
|
|
1790
|
+
function overClause(options) {
|
|
1791
|
+
const partitions = normalizeArray(options?.partitionBy);
|
|
1792
|
+
const orders = normalizeArray(options?.orderBy);
|
|
1793
|
+
const chunks = [];
|
|
1794
|
+
if (partitions.length > 0) {
|
|
1795
|
+
chunks.push(sql6`partition by ${sql6.join(partitions, sql6`, `)}`);
|
|
1796
|
+
}
|
|
1797
|
+
if (orders.length > 0) {
|
|
1798
|
+
chunks.push(sql6`order by ${sql6.join(orders, sql6`, `)}`);
|
|
1799
|
+
}
|
|
1800
|
+
if (chunks.length === 0) {
|
|
1801
|
+
return sql6``;
|
|
1802
|
+
}
|
|
1803
|
+
return sql6`over (${sql6.join(chunks, sql6` `)})`;
|
|
1804
|
+
}
|
|
1805
|
+
var rowNumber = (options) => sql6`row_number() ${overClause(options)}`.mapWith(Number);
|
|
1806
|
+
var rank = (options) => sql6`rank() ${overClause(options)}`.mapWith(Number);
|
|
1807
|
+
var denseRank = (options) => sql6`dense_rank() ${overClause(options)}`.mapWith(Number);
|
|
1808
|
+
var lag = (expr, offset = 1, defaultValue, options) => defaultValue ? sql6`lag(${expr}, ${offset}, ${defaultValue}) ${overClause(options)}` : sql6`lag(${expr}, ${offset}) ${overClause(options)}`;
|
|
1809
|
+
var lead = (expr, offset = 1, defaultValue, options) => defaultValue ? sql6`lead(${expr}, ${offset}, ${defaultValue}) ${overClause(options)}` : sql6`lead(${expr}, ${offset}) ${overClause(options)}`;
|
|
1810
|
+
function keyAlias(key, fallback) {
|
|
1811
|
+
if (is5(key, SQL5.Aliased)) {
|
|
1812
|
+
return key.fieldAlias ?? fallback;
|
|
1813
|
+
}
|
|
1814
|
+
if (is5(key, Column3)) {
|
|
1815
|
+
return `${getTableName3(key.table)}.${key.name}`;
|
|
1816
|
+
}
|
|
1817
|
+
return fallback;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
class OlapBuilder {
|
|
1821
|
+
db;
|
|
1822
|
+
source;
|
|
1823
|
+
keys = [];
|
|
1824
|
+
measureMap = {};
|
|
1825
|
+
nonAggregates = {};
|
|
1826
|
+
wrapNonAggWithAnyValue = false;
|
|
1827
|
+
orderByClauses = [];
|
|
1828
|
+
constructor(db) {
|
|
1829
|
+
this.db = db;
|
|
1830
|
+
}
|
|
1831
|
+
from(source) {
|
|
1832
|
+
this.source = source;
|
|
1833
|
+
return this;
|
|
1834
|
+
}
|
|
1835
|
+
groupBy(keys) {
|
|
1836
|
+
this.keys = keys;
|
|
1837
|
+
return this;
|
|
1838
|
+
}
|
|
1839
|
+
measures(measures) {
|
|
1840
|
+
this.measureMap = measures;
|
|
1841
|
+
return this;
|
|
1842
|
+
}
|
|
1843
|
+
selectNonAggregates(fields, options = {}) {
|
|
1844
|
+
this.nonAggregates = fields;
|
|
1845
|
+
this.wrapNonAggWithAnyValue = options.anyValue ?? false;
|
|
1846
|
+
return this;
|
|
1847
|
+
}
|
|
1848
|
+
orderBy(...clauses) {
|
|
1849
|
+
this.orderByClauses = clauses;
|
|
1850
|
+
return this;
|
|
1851
|
+
}
|
|
1852
|
+
build() {
|
|
1853
|
+
if (!this.source) {
|
|
1854
|
+
throw new Error("olap: .from() is required");
|
|
1855
|
+
}
|
|
1856
|
+
if (this.keys.length === 0) {
|
|
1857
|
+
throw new Error("olap: .groupBy() is required");
|
|
1858
|
+
}
|
|
1859
|
+
if (Object.keys(this.measureMap).length === 0) {
|
|
1860
|
+
throw new Error("olap: .measures() is required");
|
|
1861
|
+
}
|
|
1862
|
+
const selection = {};
|
|
1863
|
+
this.keys.forEach((key, idx) => {
|
|
1864
|
+
const alias = keyAlias(key, `key_${idx}`);
|
|
1865
|
+
selection[alias] = key;
|
|
1866
|
+
});
|
|
1867
|
+
Object.entries(this.nonAggregates).forEach(([alias, expr]) => {
|
|
1868
|
+
selection[alias] = this.wrapNonAggWithAnyValue ? anyValue(expr) : expr;
|
|
1869
|
+
});
|
|
1870
|
+
Object.assign(selection, this.measureMap);
|
|
1871
|
+
let query = this.db.select(selection).from(this.source).groupBy(...this.keys);
|
|
1872
|
+
if (this.orderByClauses.length > 0) {
|
|
1873
|
+
query = query.orderBy(...this.orderByClauses);
|
|
1874
|
+
}
|
|
1875
|
+
return query;
|
|
1876
|
+
}
|
|
1877
|
+
run() {
|
|
1878
|
+
return this.build();
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
var olap = (db) => new OlapBuilder(db);
|
|
1561
1882
|
export {
|
|
1883
|
+
wrapperToNodeApiValue,
|
|
1884
|
+
wrapTimestamp,
|
|
1885
|
+
wrapStruct,
|
|
1886
|
+
wrapMap,
|
|
1887
|
+
wrapList,
|
|
1888
|
+
wrapJson,
|
|
1889
|
+
wrapBlob,
|
|
1890
|
+
wrapArray,
|
|
1891
|
+
sumN,
|
|
1892
|
+
sumDistinctN,
|
|
1893
|
+
rowNumber,
|
|
1894
|
+
rank,
|
|
1895
|
+
prepareParams,
|
|
1896
|
+
percentileCont,
|
|
1897
|
+
olap,
|
|
1562
1898
|
migrate,
|
|
1899
|
+
median,
|
|
1900
|
+
lead,
|
|
1901
|
+
lag,
|
|
1902
|
+
isDuckDBWrapper,
|
|
1563
1903
|
introspect,
|
|
1904
|
+
executeOnClient,
|
|
1905
|
+
executeInBatches,
|
|
1906
|
+
executeArrowOnClient,
|
|
1564
1907
|
duckDbTimestamp,
|
|
1565
1908
|
duckDbTime,
|
|
1566
1909
|
duckDbStruct,
|
|
@@ -1576,9 +1919,16 @@ export {
|
|
|
1576
1919
|
duckDbArrayContained,
|
|
1577
1920
|
duckDbArray,
|
|
1578
1921
|
drizzle,
|
|
1922
|
+
denseRank,
|
|
1923
|
+
countN,
|
|
1924
|
+
closeClientConnection,
|
|
1925
|
+
avgN,
|
|
1926
|
+
anyValue,
|
|
1927
|
+
OlapBuilder,
|
|
1579
1928
|
DuckDBTransaction,
|
|
1580
1929
|
DuckDBSession,
|
|
1581
1930
|
DuckDBPreparedQuery,
|
|
1582
1931
|
DuckDBDriver,
|
|
1583
|
-
DuckDBDatabase
|
|
1932
|
+
DuckDBDatabase,
|
|
1933
|
+
DUCKDB_VALUE_MARKER
|
|
1584
1934
|
};
|
package/dist/olap.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Subquery, type SQLWrapper } from 'drizzle-orm';
|
|
2
|
+
import type { AnyPgColumn, PgTable } from 'drizzle-orm/pg-core';
|
|
3
|
+
import type { PgViewBase } from 'drizzle-orm/pg-core/view-base';
|
|
4
|
+
import { SQL } from 'drizzle-orm/sql/sql';
|
|
5
|
+
import type { DuckDBDatabase } from './driver.ts';
|
|
6
|
+
export declare const countN: (expr?: SQLWrapper) => SQL<number>;
|
|
7
|
+
export declare const sumN: (expr: SQLWrapper) => SQL<number>;
|
|
8
|
+
export declare const avgN: (expr: SQLWrapper) => SQL<number>;
|
|
9
|
+
export declare const sumDistinctN: (expr: SQLWrapper) => SQL<number>;
|
|
10
|
+
export declare const percentileCont: (p: number, expr: SQLWrapper) => SQL<number>;
|
|
11
|
+
export declare const median: (expr: SQLWrapper) => SQL<number>;
|
|
12
|
+
export declare const anyValue: <T = unknown>(expr: SQLWrapper) => SQL<T>;
|
|
13
|
+
type PartitionOrder = {
|
|
14
|
+
partitionBy?: SQLWrapper | SQLWrapper[];
|
|
15
|
+
orderBy?: SQLWrapper | SQLWrapper[];
|
|
16
|
+
} | undefined;
|
|
17
|
+
export declare const rowNumber: (options?: PartitionOrder) => SQL<number>;
|
|
18
|
+
export declare const rank: (options?: PartitionOrder) => SQL<number>;
|
|
19
|
+
export declare const denseRank: (options?: PartitionOrder) => SQL<number>;
|
|
20
|
+
export declare const lag: <T = unknown>(expr: SQLWrapper, offset?: number, defaultValue?: SQLWrapper, options?: PartitionOrder) => SQL<T>;
|
|
21
|
+
export declare const lead: <T = unknown>(expr: SQLWrapper, offset?: number, defaultValue?: SQLWrapper, options?: PartitionOrder) => SQL<T>;
|
|
22
|
+
type ValueExpr = SQL | SQL.Aliased | AnyPgColumn;
|
|
23
|
+
type GroupKey = ValueExpr;
|
|
24
|
+
type MeasureMap = Record<string, ValueExpr>;
|
|
25
|
+
type NonAggMap = Record<string, ValueExpr>;
|
|
26
|
+
export declare class OlapBuilder {
|
|
27
|
+
private db;
|
|
28
|
+
private source?;
|
|
29
|
+
private keys;
|
|
30
|
+
private measureMap;
|
|
31
|
+
private nonAggregates;
|
|
32
|
+
private wrapNonAggWithAnyValue;
|
|
33
|
+
private orderByClauses;
|
|
34
|
+
constructor(db: DuckDBDatabase);
|
|
35
|
+
from(source: PgTable | Subquery | PgViewBase | SQL): this;
|
|
36
|
+
groupBy(keys: GroupKey[]): this;
|
|
37
|
+
measures(measures: MeasureMap): this;
|
|
38
|
+
selectNonAggregates(fields: NonAggMap, options?: {
|
|
39
|
+
anyValue?: boolean;
|
|
40
|
+
}): this;
|
|
41
|
+
orderBy(...clauses: ValueExpr[]): this;
|
|
42
|
+
build(): any;
|
|
43
|
+
run(): any;
|
|
44
|
+
}
|
|
45
|
+
export declare const olap: (db: DuckDBDatabase) => OlapBuilder;
|
|
46
|
+
export {};
|
package/dist/session.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { type Query, SQL } from 'drizzle-orm/sql/sql';
|
|
|
9
9
|
import type { Assume } from 'drizzle-orm/utils';
|
|
10
10
|
import type { DuckDBDialect } from './dialect.ts';
|
|
11
11
|
import type { DuckDBClientLike, RowData } from './client.ts';
|
|
12
|
+
import { type ExecuteInBatchesOptions } from './client.ts';
|
|
12
13
|
export type { DuckDBClientLike, RowData } from './client.ts';
|
|
13
14
|
export declare class DuckDBPreparedQuery<T extends PreparedQueryConfig> extends PgPreparedQuery<T> {
|
|
14
15
|
private client;
|
|
@@ -47,12 +48,16 @@ export declare class DuckDBSession<TFullSchema extends Record<string, unknown> =
|
|
|
47
48
|
prepareQuery<T extends PreparedQueryConfig = PreparedQueryConfig>(query: Query, fields: SelectedFieldsOrdered | undefined, name: string | undefined, isResponseInArrayMode: boolean, customResultMapper?: (rows: unknown[][]) => T['execute']): PgPreparedQuery<T>;
|
|
48
49
|
transaction<T>(transaction: (tx: DuckDBTransaction<TFullSchema, TSchema>) => Promise<T>): Promise<T>;
|
|
49
50
|
private warnOnStringArrayLiteral;
|
|
51
|
+
executeBatches<T extends RowData = RowData>(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<GenericRowData<T>[], void, void>;
|
|
52
|
+
executeArrow(query: SQL): Promise<unknown>;
|
|
50
53
|
}
|
|
51
54
|
export declare class DuckDBTransaction<TFullSchema extends Record<string, unknown>, TSchema extends TablesRelationalConfig> extends PgTransaction<DuckDBQueryResultHKT, TFullSchema, TSchema> {
|
|
52
55
|
static readonly [entityKind]: string;
|
|
53
56
|
rollback(): never;
|
|
54
57
|
getTransactionConfigSQL(config: PgTransactionConfig): SQL;
|
|
55
58
|
setTransaction(config: PgTransactionConfig): Promise<void>;
|
|
59
|
+
executeBatches<T extends RowData = RowData>(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<GenericRowData<T>[], void, void>;
|
|
60
|
+
executeArrow(query: SQL): Promise<unknown>;
|
|
56
61
|
transaction<T>(transaction: (tx: DuckDBTransaction<TFullSchema, TSchema>) => Promise<T>): Promise<T>;
|
|
57
62
|
}
|
|
58
63
|
export type GenericRowData<T extends RowData = RowData> = T;
|