@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 +44 -85
- package/dist/client.d.ts +15 -1
- package/dist/columns.d.ts +3 -2
- package/dist/driver.d.ts +7 -3
- package/dist/duckdb-introspect.mjs +262 -50
- package/dist/helpers.mjs +31 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +288 -50
- package/dist/options.d.ts +10 -0
- package/dist/session.d.ts +11 -5
- package/package.json +1 -1
- package/src/client.ts +300 -40
- package/src/columns.ts +51 -4
- package/src/driver.ts +30 -5
- package/src/index.ts +1 -0
- package/src/options.ts +40 -0
- package/src/session.ts +108 -26
- package/src/sql/query-rewriters.ts +8 -0
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/)
|
|
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
|
-
|
|
118
|
+
> Tip: With connection strings (recommended), just pass the path: `const db = await drizzle(':memory:')`. Pooling is automatic.
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
## Connection Pooling
|
|
121
121
|
|
|
122
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
158
|
-
|
|
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
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
149
|
+
## Schema & Types
|
|
194
150
|
|
|
195
|
-
|
|
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
|
-
|
|
155
|
+
See the [column types](https://leonardovida.github.io/drizzle-neo-duckdb/api/columns) docs for full API.
|
|
198
156
|
|
|
199
|
-
|
|
200
|
-
import {
|
|
201
|
-
duckDbJson,
|
|
202
|
-
duckDbList,
|
|
203
|
-
} from '@leonardovida-md/drizzle-neo-duckdb/helpers';
|
|
204
|
-
```
|
|
157
|
+
## Postgres Schema Compatibility
|
|
205
158
|
|
|
206
|
-
|
|
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 |
|
|
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/
|
|
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)
|
|
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?:
|
|
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?:
|
|
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
|
}
|