@leonardovida-md/drizzle-neo-duckdb 1.0.2 → 1.1.0
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 +51 -18
- package/dist/client.d.ts +19 -1
- package/dist/columns.d.ts +18 -10
- package/dist/driver.d.ts +37 -1
- package/dist/duckdb-introspect.mjs +382 -60
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.mjs +319 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.mjs +588 -72
- package/dist/introspect.d.ts +1 -0
- package/dist/olap.d.ts +46 -0
- package/dist/pool.d.ts +22 -0
- package/dist/session.d.ts +5 -0
- package/dist/sql/query-rewriters.d.ts +0 -1
- package/dist/utils.d.ts +1 -1
- package/dist/value-wrappers-core.d.ts +42 -0
- package/dist/value-wrappers.d.ts +8 -0
- package/package.json +12 -4
- package/src/bin/duckdb-introspect.ts +12 -3
- package/src/client.ts +178 -23
- package/src/columns.ts +65 -36
- package/src/dialect.ts +2 -2
- package/src/driver.ts +211 -13
- package/src/helpers.ts +18 -0
- package/src/index.ts +4 -0
- package/src/introspect.ts +39 -33
- package/src/migrator.ts +2 -4
- package/src/olap.ts +190 -0
- package/src/pool.ts +104 -0
- package/src/select-builder.ts +3 -7
- package/src/session.ts +123 -28
- package/src/sql/query-rewriters.ts +4 -54
- package/src/sql/result-mapper.ts +6 -6
- package/src/sql/selection.ts +2 -9
- package/src/utils.ts +1 -1
- package/src/value-wrappers-core.ts +156 -0
- package/src/value-wrappers.ts +155 -0
package/src/sql/selection.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Column,
|
|
3
|
-
SQL,
|
|
4
|
-
getTableName,
|
|
5
|
-
is,
|
|
6
|
-
sql,
|
|
7
|
-
} from 'drizzle-orm';
|
|
1
|
+
import { Column, SQL, getTableName, is, sql } from 'drizzle-orm';
|
|
8
2
|
import type { SelectedFields } from 'drizzle-orm/pg-core';
|
|
9
3
|
|
|
10
4
|
function mapEntries(
|
|
@@ -38,8 +32,7 @@ function mapEntries(
|
|
|
38
32
|
}
|
|
39
33
|
|
|
40
34
|
if (is(value, SQL) || is(value, Column)) {
|
|
41
|
-
const aliased =
|
|
42
|
-
is(value, SQL) ? value : sql`${value}`.mapWith(value);
|
|
35
|
+
const aliased = is(value, SQL) ? value : sql`${value}`.mapWith(value);
|
|
43
36
|
return [key, aliased.as(qualified)];
|
|
44
37
|
}
|
|
45
38
|
|
package/src/utils.ts
CHANGED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuckDB wrapper value helpers that are safe for client-side bundles.
|
|
3
|
+
* These utilities only tag values; conversion to native bindings lives
|
|
4
|
+
* in value-wrappers.ts to avoid pulling @duckdb/node-api into browsers.
|
|
5
|
+
*/
|
|
6
|
+
export const DUCKDB_VALUE_MARKER = Symbol.for('drizzle-duckdb:value');
|
|
7
|
+
|
|
8
|
+
export type DuckDBValueKind =
|
|
9
|
+
| 'list'
|
|
10
|
+
| 'array'
|
|
11
|
+
| 'struct'
|
|
12
|
+
| 'map'
|
|
13
|
+
| 'timestamp'
|
|
14
|
+
| 'blob'
|
|
15
|
+
| 'json';
|
|
16
|
+
|
|
17
|
+
export interface DuckDBValueWrapper<
|
|
18
|
+
TKind extends DuckDBValueKind = DuckDBValueKind,
|
|
19
|
+
TData = unknown,
|
|
20
|
+
> {
|
|
21
|
+
readonly [DUCKDB_VALUE_MARKER]: true;
|
|
22
|
+
readonly kind: TKind;
|
|
23
|
+
readonly data: TData;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ListValueWrapper
|
|
27
|
+
extends DuckDBValueWrapper<'list', unknown[]> {
|
|
28
|
+
readonly elementType?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ArrayValueWrapper
|
|
32
|
+
extends DuckDBValueWrapper<'array', unknown[]> {
|
|
33
|
+
readonly elementType?: string;
|
|
34
|
+
readonly fixedLength?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface StructValueWrapper
|
|
38
|
+
extends DuckDBValueWrapper<'struct', Record<string, unknown>> {
|
|
39
|
+
readonly schema?: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface MapValueWrapper
|
|
43
|
+
extends DuckDBValueWrapper<'map', Record<string, unknown>> {
|
|
44
|
+
readonly valueType?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface TimestampValueWrapper
|
|
48
|
+
extends DuckDBValueWrapper<'timestamp', Date | string> {
|
|
49
|
+
readonly withTimezone: boolean;
|
|
50
|
+
readonly precision?: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface BlobValueWrapper
|
|
54
|
+
extends DuckDBValueWrapper<'blob', Buffer | Uint8Array> {}
|
|
55
|
+
|
|
56
|
+
export interface JsonValueWrapper extends DuckDBValueWrapper<'json', unknown> {}
|
|
57
|
+
|
|
58
|
+
export type AnyDuckDBValueWrapper =
|
|
59
|
+
| ListValueWrapper
|
|
60
|
+
| ArrayValueWrapper
|
|
61
|
+
| StructValueWrapper
|
|
62
|
+
| MapValueWrapper
|
|
63
|
+
| TimestampValueWrapper
|
|
64
|
+
| BlobValueWrapper
|
|
65
|
+
| JsonValueWrapper;
|
|
66
|
+
|
|
67
|
+
export function isDuckDBWrapper(
|
|
68
|
+
value: unknown
|
|
69
|
+
): value is AnyDuckDBValueWrapper {
|
|
70
|
+
return (
|
|
71
|
+
value !== null &&
|
|
72
|
+
typeof value === 'object' &&
|
|
73
|
+
DUCKDB_VALUE_MARKER in value &&
|
|
74
|
+
(value as DuckDBValueWrapper)[DUCKDB_VALUE_MARKER] === true
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function wrapList(
|
|
79
|
+
data: unknown[],
|
|
80
|
+
elementType?: string
|
|
81
|
+
): ListValueWrapper {
|
|
82
|
+
return {
|
|
83
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
84
|
+
kind: 'list',
|
|
85
|
+
data,
|
|
86
|
+
elementType,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function wrapArray(
|
|
91
|
+
data: unknown[],
|
|
92
|
+
elementType?: string,
|
|
93
|
+
fixedLength?: number
|
|
94
|
+
): ArrayValueWrapper {
|
|
95
|
+
return {
|
|
96
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
97
|
+
kind: 'array',
|
|
98
|
+
data,
|
|
99
|
+
elementType,
|
|
100
|
+
fixedLength,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function wrapStruct(
|
|
105
|
+
data: Record<string, unknown>,
|
|
106
|
+
schema?: Record<string, string>
|
|
107
|
+
): StructValueWrapper {
|
|
108
|
+
return {
|
|
109
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
110
|
+
kind: 'struct',
|
|
111
|
+
data,
|
|
112
|
+
schema,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function wrapMap(
|
|
117
|
+
data: Record<string, unknown>,
|
|
118
|
+
valueType?: string
|
|
119
|
+
): MapValueWrapper {
|
|
120
|
+
return {
|
|
121
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
122
|
+
kind: 'map',
|
|
123
|
+
data,
|
|
124
|
+
valueType,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function wrapTimestamp(
|
|
129
|
+
data: Date | string,
|
|
130
|
+
withTimezone: boolean,
|
|
131
|
+
precision?: number
|
|
132
|
+
): TimestampValueWrapper {
|
|
133
|
+
return {
|
|
134
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
135
|
+
kind: 'timestamp',
|
|
136
|
+
data,
|
|
137
|
+
withTimezone,
|
|
138
|
+
precision,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function wrapBlob(data: Buffer | Uint8Array): BlobValueWrapper {
|
|
143
|
+
return {
|
|
144
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
145
|
+
kind: 'blob',
|
|
146
|
+
data,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function wrapJson(data: unknown): JsonValueWrapper {
|
|
151
|
+
return {
|
|
152
|
+
[DUCKDB_VALUE_MARKER]: true,
|
|
153
|
+
kind: 'json',
|
|
154
|
+
data,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listValue,
|
|
3
|
+
arrayValue,
|
|
4
|
+
structValue,
|
|
5
|
+
mapValue,
|
|
6
|
+
blobValue,
|
|
7
|
+
timestampValue,
|
|
8
|
+
timestampTZValue,
|
|
9
|
+
type DuckDBValue,
|
|
10
|
+
type DuckDBMapEntry,
|
|
11
|
+
} from '@duckdb/node-api';
|
|
12
|
+
import {
|
|
13
|
+
DUCKDB_VALUE_MARKER,
|
|
14
|
+
isDuckDBWrapper,
|
|
15
|
+
wrapArray,
|
|
16
|
+
wrapBlob,
|
|
17
|
+
wrapJson,
|
|
18
|
+
wrapList,
|
|
19
|
+
wrapMap,
|
|
20
|
+
wrapStruct,
|
|
21
|
+
wrapTimestamp,
|
|
22
|
+
type AnyDuckDBValueWrapper,
|
|
23
|
+
type DuckDBValueWrapper,
|
|
24
|
+
type ArrayValueWrapper,
|
|
25
|
+
type BlobValueWrapper,
|
|
26
|
+
type JsonValueWrapper,
|
|
27
|
+
type ListValueWrapper,
|
|
28
|
+
type MapValueWrapper,
|
|
29
|
+
type StructValueWrapper,
|
|
30
|
+
type TimestampValueWrapper,
|
|
31
|
+
type DuckDBValueKind,
|
|
32
|
+
} from './value-wrappers-core.ts';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Convert a Date or string to microseconds since Unix epoch.
|
|
36
|
+
* Handles both Date objects and ISO-like timestamp strings.
|
|
37
|
+
*/
|
|
38
|
+
function dateToMicros(value: Date | string): bigint {
|
|
39
|
+
if (value instanceof Date) {
|
|
40
|
+
return BigInt(value.getTime()) * 1000n;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// For strings, normalize the format for reliable parsing
|
|
44
|
+
// Handle both 'YYYY-MM-DD HH:MM:SS' and 'YYYY-MM-DDTHH:MM:SS' formats
|
|
45
|
+
let normalized = value;
|
|
46
|
+
if (!value.includes('T') && value.includes(' ')) {
|
|
47
|
+
// Convert 'YYYY-MM-DD HH:MM:SS' to ISO format
|
|
48
|
+
normalized = value.replace(' ', 'T');
|
|
49
|
+
}
|
|
50
|
+
// Add 'Z' suffix if no timezone offset to treat as UTC
|
|
51
|
+
if (!normalized.endsWith('Z') && !/[+-]\d{2}:?\d{2}$/.test(normalized)) {
|
|
52
|
+
normalized += 'Z';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const date = new Date(normalized);
|
|
56
|
+
if (isNaN(date.getTime())) {
|
|
57
|
+
throw new Error(`Invalid timestamp string: ${value}`);
|
|
58
|
+
}
|
|
59
|
+
return BigInt(date.getTime()) * 1000n;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Convert Buffer or Uint8Array to Uint8Array.
|
|
64
|
+
*/
|
|
65
|
+
function toUint8Array(data: Buffer | Uint8Array): Uint8Array {
|
|
66
|
+
return data instanceof Uint8Array && !(data instanceof Buffer)
|
|
67
|
+
? data
|
|
68
|
+
: new Uint8Array(data);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Convert struct entries to DuckDB struct value entries.
|
|
73
|
+
*/
|
|
74
|
+
function convertStructEntries(
|
|
75
|
+
data: Record<string, unknown>,
|
|
76
|
+
toValue: (v: unknown) => DuckDBValue
|
|
77
|
+
): Record<string, DuckDBValue> {
|
|
78
|
+
const entries: Record<string, DuckDBValue> = {};
|
|
79
|
+
for (const [key, val] of Object.entries(data)) {
|
|
80
|
+
entries[key] = toValue(val);
|
|
81
|
+
}
|
|
82
|
+
return entries;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Convert map entries to DuckDB map entry format.
|
|
87
|
+
*/
|
|
88
|
+
function convertMapEntries(
|
|
89
|
+
data: Record<string, unknown>,
|
|
90
|
+
toValue: (v: unknown) => DuckDBValue
|
|
91
|
+
): DuckDBMapEntry[] {
|
|
92
|
+
return Object.entries(data).map(([key, val]) => ({
|
|
93
|
+
key: key as DuckDBValue,
|
|
94
|
+
value: toValue(val),
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Convert a wrapper to a DuckDB Node API value.
|
|
100
|
+
* Uses exhaustive switch for compile-time safety.
|
|
101
|
+
*/
|
|
102
|
+
export function wrapperToNodeApiValue(
|
|
103
|
+
wrapper: AnyDuckDBValueWrapper,
|
|
104
|
+
toValue: (v: unknown) => DuckDBValue
|
|
105
|
+
): DuckDBValue {
|
|
106
|
+
switch (wrapper.kind) {
|
|
107
|
+
case 'list':
|
|
108
|
+
return listValue(wrapper.data.map(toValue));
|
|
109
|
+
case 'array':
|
|
110
|
+
return arrayValue(wrapper.data.map(toValue));
|
|
111
|
+
case 'struct':
|
|
112
|
+
return structValue(convertStructEntries(wrapper.data, toValue));
|
|
113
|
+
case 'map':
|
|
114
|
+
return mapValue(convertMapEntries(wrapper.data, toValue));
|
|
115
|
+
case 'timestamp':
|
|
116
|
+
return wrapper.withTimezone
|
|
117
|
+
? timestampTZValue(dateToMicros(wrapper.data))
|
|
118
|
+
: timestampValue(dateToMicros(wrapper.data));
|
|
119
|
+
case 'blob':
|
|
120
|
+
return blobValue(toUint8Array(wrapper.data));
|
|
121
|
+
case 'json':
|
|
122
|
+
// JSON is stored as VARCHAR in DuckDB - stringify at binding time
|
|
123
|
+
return JSON.stringify(wrapper.data);
|
|
124
|
+
default: {
|
|
125
|
+
// Exhaustive check - TypeScript will error if a case is missing
|
|
126
|
+
const _exhaustive: never = wrapper;
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Unknown wrapper kind: ${(_exhaustive as AnyDuckDBValueWrapper).kind}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Re-export core helpers for convenience and backward compatibility.
|
|
135
|
+
export {
|
|
136
|
+
DUCKDB_VALUE_MARKER,
|
|
137
|
+
isDuckDBWrapper,
|
|
138
|
+
wrapArray,
|
|
139
|
+
wrapBlob,
|
|
140
|
+
wrapJson,
|
|
141
|
+
wrapList,
|
|
142
|
+
wrapMap,
|
|
143
|
+
wrapStruct,
|
|
144
|
+
wrapTimestamp,
|
|
145
|
+
type AnyDuckDBValueWrapper,
|
|
146
|
+
type DuckDBValueWrapper,
|
|
147
|
+
type ArrayValueWrapper,
|
|
148
|
+
type BlobValueWrapper,
|
|
149
|
+
type JsonValueWrapper,
|
|
150
|
+
type ListValueWrapper,
|
|
151
|
+
type MapValueWrapper,
|
|
152
|
+
type StructValueWrapper,
|
|
153
|
+
type TimestampValueWrapper,
|
|
154
|
+
type DuckDBValueKind,
|
|
155
|
+
} from './value-wrappers-core.ts';
|