@leonardovida-md/drizzle-neo-duckdb 1.1.1 → 1.1.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/dist/index.mjs CHANGED
@@ -153,6 +153,9 @@ function walkRight(source, scrubbed, start) {
153
153
  return [scrubbed.length, source.slice(start)];
154
154
  }
155
155
  function adaptArrayOperators(query) {
156
+ if (query.indexOf("@>") === -1 && query.indexOf("<@") === -1 && query.indexOf("&&") === -1) {
157
+ return query;
158
+ }
156
159
  let rewritten = query;
157
160
  let scrubbed = scrubForRewrite(query);
158
161
  let searchStart = 0;
@@ -524,6 +527,7 @@ function wrapperToNodeApiValue(wrapper, toValue) {
524
527
  function isPool(client) {
525
528
  return typeof client.acquire === "function";
526
529
  }
530
+ var PREPARED_CACHE = Symbol.for("drizzle-duckdb:prepared-cache");
527
531
  function isPgArrayLiteral(value) {
528
532
  return value.startsWith("{") && value.endsWith("}");
529
533
  }
@@ -537,16 +541,20 @@ function parsePgArrayLiteral(value) {
537
541
  }
538
542
  function prepareParams(params, options = {}) {
539
543
  return params.map((param) => {
540
- if (typeof param === "string") {
541
- const trimmed = param.trim();
542
- if (trimmed && isPgArrayLiteral(trimmed)) {
543
- if (options.rejectStringArrayLiterals) {
544
- throw new Error("Stringified array literals are not supported. Use duckDbList()/duckDbArray() or pass native arrays.");
545
- }
546
- if (options.warnOnStringArrayLiteral) {
547
- options.warnOnStringArrayLiteral();
544
+ if (typeof param === "string" && param.length > 0) {
545
+ const firstChar = param[0];
546
+ const maybeArrayLiteral = firstChar === "{" || firstChar === "[" || firstChar === " " || firstChar === "\t";
547
+ if (maybeArrayLiteral) {
548
+ const trimmed = firstChar === "{" || firstChar === "[" ? param : param.trim();
549
+ if (trimmed && isPgArrayLiteral(trimmed)) {
550
+ if (options.rejectStringArrayLiterals) {
551
+ throw new Error("Stringified array literals are not supported. Use duckDbList()/duckDbArray() or pass native arrays.");
552
+ }
553
+ if (options.warnOnStringArrayLiteral) {
554
+ options.warnOnStringArrayLiteral();
555
+ }
556
+ return parsePgArrayLiteral(trimmed);
548
557
  }
549
- return parsePgArrayLiteral(trimmed);
550
558
  }
551
559
  }
552
560
  return param;
@@ -571,13 +579,121 @@ function toNodeApiValue(value) {
571
579
  return value;
572
580
  }
573
581
  function deduplicateColumns(columns) {
574
- const seen = {};
575
- return columns.map((col) => {
576
- const count = seen[col] ?? 0;
577
- seen[col] = count + 1;
578
- return count === 0 ? col : `${col}_${count}`;
582
+ const counts = new Map;
583
+ let hasDuplicates = false;
584
+ for (const column of columns) {
585
+ const next = (counts.get(column) ?? 0) + 1;
586
+ counts.set(column, next);
587
+ if (next > 1) {
588
+ hasDuplicates = true;
589
+ break;
590
+ }
591
+ }
592
+ if (!hasDuplicates) {
593
+ return columns;
594
+ }
595
+ counts.clear();
596
+ return columns.map((column) => {
597
+ const count = counts.get(column) ?? 0;
598
+ counts.set(column, count + 1);
599
+ return count === 0 ? column : `${column}_${count}`;
579
600
  });
580
601
  }
602
+ function destroyPreparedStatement(entry) {
603
+ if (!entry)
604
+ return;
605
+ try {
606
+ entry.statement.destroySync();
607
+ } catch {}
608
+ }
609
+ function getPreparedCache(connection, size) {
610
+ const store = connection;
611
+ const existing = store[PREPARED_CACHE];
612
+ if (existing) {
613
+ existing.size = size;
614
+ return existing;
615
+ }
616
+ const cache = { size, entries: new Map };
617
+ store[PREPARED_CACHE] = cache;
618
+ return cache;
619
+ }
620
+ function evictOldest(cache) {
621
+ const oldest = cache.entries.keys().next();
622
+ if (!oldest.done) {
623
+ const key = oldest.value;
624
+ const entry = cache.entries.get(key);
625
+ cache.entries.delete(key);
626
+ destroyPreparedStatement(entry);
627
+ }
628
+ }
629
+ function evictCacheEntry(cache, key) {
630
+ const entry = cache.entries.get(key);
631
+ cache.entries.delete(key);
632
+ destroyPreparedStatement(entry);
633
+ }
634
+ async function getOrPrepareStatement(connection, query, cacheConfig) {
635
+ const cache = getPreparedCache(connection, cacheConfig.size);
636
+ const cached = cache.entries.get(query);
637
+ if (cached) {
638
+ cache.entries.delete(query);
639
+ cache.entries.set(query, cached);
640
+ return cached.statement;
641
+ }
642
+ const statement = await connection.prepare(query);
643
+ cache.entries.set(query, { statement });
644
+ while (cache.entries.size > cache.size) {
645
+ evictOldest(cache);
646
+ }
647
+ return statement;
648
+ }
649
+ async function materializeResultRows(result) {
650
+ const rows = await result.getRowsJS() ?? [];
651
+ const baseColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
652
+ const columns = typeof result.deduplicatedColumnNames === "function" ? baseColumns : deduplicateColumns(baseColumns);
653
+ return { columns, rows };
654
+ }
655
+ async function materializeRows(client, query, params, options = {}) {
656
+ if (isPool(client)) {
657
+ const connection2 = await client.acquire();
658
+ try {
659
+ return await materializeRows(connection2, query, params, options);
660
+ } finally {
661
+ await client.release(connection2);
662
+ }
663
+ }
664
+ const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
665
+ const connection = client;
666
+ if (options.prepareCache && typeof connection.prepare === "function") {
667
+ const cache = getPreparedCache(connection, options.prepareCache.size);
668
+ try {
669
+ const statement = await getOrPrepareStatement(connection, query, options.prepareCache);
670
+ if (values) {
671
+ statement.bind(values);
672
+ } else {
673
+ statement.clearBindings?.();
674
+ }
675
+ const result2 = await statement.run();
676
+ cache.entries.delete(query);
677
+ cache.entries.set(query, { statement });
678
+ return await materializeResultRows(result2);
679
+ } catch (error) {
680
+ evictCacheEntry(cache, query);
681
+ throw error;
682
+ }
683
+ }
684
+ const result = await connection.run(query, values);
685
+ return await materializeResultRows(result);
686
+ }
687
+ function clearPreparedCache(connection) {
688
+ const store = connection;
689
+ const cache = store[PREPARED_CACHE];
690
+ if (!cache)
691
+ return;
692
+ for (const entry of cache.entries.values()) {
693
+ destroyPreparedStatement(entry);
694
+ }
695
+ cache.entries.clear();
696
+ }
581
697
  function mapRowsToObjects(columns, rows) {
582
698
  return rows.map((vals) => {
583
699
  const obj = {};
@@ -588,6 +704,7 @@ function mapRowsToObjects(columns, rows) {
588
704
  });
589
705
  }
590
706
  async function closeClientConnection(connection) {
707
+ clearPreparedCache(connection);
591
708
  if ("close" in connection && typeof connection.close === "function") {
592
709
  await connection.close();
593
710
  return;
@@ -600,27 +717,51 @@ async function closeClientConnection(connection) {
600
717
  connection.disconnectSync();
601
718
  }
602
719
  }
603
- async function executeOnClient(client, query, params) {
720
+ async function executeOnClient(client, query, params, options = {}) {
721
+ const { columns, rows } = await materializeRows(client, query, params, options);
722
+ if (!rows || rows.length === 0) {
723
+ return [];
724
+ }
725
+ return mapRowsToObjects(columns, rows);
726
+ }
727
+ async function executeArraysOnClient(client, query, params, options = {}) {
728
+ return await materializeRows(client, query, params, options);
729
+ }
730
+ async function* executeInBatches(client, query, params, options = {}) {
604
731
  if (isPool(client)) {
605
732
  const connection = await client.acquire();
606
733
  try {
607
- return await executeOnClient(connection, query, params);
734
+ yield* executeInBatches(connection, query, params, options);
735
+ return;
608
736
  } finally {
609
737
  await client.release(connection);
610
738
  }
611
739
  }
740
+ const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
612
741
  const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
613
- const result = await client.run(query, values);
614
- const rows = await result.getRowsJS();
615
- const columns = result.deduplicatedColumnNames?.() ?? result.columnNames();
616
- const uniqueColumns = deduplicateColumns(columns);
617
- return rows ? mapRowsToObjects(uniqueColumns, rows) : [];
742
+ const result = await client.stream(query, values);
743
+ const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
744
+ const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
745
+ let buffer = [];
746
+ for await (const chunk of result.yieldRowsJs()) {
747
+ const objects = mapRowsToObjects(columns, chunk);
748
+ for (const row of objects) {
749
+ buffer.push(row);
750
+ if (buffer.length >= rowsPerChunk) {
751
+ yield buffer;
752
+ buffer = [];
753
+ }
754
+ }
755
+ }
756
+ if (buffer.length > 0) {
757
+ yield buffer;
758
+ }
618
759
  }
619
- async function* executeInBatches(client, query, params, options = {}) {
760
+ async function* executeInBatchesRaw(client, query, params, options = {}) {
620
761
  if (isPool(client)) {
621
762
  const connection = await client.acquire();
622
763
  try {
623
- yield* executeInBatches(connection, query, params, options);
764
+ yield* executeInBatchesRaw(connection, query, params, options);
624
765
  return;
625
766
  } finally {
626
767
  await client.release(connection);
@@ -629,21 +770,20 @@ async function* executeInBatches(client, query, params, options = {}) {
629
770
  const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
630
771
  const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
631
772
  const result = await client.stream(query, values);
632
- const columns = result.deduplicatedColumnNames?.() ?? result.columnNames();
633
- const uniqueColumns = deduplicateColumns(columns);
773
+ const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
774
+ const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
634
775
  let buffer = [];
635
776
  for await (const chunk of result.yieldRowsJs()) {
636
- const objects = mapRowsToObjects(uniqueColumns, chunk);
637
- for (const row of objects) {
777
+ for (const row of chunk) {
638
778
  buffer.push(row);
639
779
  if (buffer.length >= rowsPerChunk) {
640
- yield buffer;
780
+ yield { columns, rows: buffer };
641
781
  buffer = [];
642
782
  }
643
783
  }
644
784
  }
645
785
  if (buffer.length > 0) {
646
- yield buffer;
786
+ yield { columns, rows: buffer };
647
787
  }
648
788
  }
649
789
  async function executeArrowOnClient(client, query, params) {
@@ -671,6 +811,13 @@ function isSavepointSyntaxError(error) {
671
811
  }
672
812
  return error.message.toLowerCase().includes("savepoint") && error.message.toLowerCase().includes("syntax error");
673
813
  }
814
+ function rewriteQuery(mode, query) {
815
+ if (mode === "never") {
816
+ return { sql: query, rewritten: false };
817
+ }
818
+ const rewritten = adaptArrayOperators(query);
819
+ return { sql: rewritten, rewritten: rewritten !== query };
820
+ }
674
821
 
675
822
  class DuckDBPreparedQuery extends PgPreparedQuery {
676
823
  client;
@@ -681,11 +828,12 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
681
828
  fields;
682
829
  _isResponseInArrayMode;
683
830
  customResultMapper;
684
- rewriteArrays;
831
+ rewriteArraysMode;
685
832
  rejectStringArrayLiterals;
833
+ prepareCache;
686
834
  warnOnStringArrayLiteral;
687
835
  static [entityKind] = "DuckDBPreparedQuery";
688
- constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rewriteArrays, rejectStringArrayLiterals, warnOnStringArrayLiteral) {
836
+ constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rewriteArraysMode, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
689
837
  super({ sql: queryString, params });
690
838
  this.client = client;
691
839
  this.dialect = dialect;
@@ -695,8 +843,9 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
695
843
  this.fields = fields;
696
844
  this._isResponseInArrayMode = _isResponseInArrayMode;
697
845
  this.customResultMapper = customResultMapper;
698
- this.rewriteArrays = rewriteArrays;
846
+ this.rewriteArraysMode = rewriteArraysMode;
699
847
  this.rejectStringArrayLiterals = rejectStringArrayLiterals;
848
+ this.prepareCache = prepareCache;
700
849
  this.warnOnStringArrayLiteral = warnOnStringArrayLiteral;
701
850
  }
702
851
  async execute(placeholderValues = {}) {
@@ -705,18 +854,23 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
705
854
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
706
855
  warnOnStringArrayLiteral: this.warnOnStringArrayLiteral ? () => this.warnOnStringArrayLiteral?.(this.queryString) : undefined
707
856
  });
708
- const rewrittenQuery = this.rewriteArrays ? adaptArrayOperators(this.queryString) : this.queryString;
709
- if (this.rewriteArrays && rewrittenQuery !== this.queryString) {
857
+ const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, this.queryString);
858
+ if (didRewrite) {
710
859
  this.logger.logQuery(`[duckdb] original query before array rewrite: ${this.queryString}`, params);
711
860
  }
712
861
  this.logger.logQuery(rewrittenQuery, params);
713
862
  const { fields, joinsNotNullableMap, customResultMapper } = this;
714
- const rows = await executeOnClient(this.client, rewrittenQuery, params);
715
- if (rows.length === 0 || !fields) {
716
- return rows;
863
+ if (fields) {
864
+ const { rows: rows2 } = await executeArraysOnClient(this.client, rewrittenQuery, params, { prepareCache: this.prepareCache });
865
+ if (rows2.length === 0) {
866
+ return [];
867
+ }
868
+ return customResultMapper ? customResultMapper(rows2) : rows2.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
717
869
  }
718
- const rowValues = rows.map((row) => Object.values(row));
719
- return customResultMapper ? customResultMapper(rowValues) : rowValues.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
870
+ const rows = await executeOnClient(this.client, rewrittenQuery, params, {
871
+ prepareCache: this.prepareCache
872
+ });
873
+ return rows;
720
874
  }
721
875
  all(placeholderValues = {}) {
722
876
  return this.execute(placeholderValues);
@@ -733,8 +887,9 @@ class DuckDBSession extends PgSession {
733
887
  static [entityKind] = "DuckDBSession";
734
888
  dialect;
735
889
  logger;
736
- rewriteArrays;
890
+ rewriteArraysMode;
737
891
  rejectStringArrayLiterals;
892
+ prepareCache;
738
893
  hasWarnedArrayLiteral = false;
739
894
  rollbackOnly = false;
740
895
  constructor(client, dialect, schema, options = {}) {
@@ -744,11 +899,17 @@ class DuckDBSession extends PgSession {
744
899
  this.options = options;
745
900
  this.dialect = dialect;
746
901
  this.logger = options.logger ?? new NoopLogger;
747
- this.rewriteArrays = options.rewriteArrays ?? true;
902
+ this.rewriteArraysMode = options.rewriteArrays ?? "auto";
748
903
  this.rejectStringArrayLiterals = options.rejectStringArrayLiterals ?? false;
904
+ this.prepareCache = options.prepareCache;
905
+ this.options = {
906
+ ...options,
907
+ rewriteArrays: this.rewriteArraysMode,
908
+ prepareCache: this.prepareCache
909
+ };
749
910
  }
750
911
  prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
751
- return new DuckDBPreparedQuery(this.client, this.dialect, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, this.rewriteArrays, this.rejectStringArrayLiterals, this.rejectStringArrayLiterals ? undefined : this.warnOnStringArrayLiteral);
912
+ return new DuckDBPreparedQuery(this.client, this.dialect, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, this.rewriteArraysMode, this.rejectStringArrayLiterals, this.prepareCache, this.rejectStringArrayLiterals ? undefined : this.warnOnStringArrayLiteral);
752
913
  }
753
914
  execute(query) {
754
915
  this.dialect.resetPgJsonFlag();
@@ -808,13 +969,28 @@ query: ${query}`, []);
808
969
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
809
970
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
810
971
  });
811
- const rewrittenQuery = this.rewriteArrays ? adaptArrayOperators(builtQuery.sql) : builtQuery.sql;
812
- if (this.rewriteArrays && rewrittenQuery !== builtQuery.sql) {
972
+ const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
973
+ if (didRewrite) {
813
974
  this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
814
975
  }
815
976
  this.logger.logQuery(rewrittenQuery, params);
816
977
  return executeInBatches(this.client, rewrittenQuery, params, options);
817
978
  }
979
+ executeBatchesRaw(query, options = {}) {
980
+ this.dialect.resetPgJsonFlag();
981
+ const builtQuery = this.dialect.sqlToQuery(query);
982
+ this.dialect.assertNoPgJsonColumns();
983
+ const params = prepareParams(builtQuery.params, {
984
+ rejectStringArrayLiterals: this.rejectStringArrayLiterals,
985
+ warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
986
+ });
987
+ const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
988
+ if (didRewrite) {
989
+ this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
990
+ }
991
+ this.logger.logQuery(rewrittenQuery, params);
992
+ return executeInBatchesRaw(this.client, rewrittenQuery, params, options);
993
+ }
818
994
  async executeArrow(query) {
819
995
  this.dialect.resetPgJsonFlag();
820
996
  const builtQuery = this.dialect.sqlToQuery(query);
@@ -823,8 +999,8 @@ query: ${query}`, []);
823
999
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
824
1000
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
825
1001
  });
826
- const rewrittenQuery = this.rewriteArrays ? adaptArrayOperators(builtQuery.sql) : builtQuery.sql;
827
- if (this.rewriteArrays && rewrittenQuery !== builtQuery.sql) {
1002
+ const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
1003
+ if (didRewrite) {
828
1004
  this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
829
1005
  }
830
1006
  this.logger.logQuery(rewrittenQuery, params);
@@ -862,6 +1038,9 @@ class DuckDBTransaction extends PgTransaction {
862
1038
  executeBatches(query, options = {}) {
863
1039
  return this.session.executeBatches(query, options);
864
1040
  }
1041
+ executeBatchesRaw(query, options = {}) {
1042
+ return this.session.executeBatchesRaw(query, options);
1043
+ }
865
1044
  executeArrow(query) {
866
1045
  return this.session.executeArrow(query);
867
1046
  }
@@ -1273,6 +1452,32 @@ function createDuckDBConnectionPool(instance, options = {}) {
1273
1452
  };
1274
1453
  }
1275
1454
 
1455
+ // src/options.ts
1456
+ var DEFAULT_REWRITE_ARRAYS_MODE = "auto";
1457
+ function resolveRewriteArraysOption(value) {
1458
+ if (value === undefined)
1459
+ return DEFAULT_REWRITE_ARRAYS_MODE;
1460
+ if (value === true)
1461
+ return "auto";
1462
+ if (value === false)
1463
+ return "never";
1464
+ return value;
1465
+ }
1466
+ var DEFAULT_PREPARED_CACHE_SIZE = 32;
1467
+ function resolvePrepareCacheOption(option) {
1468
+ if (!option)
1469
+ return;
1470
+ if (option === true) {
1471
+ return { size: DEFAULT_PREPARED_CACHE_SIZE };
1472
+ }
1473
+ if (typeof option === "number") {
1474
+ const size2 = Math.max(1, Math.floor(option));
1475
+ return { size: size2 };
1476
+ }
1477
+ const size = option.size ?? DEFAULT_PREPARED_CACHE_SIZE;
1478
+ return { size: Math.max(1, Math.floor(size)) };
1479
+ }
1480
+
1276
1481
  // src/driver.ts
1277
1482
  class DuckDBDriver {
1278
1483
  client;
@@ -1287,8 +1492,9 @@ class DuckDBDriver {
1287
1492
  createSession(schema) {
1288
1493
  return new DuckDBSession(this.client, this.dialect, schema, {
1289
1494
  logger: this.options.logger,
1290
- rewriteArrays: this.options.rewriteArrays,
1291
- rejectStringArrayLiterals: this.options.rejectStringArrayLiterals
1495
+ rewriteArrays: this.options.rewriteArrays ?? "auto",
1496
+ rejectStringArrayLiterals: this.options.rejectStringArrayLiterals,
1497
+ prepareCache: this.options.prepareCache
1292
1498
  });
1293
1499
  }
1294
1500
  }
@@ -1301,6 +1507,8 @@ function isConfigObject(data) {
1301
1507
  }
1302
1508
  function createFromClient(client, config = {}, instance) {
1303
1509
  const dialect = new DuckDBDialect;
1510
+ const rewriteArraysMode = resolveRewriteArraysOption(config.rewriteArrays);
1511
+ const prepareCache = resolvePrepareCacheOption(config.prepareCache);
1304
1512
  const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
1305
1513
  let schema;
1306
1514
  if (config.schema) {
@@ -1313,8 +1521,9 @@ function createFromClient(client, config = {}, instance) {
1313
1521
  }
1314
1522
  const driver = new DuckDBDriver(client, dialect, {
1315
1523
  logger,
1316
- rewriteArrays: config.rewriteArrays,
1317
- rejectStringArrayLiterals: config.rejectStringArrayLiterals
1524
+ rewriteArrays: rewriteArraysMode,
1525
+ rejectStringArrayLiterals: config.rejectStringArrayLiterals,
1526
+ prepareCache
1318
1527
  });
1319
1528
  const session = driver.createSession(schema);
1320
1529
  const db = new DuckDBDatabase(dialect, session, schema, client, instance);
@@ -1394,6 +1603,9 @@ class DuckDBDatabase extends PgDatabase {
1394
1603
  executeBatches(query, options = {}) {
1395
1604
  return this.session.executeBatches(query, options);
1396
1605
  }
1606
+ executeBatchesRaw(query, options = {}) {
1607
+ return this.session.executeBatchesRaw(query, options);
1608
+ }
1397
1609
  executeArrow(query) {
1398
1610
  return this.session.executeArrow(query);
1399
1611
  }
@@ -1594,6 +1806,21 @@ var duckDbInterval = (name) => customType({
1594
1806
  return value;
1595
1807
  }
1596
1808
  })(name);
1809
+ function shouldBindTimestamp(options) {
1810
+ const bindMode = options.bindMode ?? "auto";
1811
+ if (bindMode === "bind")
1812
+ return true;
1813
+ if (bindMode === "literal")
1814
+ return false;
1815
+ const isBun = typeof process !== "undefined" && typeof process.versions?.bun !== "undefined";
1816
+ if (isBun)
1817
+ return false;
1818
+ const forceLiteral = typeof process !== "undefined" ? process.env.DRIZZLE_DUCKDB_FORCE_LITERAL_TIMESTAMPS : undefined;
1819
+ if (forceLiteral && forceLiteral !== "0") {
1820
+ return false;
1821
+ }
1822
+ return true;
1823
+ }
1597
1824
  var duckDbTimestamp = (name, options = {}) => customType({
1598
1825
  dataType() {
1599
1826
  if (options.withTimezone) {
@@ -1603,12 +1830,19 @@ var duckDbTimestamp = (name, options = {}) => customType({
1603
1830
  return `TIMESTAMP${precision}`;
1604
1831
  },
1605
1832
  toDriver(value) {
1833
+ if (shouldBindTimestamp(options)) {
1834
+ return wrapTimestamp(value, options.withTimezone ?? false, options.precision);
1835
+ }
1606
1836
  const iso = value instanceof Date ? value.toISOString() : value;
1607
1837
  const normalized = iso.replace("T", " ").replace("Z", "+00");
1608
1838
  const typeKeyword = options.withTimezone ? "TIMESTAMPTZ" : "TIMESTAMP";
1609
1839
  return sql4.raw(`${typeKeyword} '${normalized}'`);
1610
1840
  },
1611
1841
  fromDriver(value) {
1842
+ if (value && typeof value === "object" && "kind" in value && value.kind === "timestamp") {
1843
+ const wrapped = value;
1844
+ return wrapped.data instanceof Date ? wrapped.data : typeof wrapped.data === "number" || typeof wrapped.data === "bigint" ? new Date(Number(wrapped.data) / 1000) : wrapped.data;
1845
+ }
1612
1846
  if (options.mode === "string") {
1613
1847
  if (value instanceof Date) {
1614
1848
  return value.toISOString().replace("T", " ").replace("Z", "+00");
@@ -2364,6 +2598,8 @@ export {
2364
2598
  sumDistinctN,
2365
2599
  splitTopLevel,
2366
2600
  rowNumber,
2601
+ resolveRewriteArraysOption,
2602
+ resolvePrepareCacheOption,
2367
2603
  resolvePoolSize,
2368
2604
  rank,
2369
2605
  prepareParams,
@@ -2380,8 +2616,10 @@ export {
2380
2616
  introspect,
2381
2617
  formatLiteral,
2382
2618
  executeOnClient,
2619
+ executeInBatchesRaw,
2383
2620
  executeInBatches,
2384
2621
  executeArrowOnClient,
2622
+ executeArraysOnClient,
2385
2623
  duckDbTimestamp,
2386
2624
  duckDbTime,
2387
2625
  duckDbStruct,
@@ -0,0 +1,10 @@
1
+ export type RewriteArraysMode = 'auto' | 'always' | 'never';
2
+ export type RewriteArraysOption = boolean | RewriteArraysMode;
3
+ export declare function resolveRewriteArraysOption(value?: RewriteArraysOption): RewriteArraysMode;
4
+ export type PrepareCacheOption = boolean | number | {
5
+ size?: number;
6
+ };
7
+ export interface PreparedStatementCacheConfig {
8
+ size: number;
9
+ }
10
+ export declare function resolvePrepareCacheOption(option?: PrepareCacheOption): PreparedStatementCacheConfig | undefined;
package/dist/session.d.ts CHANGED
@@ -9,7 +9,8 @@ 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
+ import { type ExecuteBatchesRawChunk, type ExecuteInBatchesOptions } from './client.ts';
13
+ import type { PreparedStatementCacheConfig, RewriteArraysMode } from './options.ts';
13
14
  export type { DuckDBClientLike, RowData } from './client.ts';
14
15
  export declare class DuckDBPreparedQuery<T extends PreparedQueryConfig> extends PgPreparedQuery<T> {
15
16
  private client;
@@ -20,19 +21,21 @@ export declare class DuckDBPreparedQuery<T extends PreparedQueryConfig> extends
20
21
  private fields;
21
22
  private _isResponseInArrayMode;
22
23
  private customResultMapper;
23
- private rewriteArrays;
24
+ private rewriteArraysMode;
24
25
  private rejectStringArrayLiterals;
26
+ private prepareCache;
25
27
  private warnOnStringArrayLiteral?;
26
28
  static readonly [entityKind]: string;
27
- constructor(client: DuckDBClientLike, dialect: DuckDBDialect, queryString: string, params: unknown[], logger: Logger, fields: SelectedFieldsOrdered | undefined, _isResponseInArrayMode: boolean, customResultMapper: ((rows: unknown[][]) => T['execute']) | undefined, rewriteArrays: boolean, rejectStringArrayLiterals: boolean, warnOnStringArrayLiteral?: ((sql: string) => void) | undefined);
29
+ constructor(client: DuckDBClientLike, dialect: DuckDBDialect, queryString: string, params: unknown[], logger: Logger, fields: SelectedFieldsOrdered | undefined, _isResponseInArrayMode: boolean, customResultMapper: ((rows: unknown[][]) => T['execute']) | undefined, rewriteArraysMode: RewriteArraysMode, rejectStringArrayLiterals: boolean, prepareCache: PreparedStatementCacheConfig | undefined, warnOnStringArrayLiteral?: ((sql: string) => void) | undefined);
28
30
  execute(placeholderValues?: Record<string, unknown> | undefined): Promise<T['execute']>;
29
31
  all(placeholderValues?: Record<string, unknown> | undefined): Promise<T['all']>;
30
32
  isResponseInArrayMode(): boolean;
31
33
  }
32
34
  export interface DuckDBSessionOptions {
33
35
  logger?: Logger;
34
- rewriteArrays?: boolean;
36
+ rewriteArrays?: RewriteArraysMode;
35
37
  rejectStringArrayLiterals?: boolean;
38
+ prepareCache?: PreparedStatementCacheConfig;
36
39
  }
37
40
  export declare class DuckDBSession<TFullSchema extends Record<string, unknown> = Record<string, never>, TSchema extends TablesRelationalConfig = Record<string, never>> extends PgSession<DuckDBQueryResultHKT, TFullSchema, TSchema> {
38
41
  private client;
@@ -41,8 +44,9 @@ export declare class DuckDBSession<TFullSchema extends Record<string, unknown> =
41
44
  static readonly [entityKind]: string;
42
45
  protected dialect: DuckDBDialect;
43
46
  private logger;
44
- private rewriteArrays;
47
+ private rewriteArraysMode;
45
48
  private rejectStringArrayLiterals;
49
+ private prepareCache;
46
50
  private hasWarnedArrayLiteral;
47
51
  private rollbackOnly;
48
52
  constructor(client: DuckDBClientLike, dialect: DuckDBDialect, schema: RelationalSchemaConfig<TSchema> | undefined, options?: DuckDBSessionOptions);
@@ -52,6 +56,7 @@ export declare class DuckDBSession<TFullSchema extends Record<string, unknown> =
52
56
  transaction<T>(transaction: (tx: DuckDBTransaction<TFullSchema, TSchema>) => Promise<T>, config?: PgTransactionConfig): Promise<T>;
53
57
  private warnOnStringArrayLiteral;
54
58
  executeBatches<T extends RowData = RowData>(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<GenericRowData<T>[], void, void>;
59
+ executeBatchesRaw(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<ExecuteBatchesRawChunk, void, void>;
55
60
  executeArrow(query: SQL): Promise<unknown>;
56
61
  markRollbackOnly(): void;
57
62
  isRollbackOnly(): boolean;
@@ -62,6 +67,7 @@ export declare class DuckDBTransaction<TFullSchema extends Record<string, unknow
62
67
  getTransactionConfigSQL(config: PgTransactionConfig): SQL;
63
68
  setTransaction(config: PgTransactionConfig): Promise<void>;
64
69
  executeBatches<T extends RowData = RowData>(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<GenericRowData<T>[], void, void>;
70
+ executeBatchesRaw(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<ExecuteBatchesRawChunk, void, void>;
65
71
  executeArrow(query: SQL): Promise<unknown>;
66
72
  transaction<T>(transaction: (tx: DuckDBTransaction<TFullSchema, TSchema>) => Promise<T>): Promise<T>;
67
73
  private runNestedWithoutSavepoint;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "module": "./dist/index.mjs",
4
4
  "main": "./dist/index.mjs",
5
5
  "types": "./dist/index.d.ts",
6
- "version": "1.1.1",
6
+ "version": "1.1.2",
7
7
  "description": "A drizzle ORM client for use with DuckDB. Based on drizzle's Postgres client.",
8
8
  "type": "module",
9
9
  "scripts": {