@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.
@@ -161,6 +161,9 @@ function walkRight(source, scrubbed, start) {
161
161
  return [scrubbed.length, source.slice(start)];
162
162
  }
163
163
  function adaptArrayOperators(query) {
164
+ if (query.indexOf("@>") === -1 && query.indexOf("<@") === -1 && query.indexOf("&&") === -1) {
165
+ return query;
166
+ }
164
167
  let rewritten = query;
165
168
  let scrubbed = scrubForRewrite(query);
166
169
  let searchStart = 0;
@@ -473,6 +476,7 @@ function wrapperToNodeApiValue(wrapper, toValue) {
473
476
  function isPool(client) {
474
477
  return typeof client.acquire === "function";
475
478
  }
479
+ var PREPARED_CACHE = Symbol.for("drizzle-duckdb:prepared-cache");
476
480
  function isPgArrayLiteral(value) {
477
481
  return value.startsWith("{") && value.endsWith("}");
478
482
  }
@@ -486,16 +490,20 @@ function parsePgArrayLiteral(value) {
486
490
  }
487
491
  function prepareParams(params, options = {}) {
488
492
  return params.map((param) => {
489
- if (typeof param === "string") {
490
- const trimmed = param.trim();
491
- if (trimmed && isPgArrayLiteral(trimmed)) {
492
- if (options.rejectStringArrayLiterals) {
493
- throw new Error("Stringified array literals are not supported. Use duckDbList()/duckDbArray() or pass native arrays.");
494
- }
495
- if (options.warnOnStringArrayLiteral) {
496
- options.warnOnStringArrayLiteral();
493
+ if (typeof param === "string" && param.length > 0) {
494
+ const firstChar = param[0];
495
+ const maybeArrayLiteral = firstChar === "{" || firstChar === "[" || firstChar === " " || firstChar === "\t";
496
+ if (maybeArrayLiteral) {
497
+ const trimmed = firstChar === "{" || firstChar === "[" ? param : param.trim();
498
+ if (trimmed && isPgArrayLiteral(trimmed)) {
499
+ if (options.rejectStringArrayLiterals) {
500
+ throw new Error("Stringified array literals are not supported. Use duckDbList()/duckDbArray() or pass native arrays.");
501
+ }
502
+ if (options.warnOnStringArrayLiteral) {
503
+ options.warnOnStringArrayLiteral();
504
+ }
505
+ return parsePgArrayLiteral(trimmed);
497
506
  }
498
- return parsePgArrayLiteral(trimmed);
499
507
  }
500
508
  }
501
509
  return param;
@@ -520,13 +528,121 @@ function toNodeApiValue(value) {
520
528
  return value;
521
529
  }
522
530
  function deduplicateColumns(columns) {
523
- const seen = {};
524
- return columns.map((col) => {
525
- const count = seen[col] ?? 0;
526
- seen[col] = count + 1;
527
- return count === 0 ? col : `${col}_${count}`;
531
+ const counts = new Map;
532
+ let hasDuplicates = false;
533
+ for (const column of columns) {
534
+ const next = (counts.get(column) ?? 0) + 1;
535
+ counts.set(column, next);
536
+ if (next > 1) {
537
+ hasDuplicates = true;
538
+ break;
539
+ }
540
+ }
541
+ if (!hasDuplicates) {
542
+ return columns;
543
+ }
544
+ counts.clear();
545
+ return columns.map((column) => {
546
+ const count = counts.get(column) ?? 0;
547
+ counts.set(column, count + 1);
548
+ return count === 0 ? column : `${column}_${count}`;
528
549
  });
529
550
  }
551
+ function destroyPreparedStatement(entry) {
552
+ if (!entry)
553
+ return;
554
+ try {
555
+ entry.statement.destroySync();
556
+ } catch {}
557
+ }
558
+ function getPreparedCache(connection, size) {
559
+ const store = connection;
560
+ const existing = store[PREPARED_CACHE];
561
+ if (existing) {
562
+ existing.size = size;
563
+ return existing;
564
+ }
565
+ const cache = { size, entries: new Map };
566
+ store[PREPARED_CACHE] = cache;
567
+ return cache;
568
+ }
569
+ function evictOldest(cache) {
570
+ const oldest = cache.entries.keys().next();
571
+ if (!oldest.done) {
572
+ const key = oldest.value;
573
+ const entry = cache.entries.get(key);
574
+ cache.entries.delete(key);
575
+ destroyPreparedStatement(entry);
576
+ }
577
+ }
578
+ function evictCacheEntry(cache, key) {
579
+ const entry = cache.entries.get(key);
580
+ cache.entries.delete(key);
581
+ destroyPreparedStatement(entry);
582
+ }
583
+ async function getOrPrepareStatement(connection, query, cacheConfig) {
584
+ const cache = getPreparedCache(connection, cacheConfig.size);
585
+ const cached = cache.entries.get(query);
586
+ if (cached) {
587
+ cache.entries.delete(query);
588
+ cache.entries.set(query, cached);
589
+ return cached.statement;
590
+ }
591
+ const statement = await connection.prepare(query);
592
+ cache.entries.set(query, { statement });
593
+ while (cache.entries.size > cache.size) {
594
+ evictOldest(cache);
595
+ }
596
+ return statement;
597
+ }
598
+ async function materializeResultRows(result) {
599
+ const rows = await result.getRowsJS() ?? [];
600
+ const baseColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
601
+ const columns = typeof result.deduplicatedColumnNames === "function" ? baseColumns : deduplicateColumns(baseColumns);
602
+ return { columns, rows };
603
+ }
604
+ async function materializeRows(client, query, params, options = {}) {
605
+ if (isPool(client)) {
606
+ const connection2 = await client.acquire();
607
+ try {
608
+ return await materializeRows(connection2, query, params, options);
609
+ } finally {
610
+ await client.release(connection2);
611
+ }
612
+ }
613
+ const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
614
+ const connection = client;
615
+ if (options.prepareCache && typeof connection.prepare === "function") {
616
+ const cache = getPreparedCache(connection, options.prepareCache.size);
617
+ try {
618
+ const statement = await getOrPrepareStatement(connection, query, options.prepareCache);
619
+ if (values) {
620
+ statement.bind(values);
621
+ } else {
622
+ statement.clearBindings?.();
623
+ }
624
+ const result2 = await statement.run();
625
+ cache.entries.delete(query);
626
+ cache.entries.set(query, { statement });
627
+ return await materializeResultRows(result2);
628
+ } catch (error) {
629
+ evictCacheEntry(cache, query);
630
+ throw error;
631
+ }
632
+ }
633
+ const result = await connection.run(query, values);
634
+ return await materializeResultRows(result);
635
+ }
636
+ function clearPreparedCache(connection) {
637
+ const store = connection;
638
+ const cache = store[PREPARED_CACHE];
639
+ if (!cache)
640
+ return;
641
+ for (const entry of cache.entries.values()) {
642
+ destroyPreparedStatement(entry);
643
+ }
644
+ cache.entries.clear();
645
+ }
530
646
  function mapRowsToObjects(columns, rows) {
531
647
  return rows.map((vals) => {
532
648
  const obj = {};
@@ -537,6 +653,7 @@ function mapRowsToObjects(columns, rows) {
537
653
  });
538
654
  }
539
655
  async function closeClientConnection(connection) {
656
+ clearPreparedCache(connection);
540
657
  if ("close" in connection && typeof connection.close === "function") {
541
658
  await connection.close();
542
659
  return;
@@ -549,27 +666,51 @@ async function closeClientConnection(connection) {
549
666
  connection.disconnectSync();
550
667
  }
551
668
  }
552
- async function executeOnClient(client, query, params) {
669
+ async function executeOnClient(client, query, params, options = {}) {
670
+ const { columns, rows } = await materializeRows(client, query, params, options);
671
+ if (!rows || rows.length === 0) {
672
+ return [];
673
+ }
674
+ return mapRowsToObjects(columns, rows);
675
+ }
676
+ async function executeArraysOnClient(client, query, params, options = {}) {
677
+ return await materializeRows(client, query, params, options);
678
+ }
679
+ async function* executeInBatches(client, query, params, options = {}) {
553
680
  if (isPool(client)) {
554
681
  const connection = await client.acquire();
555
682
  try {
556
- return await executeOnClient(connection, query, params);
683
+ yield* executeInBatches(connection, query, params, options);
684
+ return;
557
685
  } finally {
558
686
  await client.release(connection);
559
687
  }
560
688
  }
689
+ const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
561
690
  const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
562
- const result = await client.run(query, values);
563
- const rows = await result.getRowsJS();
564
- const columns = result.deduplicatedColumnNames?.() ?? result.columnNames();
565
- const uniqueColumns = deduplicateColumns(columns);
566
- return rows ? mapRowsToObjects(uniqueColumns, rows) : [];
691
+ const result = await client.stream(query, values);
692
+ const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
693
+ const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
694
+ let buffer = [];
695
+ for await (const chunk of result.yieldRowsJs()) {
696
+ const objects = mapRowsToObjects(columns, chunk);
697
+ for (const row of objects) {
698
+ buffer.push(row);
699
+ if (buffer.length >= rowsPerChunk) {
700
+ yield buffer;
701
+ buffer = [];
702
+ }
703
+ }
704
+ }
705
+ if (buffer.length > 0) {
706
+ yield buffer;
707
+ }
567
708
  }
568
- async function* executeInBatches(client, query, params, options = {}) {
709
+ async function* executeInBatchesRaw(client, query, params, options = {}) {
569
710
  if (isPool(client)) {
570
711
  const connection = await client.acquire();
571
712
  try {
572
- yield* executeInBatches(connection, query, params, options);
713
+ yield* executeInBatchesRaw(connection, query, params, options);
573
714
  return;
574
715
  } finally {
575
716
  await client.release(connection);
@@ -578,21 +719,20 @@ async function* executeInBatches(client, query, params, options = {}) {
578
719
  const rowsPerChunk = options.rowsPerChunk && options.rowsPerChunk > 0 ? options.rowsPerChunk : 1e5;
579
720
  const values = params.length > 0 ? params.map((param) => toNodeApiValue(param)) : undefined;
580
721
  const result = await client.stream(query, values);
581
- const columns = result.deduplicatedColumnNames?.() ?? result.columnNames();
582
- const uniqueColumns = deduplicateColumns(columns);
722
+ const rawColumns = typeof result.deduplicatedColumnNames === "function" ? result.deduplicatedColumnNames() : result.columnNames();
723
+ const columns = typeof result.deduplicatedColumnNames === "function" ? rawColumns : deduplicateColumns(rawColumns);
583
724
  let buffer = [];
584
725
  for await (const chunk of result.yieldRowsJs()) {
585
- const objects = mapRowsToObjects(uniqueColumns, chunk);
586
- for (const row of objects) {
726
+ for (const row of chunk) {
587
727
  buffer.push(row);
588
728
  if (buffer.length >= rowsPerChunk) {
589
- yield buffer;
729
+ yield { columns, rows: buffer };
590
730
  buffer = [];
591
731
  }
592
732
  }
593
733
  }
594
734
  if (buffer.length > 0) {
595
- yield buffer;
735
+ yield { columns, rows: buffer };
596
736
  }
597
737
  }
598
738
  async function executeArrowOnClient(client, query, params) {
@@ -620,6 +760,13 @@ function isSavepointSyntaxError(error) {
620
760
  }
621
761
  return error.message.toLowerCase().includes("savepoint") && error.message.toLowerCase().includes("syntax error");
622
762
  }
763
+ function rewriteQuery(mode, query) {
764
+ if (mode === "never") {
765
+ return { sql: query, rewritten: false };
766
+ }
767
+ const rewritten = adaptArrayOperators(query);
768
+ return { sql: rewritten, rewritten: rewritten !== query };
769
+ }
623
770
 
624
771
  class DuckDBPreparedQuery extends PgPreparedQuery {
625
772
  client;
@@ -630,11 +777,12 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
630
777
  fields;
631
778
  _isResponseInArrayMode;
632
779
  customResultMapper;
633
- rewriteArrays;
780
+ rewriteArraysMode;
634
781
  rejectStringArrayLiterals;
782
+ prepareCache;
635
783
  warnOnStringArrayLiteral;
636
784
  static [entityKind] = "DuckDBPreparedQuery";
637
- constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rewriteArrays, rejectStringArrayLiterals, warnOnStringArrayLiteral) {
785
+ constructor(client, dialect, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, rewriteArraysMode, rejectStringArrayLiterals, prepareCache, warnOnStringArrayLiteral) {
638
786
  super({ sql: queryString, params });
639
787
  this.client = client;
640
788
  this.dialect = dialect;
@@ -644,8 +792,9 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
644
792
  this.fields = fields;
645
793
  this._isResponseInArrayMode = _isResponseInArrayMode;
646
794
  this.customResultMapper = customResultMapper;
647
- this.rewriteArrays = rewriteArrays;
795
+ this.rewriteArraysMode = rewriteArraysMode;
648
796
  this.rejectStringArrayLiterals = rejectStringArrayLiterals;
797
+ this.prepareCache = prepareCache;
649
798
  this.warnOnStringArrayLiteral = warnOnStringArrayLiteral;
650
799
  }
651
800
  async execute(placeholderValues = {}) {
@@ -654,18 +803,23 @@ class DuckDBPreparedQuery extends PgPreparedQuery {
654
803
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
655
804
  warnOnStringArrayLiteral: this.warnOnStringArrayLiteral ? () => this.warnOnStringArrayLiteral?.(this.queryString) : undefined
656
805
  });
657
- const rewrittenQuery = this.rewriteArrays ? adaptArrayOperators(this.queryString) : this.queryString;
658
- if (this.rewriteArrays && rewrittenQuery !== this.queryString) {
806
+ const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, this.queryString);
807
+ if (didRewrite) {
659
808
  this.logger.logQuery(`[duckdb] original query before array rewrite: ${this.queryString}`, params);
660
809
  }
661
810
  this.logger.logQuery(rewrittenQuery, params);
662
811
  const { fields, joinsNotNullableMap, customResultMapper } = this;
663
- const rows = await executeOnClient(this.client, rewrittenQuery, params);
664
- if (rows.length === 0 || !fields) {
665
- return rows;
812
+ if (fields) {
813
+ const { rows: rows2 } = await executeArraysOnClient(this.client, rewrittenQuery, params, { prepareCache: this.prepareCache });
814
+ if (rows2.length === 0) {
815
+ return [];
816
+ }
817
+ return customResultMapper ? customResultMapper(rows2) : rows2.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
666
818
  }
667
- const rowValues = rows.map((row) => Object.values(row));
668
- return customResultMapper ? customResultMapper(rowValues) : rowValues.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
819
+ const rows = await executeOnClient(this.client, rewrittenQuery, params, {
820
+ prepareCache: this.prepareCache
821
+ });
822
+ return rows;
669
823
  }
670
824
  all(placeholderValues = {}) {
671
825
  return this.execute(placeholderValues);
@@ -682,8 +836,9 @@ class DuckDBSession extends PgSession {
682
836
  static [entityKind] = "DuckDBSession";
683
837
  dialect;
684
838
  logger;
685
- rewriteArrays;
839
+ rewriteArraysMode;
686
840
  rejectStringArrayLiterals;
841
+ prepareCache;
687
842
  hasWarnedArrayLiteral = false;
688
843
  rollbackOnly = false;
689
844
  constructor(client, dialect, schema, options = {}) {
@@ -693,11 +848,17 @@ class DuckDBSession extends PgSession {
693
848
  this.options = options;
694
849
  this.dialect = dialect;
695
850
  this.logger = options.logger ?? new NoopLogger;
696
- this.rewriteArrays = options.rewriteArrays ?? true;
851
+ this.rewriteArraysMode = options.rewriteArrays ?? "auto";
697
852
  this.rejectStringArrayLiterals = options.rejectStringArrayLiterals ?? false;
853
+ this.prepareCache = options.prepareCache;
854
+ this.options = {
855
+ ...options,
856
+ rewriteArrays: this.rewriteArraysMode,
857
+ prepareCache: this.prepareCache
858
+ };
698
859
  }
699
860
  prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
700
- 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);
861
+ 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);
701
862
  }
702
863
  execute(query) {
703
864
  this.dialect.resetPgJsonFlag();
@@ -757,13 +918,28 @@ query: ${query}`, []);
757
918
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
758
919
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
759
920
  });
760
- const rewrittenQuery = this.rewriteArrays ? adaptArrayOperators(builtQuery.sql) : builtQuery.sql;
761
- if (this.rewriteArrays && rewrittenQuery !== builtQuery.sql) {
921
+ const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
922
+ if (didRewrite) {
762
923
  this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
763
924
  }
764
925
  this.logger.logQuery(rewrittenQuery, params);
765
926
  return executeInBatches(this.client, rewrittenQuery, params, options);
766
927
  }
928
+ executeBatchesRaw(query, options = {}) {
929
+ this.dialect.resetPgJsonFlag();
930
+ const builtQuery = this.dialect.sqlToQuery(query);
931
+ this.dialect.assertNoPgJsonColumns();
932
+ const params = prepareParams(builtQuery.params, {
933
+ rejectStringArrayLiterals: this.rejectStringArrayLiterals,
934
+ warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
935
+ });
936
+ const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
937
+ if (didRewrite) {
938
+ this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
939
+ }
940
+ this.logger.logQuery(rewrittenQuery, params);
941
+ return executeInBatchesRaw(this.client, rewrittenQuery, params, options);
942
+ }
767
943
  async executeArrow(query) {
768
944
  this.dialect.resetPgJsonFlag();
769
945
  const builtQuery = this.dialect.sqlToQuery(query);
@@ -772,8 +948,8 @@ query: ${query}`, []);
772
948
  rejectStringArrayLiterals: this.rejectStringArrayLiterals,
773
949
  warnOnStringArrayLiteral: this.rejectStringArrayLiterals ? undefined : () => this.warnOnStringArrayLiteral(builtQuery.sql)
774
950
  });
775
- const rewrittenQuery = this.rewriteArrays ? adaptArrayOperators(builtQuery.sql) : builtQuery.sql;
776
- if (this.rewriteArrays && rewrittenQuery !== builtQuery.sql) {
951
+ const { sql: rewrittenQuery, rewritten: didRewrite } = rewriteQuery(this.rewriteArraysMode, builtQuery.sql);
952
+ if (didRewrite) {
777
953
  this.logger.logQuery(`[duckdb] original query before array rewrite: ${builtQuery.sql}`, params);
778
954
  }
779
955
  this.logger.logQuery(rewrittenQuery, params);
@@ -811,6 +987,9 @@ class DuckDBTransaction extends PgTransaction {
811
987
  executeBatches(query, options = {}) {
812
988
  return this.session.executeBatches(query, options);
813
989
  }
990
+ executeBatchesRaw(query, options = {}) {
991
+ return this.session.executeBatchesRaw(query, options);
992
+ }
814
993
  executeArrow(query) {
815
994
  return this.session.executeArrow(query);
816
995
  }
@@ -1222,6 +1401,32 @@ function createDuckDBConnectionPool(instance, options = {}) {
1222
1401
  };
1223
1402
  }
1224
1403
 
1404
+ // src/options.ts
1405
+ var DEFAULT_REWRITE_ARRAYS_MODE = "auto";
1406
+ function resolveRewriteArraysOption(value) {
1407
+ if (value === undefined)
1408
+ return DEFAULT_REWRITE_ARRAYS_MODE;
1409
+ if (value === true)
1410
+ return "auto";
1411
+ if (value === false)
1412
+ return "never";
1413
+ return value;
1414
+ }
1415
+ var DEFAULT_PREPARED_CACHE_SIZE = 32;
1416
+ function resolvePrepareCacheOption(option) {
1417
+ if (!option)
1418
+ return;
1419
+ if (option === true) {
1420
+ return { size: DEFAULT_PREPARED_CACHE_SIZE };
1421
+ }
1422
+ if (typeof option === "number") {
1423
+ const size2 = Math.max(1, Math.floor(option));
1424
+ return { size: size2 };
1425
+ }
1426
+ const size = option.size ?? DEFAULT_PREPARED_CACHE_SIZE;
1427
+ return { size: Math.max(1, Math.floor(size)) };
1428
+ }
1429
+
1225
1430
  // src/driver.ts
1226
1431
  class DuckDBDriver {
1227
1432
  client;
@@ -1236,8 +1441,9 @@ class DuckDBDriver {
1236
1441
  createSession(schema) {
1237
1442
  return new DuckDBSession(this.client, this.dialect, schema, {
1238
1443
  logger: this.options.logger,
1239
- rewriteArrays: this.options.rewriteArrays,
1240
- rejectStringArrayLiterals: this.options.rejectStringArrayLiterals
1444
+ rewriteArrays: this.options.rewriteArrays ?? "auto",
1445
+ rejectStringArrayLiterals: this.options.rejectStringArrayLiterals,
1446
+ prepareCache: this.options.prepareCache
1241
1447
  });
1242
1448
  }
1243
1449
  }
@@ -1250,6 +1456,8 @@ function isConfigObject(data) {
1250
1456
  }
1251
1457
  function createFromClient(client, config = {}, instance) {
1252
1458
  const dialect = new DuckDBDialect;
1459
+ const rewriteArraysMode = resolveRewriteArraysOption(config.rewriteArrays);
1460
+ const prepareCache = resolvePrepareCacheOption(config.prepareCache);
1253
1461
  const logger = config.logger === true ? new DefaultLogger : config.logger || undefined;
1254
1462
  let schema;
1255
1463
  if (config.schema) {
@@ -1262,8 +1470,9 @@ function createFromClient(client, config = {}, instance) {
1262
1470
  }
1263
1471
  const driver = new DuckDBDriver(client, dialect, {
1264
1472
  logger,
1265
- rewriteArrays: config.rewriteArrays,
1266
- rejectStringArrayLiterals: config.rejectStringArrayLiterals
1473
+ rewriteArrays: rewriteArraysMode,
1474
+ rejectStringArrayLiterals: config.rejectStringArrayLiterals,
1475
+ prepareCache
1267
1476
  });
1268
1477
  const session = driver.createSession(schema);
1269
1478
  const db = new DuckDBDatabase(dialect, session, schema, client, instance);
@@ -1343,6 +1552,9 @@ class DuckDBDatabase extends PgDatabase {
1343
1552
  executeBatches(query, options = {}) {
1344
1553
  return this.session.executeBatches(query, options);
1345
1554
  }
1555
+ executeBatchesRaw(query, options = {}) {
1556
+ return this.session.executeBatchesRaw(query, options);
1557
+ }
1346
1558
  executeArrow(query) {
1347
1559
  return this.session.executeArrow(query);
1348
1560
  }
package/dist/helpers.mjs CHANGED
@@ -29,6 +29,15 @@ function wrapMap(data, valueType) {
29
29
  valueType
30
30
  };
31
31
  }
32
+ function wrapTimestamp(data, withTimezone, precision) {
33
+ return {
34
+ [DUCKDB_VALUE_MARKER]: true,
35
+ kind: "timestamp",
36
+ data,
37
+ withTimezone,
38
+ precision
39
+ };
40
+ }
32
41
  function wrapBlob(data) {
33
42
  return {
34
43
  [DUCKDB_VALUE_MARKER]: true,
@@ -228,6 +237,21 @@ var duckDbInterval = (name) => customType({
228
237
  return value;
229
238
  }
230
239
  })(name);
240
+ function shouldBindTimestamp(options) {
241
+ const bindMode = options.bindMode ?? "auto";
242
+ if (bindMode === "bind")
243
+ return true;
244
+ if (bindMode === "literal")
245
+ return false;
246
+ const isBun = typeof process !== "undefined" && typeof process.versions?.bun !== "undefined";
247
+ if (isBun)
248
+ return false;
249
+ const forceLiteral = typeof process !== "undefined" ? process.env.DRIZZLE_DUCKDB_FORCE_LITERAL_TIMESTAMPS : undefined;
250
+ if (forceLiteral && forceLiteral !== "0") {
251
+ return false;
252
+ }
253
+ return true;
254
+ }
231
255
  var duckDbTimestamp = (name, options = {}) => customType({
232
256
  dataType() {
233
257
  if (options.withTimezone) {
@@ -237,12 +261,19 @@ var duckDbTimestamp = (name, options = {}) => customType({
237
261
  return `TIMESTAMP${precision}`;
238
262
  },
239
263
  toDriver(value) {
264
+ if (shouldBindTimestamp(options)) {
265
+ return wrapTimestamp(value, options.withTimezone ?? false, options.precision);
266
+ }
240
267
  const iso = value instanceof Date ? value.toISOString() : value;
241
268
  const normalized = iso.replace("T", " ").replace("Z", "+00");
242
269
  const typeKeyword = options.withTimezone ? "TIMESTAMPTZ" : "TIMESTAMP";
243
270
  return sql.raw(`${typeKeyword} '${normalized}'`);
244
271
  },
245
272
  fromDriver(value) {
273
+ if (value && typeof value === "object" && "kind" in value && value.kind === "timestamp") {
274
+ const wrapped = value;
275
+ return wrapped.data instanceof Date ? wrapped.data : typeof wrapped.data === "number" || typeof wrapped.data === "bigint" ? new Date(Number(wrapped.data) / 1000) : wrapped.data;
276
+ }
246
277
  if (options.mode === "string") {
247
278
  if (value instanceof Date) {
248
279
  return value.toISOString().replace("T", " ").replace("Z", "+00");
package/dist/index.d.ts CHANGED
@@ -7,3 +7,4 @@ export * from './client.ts';
7
7
  export * from './pool.ts';
8
8
  export * from './olap.ts';
9
9
  export * from './value-wrappers.ts';
10
+ export * from './options.ts';