@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/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  <br>
15
15
 
16
- **Drizzle DuckDB** brings [Drizzle ORM](https://orm.drizzle.team/) to [DuckDB](https://duckdb.org/) the fast in-process analytical database. Get Drizzle's type-safe query builder, automatic migrations, and full TypeScript inference while working with DuckDB's powerful analytics engine.
16
+ **Drizzle DuckDB** brings [Drizzle ORM](https://orm.drizzle.team/) to [DuckDB](https://duckdb.org/), an in-process analytical database. You get Drizzle's type-safe query builder, automatic migrations, and full TypeScript inference while working with DuckDB's analytics engine.
17
17
 
18
18
  Works with local DuckDB files, in-memory databases, and [MotherDuck](https://motherduck.com/) cloud.
19
19
 
@@ -115,95 +115,48 @@ const db = drizzle(connection, {
115
115
  });
116
116
  ```
117
117
 
118
- ## Schema Declaration
118
+ > Tip: With connection strings (recommended), just pass the path: `const db = await drizzle(':memory:')`. Pooling is automatic.
119
119
 
120
- Drizzle DuckDB uses `drizzle-orm/pg-core` for schema definitions since DuckDB's SQL is largely Postgres-compatible:
120
+ ## Connection Pooling
121
121
 
122
- ```typescript
123
- import { sql } from 'drizzle-orm';
124
- import {
125
- integer,
126
- text,
127
- boolean,
128
- timestamp,
129
- pgTable,
130
- pgSchema,
131
- } from 'drizzle-orm/pg-core';
132
-
133
- // Tables in default schema
134
- const posts = pgTable('posts', {
135
- id: integer('id').primaryKey(),
136
- title: text('title').notNull(),
137
- published: boolean('published').default(false),
138
- createdAt: timestamp('created_at').defaultNow(),
139
- });
122
+ DuckDB executes one query per connection. The async `drizzle()` entrypoints create a pool automatically (default size: 4). Options:
140
123
 
141
- // Tables in custom schema
142
- const analytics = pgSchema('analytics');
143
-
144
- const events = analytics.table('events', {
145
- id: integer('id').primaryKey(),
146
- name: text('name').notNull(),
147
- timestamp: timestamp('timestamp').defaultNow(),
148
- });
149
- ```
150
-
151
- ## DuckDB-Specific Column Types
152
-
153
- For DuckDB-specific types like `STRUCT`, `MAP`, `LIST`, and proper timestamp handling, use the custom column helpers:
124
+ - Set pool size or MotherDuck preset: `drizzle('md:', { pool: { size: 8 } })` or `pool: 'jumbo'` / `pool: 'giga'`.
125
+ - Disable pooling for single-connection workloads: `pool: false`.
126
+ - Transactions pin one pooled connection for their entire lifetime; non-transactional queries still use the pool.
127
+ - For tuning (acquire timeout, queue limits, idle/lifetime recycling), create the pool manually:
154
128
 
155
129
  ```typescript
130
+ import { DuckDBInstance } from '@duckdb/node-api';
156
131
  import {
157
- duckDbList,
158
- duckDbArray,
159
- duckDbStruct,
160
- duckDbMap,
161
- duckDbJson,
162
- duckDbTimestamp,
163
- duckDbDate,
164
- duckDbTime,
132
+ createDuckDBConnectionPool,
133
+ drizzle,
165
134
  } from '@leonardovida-md/drizzle-neo-duckdb';
166
135
 
167
- const products = pgTable('products', {
168
- id: integer('id').primaryKey(),
169
-
170
- // LIST type (variable length)
171
- tags: duckDbList('tags', 'TEXT'),
172
-
173
- // ARRAY type (fixed length)
174
- coordinates: duckDbArray('coordinates', 'DOUBLE', 3),
175
-
176
- // STRUCT type
177
- metadata: duckDbStruct('metadata', {
178
- version: 'INTEGER',
179
- author: 'TEXT',
180
- }),
181
-
182
- // MAP type
183
- attributes: duckDbMap('attributes', 'TEXT'),
184
-
185
- // JSON type (use this instead of pg json/jsonb)
186
- config: duckDbJson('config'),
187
-
188
- // Timestamp with proper DuckDB handling
189
- createdAt: duckDbTimestamp('created_at', { withTimezone: true }),
136
+ const instance = await DuckDBInstance.create('md:', {
137
+ motherduck_token: process.env.MOTHERDUCK_TOKEN,
138
+ });
139
+ const pool = createDuckDBConnectionPool(instance, {
140
+ size: 8,
141
+ acquireTimeout: 20_000,
142
+ maxWaitingRequests: 200,
143
+ maxLifetimeMs: 10 * 60_000,
144
+ idleTimeoutMs: 60_000,
190
145
  });
146
+ const db = drizzle(pool);
191
147
  ```
192
148
 
193
- See [Column Types Documentation](https://leonardovida.github.io/drizzle-neo-duckdb/api/columns) for complete reference.
149
+ ## Schema & Types
194
150
 
195
- ### Client-Safe Imports (schemas, drizzle-zod, trpc)
151
+ - Use `drizzle-orm/pg-core` for schemas; DuckDB SQL is largely Postgres-compatible.
152
+ - DuckDB-specific helpers: `duckDbList`, `duckDbArray`, `duckDbStruct`, `duckDbMap`, `duckDbJson`, `duckDbTimestamp`, `duckDbDate`, `duckDbTime`.
153
+ - Browser-safe imports live under `@leonardovida-md/drizzle-neo-duckdb/helpers` (introspection emits this path).
196
154
 
197
- When generated schemas are consumed in client bundles, import the helpers from the browser-safe subpath to avoid pulling the native DuckDB bindings:
155
+ See the [column types](https://leonardovida.github.io/drizzle-neo-duckdb/api/columns) docs for full API.
198
156
 
199
- ```typescript
200
- import {
201
- duckDbJson,
202
- duckDbList,
203
- } from '@leonardovida-md/drizzle-neo-duckdb/helpers';
204
- ```
157
+ ## Postgres Schema Compatibility
205
158
 
206
- The introspection CLI now emits this import path by default. You can still override `importBasePath` if you need the old root import.
159
+ Use `pgTable`, `pgSchema`, and other `drizzle-orm/pg-core` builders as you do with Postgres. The dialect keeps table definitions and relations intact while adapting queries to DuckDB.
207
160
 
208
161
  ## Querying
209
162
 
@@ -324,6 +277,9 @@ const db = drizzle(connection, {
324
277
  // Enable query logging
325
278
  logger: new DefaultLogger(),
326
279
 
280
+ // Pool size/preset when using connection strings (default: 4). Set false to disable.
281
+ pool: { size: 8 },
282
+
327
283
  // Rewrite Postgres array operators to DuckDB functions (default: true)
328
284
  rewriteArrays: true,
329
285
 
@@ -339,25 +295,28 @@ const db = drizzle(connection, {
339
295
 
340
296
  This connector aims for compatibility with Drizzle's Postgres driver but has some differences:
341
297
 
342
- | Feature | Status |
343
- | --------------------- | ---------------------------------------------------------- |
344
- | Basic CRUD operations | Full support |
345
- | Joins and subqueries | Full support |
346
- | Transactions | No savepoints (nested transactions reuse outer) |
347
- | JSON/JSONB columns | Use `duckDbJson()` instead |
348
- | Prepared statements | No statement caching |
349
- | Streaming results | Materialized by default; use `executeBatches()` for chunks |
298
+ | Feature | Status |
299
+ | --------------------- | ---------------------------------------------------------------------------- |
300
+ | Basic CRUD operations | Full support |
301
+ | Joins and subqueries | Full support |
302
+ | Transactions | No savepoints (nested transactions reuse outer) |
303
+ | JSON/JSONB columns | Use `duckDbJson()` instead |
304
+ | Prepared statements | No statement caching |
305
+ | Streaming results | Chunked reads via `executeBatches()` / `executeArrow()`; no cursor streaming |
306
+ | Concurrent queries | One query per connection; use pooling for parallelism |
350
307
 
351
- See [Limitations Documentation](https://leonardovida.github.io/drizzle-neo-duckdb/guide/limitations) for details.
308
+ See [Limitations Documentation](https://leonardovida.github.io/drizzle-neo-duckdb/reference/limitations) for details.
352
309
 
353
310
  ## Examples
354
311
 
355
- - **[MotherDuck NYC Taxi](./example/motherduck-nyc.ts)** Query the built-in NYC taxi dataset from MotherDuck cloud
312
+ - **[MotherDuck NYC Taxi](./example/motherduck-nyc.ts)**: Query the built-in NYC taxi dataset from MotherDuck cloud
313
+ - **[Analytics Dashboard](./example/analytics-dashboard.ts)**: Local in-memory analytics with DuckDB types and Parquet loading
356
314
 
357
315
  Run examples:
358
316
 
359
317
  ```bash
360
318
  MOTHERDUCK_TOKEN=your_token bun example/motherduck-nyc.ts
319
+ bun example/analytics-dashboard.ts
361
320
  ```
362
321
 
363
322
  ## Contributing
package/dist/client.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type DuckDBConnection } from '@duckdb/node-api';
2
+ import type { PreparedStatementCacheConfig } from './options.ts';
2
3
  export type DuckDBClientLike = DuckDBConnection | DuckDBConnectionPool;
3
4
  export type RowData = Record<string, unknown>;
4
5
  export interface DuckDBConnectionPool {
@@ -7,20 +8,33 @@ export interface DuckDBConnectionPool {
7
8
  close?(): Promise<void> | void;
8
9
  }
9
10
  export declare function isPool(client: DuckDBClientLike): client is DuckDBConnectionPool;
11
+ export interface ExecuteClientOptions {
12
+ prepareCache?: PreparedStatementCacheConfig;
13
+ }
14
+ export type ExecuteArraysResult = {
15
+ columns: string[];
16
+ rows: unknown[][];
17
+ };
10
18
  export interface PrepareParamsOptions {
11
19
  rejectStringArrayLiterals?: boolean;
12
20
  warnOnStringArrayLiteral?: () => void;
13
21
  }
14
22
  export declare function prepareParams(params: unknown[], options?: PrepareParamsOptions): unknown[];
15
23
  export declare function closeClientConnection(connection: DuckDBConnection): Promise<void>;
16
- export declare function executeOnClient(client: DuckDBClientLike, query: string, params: unknown[]): Promise<RowData[]>;
24
+ export declare function executeOnClient(client: DuckDBClientLike, query: string, params: unknown[], options?: ExecuteClientOptions): Promise<RowData[]>;
25
+ export declare function executeArraysOnClient(client: DuckDBClientLike, query: string, params: unknown[], options?: ExecuteClientOptions): Promise<ExecuteArraysResult>;
17
26
  export interface ExecuteInBatchesOptions {
18
27
  rowsPerChunk?: number;
19
28
  }
29
+ export interface ExecuteBatchesRawChunk {
30
+ columns: string[];
31
+ rows: unknown[][];
32
+ }
20
33
  /**
21
34
  * Stream results from DuckDB in batches to avoid fully materializing rows in JS.
22
35
  */
23
36
  export declare function executeInBatches(client: DuckDBClientLike, query: string, params: unknown[], options?: ExecuteInBatchesOptions): AsyncGenerator<RowData[], void, void>;
37
+ export declare function executeInBatchesRaw(client: DuckDBClientLike, query: string, params: unknown[], options?: ExecuteInBatchesOptions): AsyncGenerator<ExecuteBatchesRawChunk, void, void>;
24
38
  /**
25
39
  * Return columnar results when the underlying node-api exposes an Arrow/columnar API.
26
40
  * Falls back to column-major JS arrays when Arrow is unavailable.
package/dist/columns.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type SQL } from 'drizzle-orm';
2
2
  import type { SQLWrapper } from 'drizzle-orm/sql/sql';
3
- import { type ListValueWrapper, type ArrayValueWrapper, type MapValueWrapper, type BlobValueWrapper, type JsonValueWrapper } from './value-wrappers-core.ts';
3
+ import { type ListValueWrapper, type ArrayValueWrapper, type MapValueWrapper, type BlobValueWrapper, type JsonValueWrapper, type TimestampValueWrapper } from './value-wrappers-core.ts';
4
4
  type IntColType = 'SMALLINT' | 'INTEGER' | 'BIGINT' | 'HUGEINT' | 'USMALLINT' | 'UINTEGER' | 'UBIGINT' | 'UHUGEINT' | 'INT' | 'INT16' | 'INT32' | 'INT64' | 'INT128' | 'LONG' | 'VARINT';
5
5
  type FloatColType = 'FLOAT' | 'DOUBLE';
6
6
  type StringColType = 'STRING' | 'VARCHAR' | 'TEXT';
@@ -111,13 +111,14 @@ interface TimestampOptions {
111
111
  withTimezone?: boolean;
112
112
  mode?: TimestampMode;
113
113
  precision?: number;
114
+ bindMode?: 'auto' | 'bind' | 'literal';
114
115
  }
115
116
  export declare const duckDbTimestamp: (name: string, options?: TimestampOptions) => import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
116
117
  name: string;
117
118
  dataType: "custom";
118
119
  columnType: "PgCustomColumn";
119
120
  data: string | Date;
120
- driverParam: string | Date | SQL<unknown>;
121
+ driverParam: string | TimestampValueWrapper | Date | SQL<unknown>;
121
122
  enumValues: undefined;
122
123
  }>;
123
124
  export declare const duckDbDate: (name: string) => import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
package/dist/driver.d.ts CHANGED
@@ -10,12 +10,14 @@ import type { DuckDBClientLike, DuckDBQueryResultHKT, DuckDBTransaction } from '
10
10
  import { DuckDBSession } from './session.ts';
11
11
  import { DuckDBDialect } from './dialect.ts';
12
12
  import { DuckDBSelectBuilder } from './select-builder.ts';
13
- import type { ExecuteInBatchesOptions, RowData } from './client.ts';
13
+ import type { ExecuteBatchesRawChunk, ExecuteInBatchesOptions, RowData } from './client.ts';
14
14
  import { type DuckDBPoolConfig, type PoolPreset } from './pool.ts';
15
+ import { type PreparedStatementCacheConfig, type PrepareCacheOption, type RewriteArraysMode, type RewriteArraysOption } from './options.ts';
15
16
  export interface PgDriverOptions {
16
17
  logger?: Logger;
17
- rewriteArrays?: boolean;
18
+ rewriteArrays?: RewriteArraysMode;
18
19
  rejectStringArrayLiterals?: boolean;
20
+ prepareCache?: PreparedStatementCacheConfig;
19
21
  }
20
22
  export declare class DuckDBDriver {
21
23
  private client;
@@ -33,8 +35,9 @@ export interface DuckDBConnectionConfig {
33
35
  options?: Record<string, string>;
34
36
  }
35
37
  export interface DuckDBDrizzleConfig<TSchema extends Record<string, unknown> = Record<string, never>> extends DrizzleConfig<TSchema> {
36
- rewriteArrays?: boolean;
38
+ rewriteArrays?: RewriteArraysOption;
37
39
  rejectStringArrayLiterals?: boolean;
40
+ prepareCache?: PrepareCacheOption;
38
41
  /** Pool configuration. Use preset name, size config, or false to disable. */
39
42
  pool?: DuckDBPoolConfig | PoolPreset | false;
40
43
  }
@@ -68,6 +71,7 @@ export declare class DuckDBDatabase<TFullSchema extends Record<string, unknown>
68
71
  select(): DuckDBSelectBuilder<undefined>;
69
72
  select<TSelection extends SelectedFields>(fields: TSelection): DuckDBSelectBuilder<TSelection>;
70
73
  executeBatches<T extends RowData = RowData>(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<T[], void, void>;
74
+ executeBatchesRaw(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<ExecuteBatchesRawChunk, void, void>;
71
75
  executeArrow(query: SQL): Promise<unknown>;
72
76
  transaction<T>(transaction: (tx: DuckDBTransaction<TFullSchema, TSchema>) => Promise<T>): Promise<T>;
73
77
  }