@leonardovida-md/drizzle-neo-duckdb 1.1.0 → 1.1.1
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/columns.d.ts +5 -0
- package/dist/dialect.d.ts +21 -0
- package/dist/duckdb-introspect.mjs +414 -92
- package/dist/helpers.mjs +4 -4
- package/dist/index.mjs +416 -96
- package/dist/introspect.d.ts +8 -0
- package/dist/pool.d.ts +8 -0
- package/dist/session.d.ts +7 -1
- package/dist/sql/query-rewriters.d.ts +1 -0
- package/dist/sql/result-mapper.d.ts +7 -0
- package/dist/value-wrappers-core.d.ts +2 -2
- package/package.json +1 -1
- package/src/bin/duckdb-introspect.ts +27 -0
- package/src/client.ts +13 -10
- package/src/columns.ts +9 -9
- package/src/dialect.ts +51 -3
- package/src/driver.ts +15 -2
- package/src/introspect.ts +23 -6
- package/src/pool.ts +182 -12
- package/src/session.ts +98 -5
- package/src/sql/query-rewriters.ts +183 -75
- package/src/sql/result-mapper.ts +7 -7
- package/src/value-wrappers-core.ts +2 -2
- package/src/value-wrappers.ts +13 -3
package/dist/introspect.d.ts
CHANGED
|
@@ -63,4 +63,12 @@ export interface IntrospectResult {
|
|
|
63
63
|
}
|
|
64
64
|
export declare const DEFAULT_IMPORT_BASE = "@leonardovida-md/drizzle-neo-duckdb/helpers";
|
|
65
65
|
export declare function introspect(db: DuckDBDatabase, opts?: IntrospectOptions): Promise<IntrospectResult>;
|
|
66
|
+
export declare function buildDefault(defaultValue: string | null): string;
|
|
67
|
+
export declare function parseStructFields(inner: string): Array<{
|
|
68
|
+
name: string;
|
|
69
|
+
type: string;
|
|
70
|
+
}>;
|
|
71
|
+
export declare function parseMapValue(raw: string): string;
|
|
72
|
+
export declare function splitTopLevel(input: string, delimiter: string): string[];
|
|
73
|
+
export declare function toIdentifier(name: string): string;
|
|
66
74
|
export {};
|
package/dist/pool.d.ts
CHANGED
|
@@ -16,6 +16,14 @@ export declare function resolvePoolSize(pool: DuckDBPoolConfig | PoolPreset | fa
|
|
|
16
16
|
export interface DuckDBConnectionPoolOptions {
|
|
17
17
|
/** Maximum concurrent connections. Defaults to 4. */
|
|
18
18
|
size?: number;
|
|
19
|
+
/** Timeout in milliseconds to wait for a connection. Defaults to 30000 (30s). */
|
|
20
|
+
acquireTimeout?: number;
|
|
21
|
+
/** Maximum number of requests waiting for a connection. Defaults to 100. */
|
|
22
|
+
maxWaitingRequests?: number;
|
|
23
|
+
/** Max time (ms) a connection may live before being recycled. */
|
|
24
|
+
maxLifetimeMs?: number;
|
|
25
|
+
/** Max idle time (ms) before an idle connection is discarded. */
|
|
26
|
+
idleTimeoutMs?: number;
|
|
19
27
|
}
|
|
20
28
|
export declare function createDuckDBConnectionPool(instance: DuckDBInstance, options?: DuckDBConnectionPoolOptions): DuckDBConnectionPool & {
|
|
21
29
|
size: number;
|
package/dist/session.d.ts
CHANGED
|
@@ -44,12 +44,17 @@ export declare class DuckDBSession<TFullSchema extends Record<string, unknown> =
|
|
|
44
44
|
private rewriteArrays;
|
|
45
45
|
private rejectStringArrayLiterals;
|
|
46
46
|
private hasWarnedArrayLiteral;
|
|
47
|
+
private rollbackOnly;
|
|
47
48
|
constructor(client: DuckDBClientLike, dialect: DuckDBDialect, schema: RelationalSchemaConfig<TSchema> | undefined, options?: DuckDBSessionOptions);
|
|
48
49
|
prepareQuery<T extends PreparedQueryConfig = PreparedQueryConfig>(query: Query, fields: SelectedFieldsOrdered | undefined, name: string | undefined, isResponseInArrayMode: boolean, customResultMapper?: (rows: unknown[][]) => T['execute']): PgPreparedQuery<T>;
|
|
49
|
-
|
|
50
|
+
execute<T>(query: SQL): Promise<T>;
|
|
51
|
+
all<T = unknown>(query: SQL): Promise<T[]>;
|
|
52
|
+
transaction<T>(transaction: (tx: DuckDBTransaction<TFullSchema, TSchema>) => Promise<T>, config?: PgTransactionConfig): Promise<T>;
|
|
50
53
|
private warnOnStringArrayLiteral;
|
|
51
54
|
executeBatches<T extends RowData = RowData>(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<GenericRowData<T>[], void, void>;
|
|
52
55
|
executeArrow(query: SQL): Promise<unknown>;
|
|
56
|
+
markRollbackOnly(): void;
|
|
57
|
+
isRollbackOnly(): boolean;
|
|
53
58
|
}
|
|
54
59
|
export declare class DuckDBTransaction<TFullSchema extends Record<string, unknown>, TSchema extends TablesRelationalConfig> extends PgTransaction<DuckDBQueryResultHKT, TFullSchema, TSchema> {
|
|
55
60
|
static readonly [entityKind]: string;
|
|
@@ -59,6 +64,7 @@ export declare class DuckDBTransaction<TFullSchema extends Record<string, unknow
|
|
|
59
64
|
executeBatches<T extends RowData = RowData>(query: SQL, options?: ExecuteInBatchesOptions): AsyncGenerator<GenericRowData<T>[], void, void>;
|
|
60
65
|
executeArrow(query: SQL): Promise<unknown>;
|
|
61
66
|
transaction<T>(transaction: (tx: DuckDBTransaction<TFullSchema, TSchema>) => Promise<T>): Promise<T>;
|
|
67
|
+
private runNestedWithoutSavepoint;
|
|
62
68
|
}
|
|
63
69
|
export type GenericRowData<T extends RowData = RowData> = T;
|
|
64
70
|
export type GenericTableData<T = RowData> = T[];
|
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
import { type AnyColumn, type SelectedFieldsOrdered } from 'drizzle-orm';
|
|
2
|
+
export declare function normalizeInet(value: unknown): unknown;
|
|
3
|
+
export declare function normalizeTimestampString(value: unknown, withTimezone: boolean): string | unknown;
|
|
4
|
+
export declare function normalizeTimestamp(value: unknown, withTimezone: boolean): Date | unknown;
|
|
5
|
+
export declare function normalizeDateString(value: unknown): string | unknown;
|
|
6
|
+
export declare function normalizeDateValue(value: unknown): Date | unknown;
|
|
7
|
+
export declare function normalizeTime(value: unknown): string | unknown;
|
|
8
|
+
export declare function normalizeInterval(value: unknown): string | unknown;
|
|
2
9
|
export declare function mapResultRow<TResult>(columns: SelectedFieldsOrdered<AnyColumn>, row: unknown[], joinsNotNullableMap: Record<string, boolean> | undefined): TResult;
|
|
@@ -23,7 +23,7 @@ export interface StructValueWrapper extends DuckDBValueWrapper<'struct', Record<
|
|
|
23
23
|
export interface MapValueWrapper extends DuckDBValueWrapper<'map', Record<string, unknown>> {
|
|
24
24
|
readonly valueType?: string;
|
|
25
25
|
}
|
|
26
|
-
export interface TimestampValueWrapper extends DuckDBValueWrapper<'timestamp', Date | string> {
|
|
26
|
+
export interface TimestampValueWrapper extends DuckDBValueWrapper<'timestamp', Date | string | number | bigint> {
|
|
27
27
|
readonly withTimezone: boolean;
|
|
28
28
|
readonly precision?: number;
|
|
29
29
|
}
|
|
@@ -37,6 +37,6 @@ export declare function wrapList(data: unknown[], elementType?: string): ListVal
|
|
|
37
37
|
export declare function wrapArray(data: unknown[], elementType?: string, fixedLength?: number): ArrayValueWrapper;
|
|
38
38
|
export declare function wrapStruct(data: Record<string, unknown>, schema?: Record<string, string>): StructValueWrapper;
|
|
39
39
|
export declare function wrapMap(data: Record<string, unknown>, valueType?: string): MapValueWrapper;
|
|
40
|
-
export declare function wrapTimestamp(data: Date | string, withTimezone: boolean, precision?: number): TimestampValueWrapper;
|
|
40
|
+
export declare function wrapTimestamp(data: Date | string | number | bigint, withTimezone: boolean, precision?: number): TimestampValueWrapper;
|
|
41
41
|
export declare function wrapBlob(data: Buffer | Uint8Array): BlobValueWrapper;
|
|
42
42
|
export declare function wrapJson(data: unknown): JsonValueWrapper;
|
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.
|
|
6
|
+
"version": "1.1.1",
|
|
7
7
|
"description": "A drizzle ORM client for use with DuckDB. Based on drizzle's Postgres client.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|
|
@@ -12,6 +12,7 @@ interface CliOptions {
|
|
|
12
12
|
allDatabases: boolean;
|
|
13
13
|
schemas?: string[];
|
|
14
14
|
outFile: string;
|
|
15
|
+
outMeta?: string;
|
|
15
16
|
includeViews: boolean;
|
|
16
17
|
useCustomTimeTypes: boolean;
|
|
17
18
|
importBasePath?: string;
|
|
@@ -20,6 +21,7 @@ interface CliOptions {
|
|
|
20
21
|
function parseArgs(argv: string[]): CliOptions {
|
|
21
22
|
const options: CliOptions = {
|
|
22
23
|
outFile: path.resolve(process.cwd(), 'drizzle/schema.ts'),
|
|
24
|
+
outMeta: undefined,
|
|
23
25
|
allDatabases: false,
|
|
24
26
|
includeViews: false,
|
|
25
27
|
useCustomTimeTypes: true,
|
|
@@ -52,6 +54,14 @@ function parseArgs(argv: string[]): CliOptions {
|
|
|
52
54
|
argv[++i] ?? 'drizzle/schema.ts'
|
|
53
55
|
);
|
|
54
56
|
break;
|
|
57
|
+
case '--out-json':
|
|
58
|
+
case '--outJson':
|
|
59
|
+
case '--json':
|
|
60
|
+
options.outMeta = path.resolve(
|
|
61
|
+
process.cwd(),
|
|
62
|
+
argv[++i] ?? 'drizzle/schema.meta.json'
|
|
63
|
+
);
|
|
64
|
+
break;
|
|
55
65
|
case '--include-views':
|
|
56
66
|
case '--includeViews':
|
|
57
67
|
options.includeViews = true;
|
|
@@ -88,6 +98,7 @@ Options:
|
|
|
88
98
|
--all-databases Introspect all attached databases (not just current)
|
|
89
99
|
--schema Comma separated schema list (defaults to all non-system schemas)
|
|
90
100
|
--out Output file (default: ./drizzle/schema.ts)
|
|
101
|
+
--json Optional JSON metadata output (default: ./drizzle/schema.meta.json)
|
|
91
102
|
--include-views Include views in the generated schema
|
|
92
103
|
--use-pg-time Use pg-core timestamp/date/time instead of DuckDB custom helpers
|
|
93
104
|
--import-base Override import path for duckdb helpers (default: package name)
|
|
@@ -136,8 +147,19 @@ async function main() {
|
|
|
136
147
|
|
|
137
148
|
await mkdir(path.dirname(options.outFile), { recursive: true });
|
|
138
149
|
await writeFile(options.outFile, result.files.schemaTs, 'utf8');
|
|
150
|
+
if (options.outMeta) {
|
|
151
|
+
await mkdir(path.dirname(options.outMeta), { recursive: true });
|
|
152
|
+
await writeFile(
|
|
153
|
+
options.outMeta,
|
|
154
|
+
JSON.stringify(result.files.metaJson, null, 2),
|
|
155
|
+
'utf8'
|
|
156
|
+
);
|
|
157
|
+
}
|
|
139
158
|
|
|
140
159
|
console.log(`Wrote schema to ${options.outFile}`);
|
|
160
|
+
if (options.outMeta) {
|
|
161
|
+
console.log(`Wrote metadata to ${options.outMeta}`);
|
|
162
|
+
}
|
|
141
163
|
} finally {
|
|
142
164
|
if (
|
|
143
165
|
'closeSync' in connection &&
|
|
@@ -145,6 +167,11 @@ async function main() {
|
|
|
145
167
|
) {
|
|
146
168
|
connection.closeSync();
|
|
147
169
|
}
|
|
170
|
+
if ('closeSync' in instance && typeof instance.closeSync === 'function') {
|
|
171
|
+
instance.closeSync();
|
|
172
|
+
} else if ('close' in instance && typeof instance.close === 'function') {
|
|
173
|
+
await instance.close();
|
|
174
|
+
}
|
|
148
175
|
}
|
|
149
176
|
}
|
|
150
177
|
|
package/src/client.ts
CHANGED
|
@@ -49,17 +49,20 @@ export function prepareParams(
|
|
|
49
49
|
options: PrepareParamsOptions = {}
|
|
50
50
|
): unknown[] {
|
|
51
51
|
return params.map((param) => {
|
|
52
|
-
if (typeof param === 'string'
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
if (typeof param === 'string') {
|
|
53
|
+
const trimmed = param.trim();
|
|
54
|
+
if (trimmed && isPgArrayLiteral(trimmed)) {
|
|
55
|
+
if (options.rejectStringArrayLiterals) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
'Stringified array literals are not supported. Use duckDbList()/duckDbArray() or pass native arrays.'
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (options.warnOnStringArrayLiteral) {
|
|
62
|
+
options.warnOnStringArrayLiteral();
|
|
63
|
+
}
|
|
64
|
+
return parsePgArrayLiteral(trimmed);
|
|
57
65
|
}
|
|
58
|
-
|
|
59
|
-
if (options.warnOnStringArrayLiteral) {
|
|
60
|
-
options.warnOnStringArrayLiteral();
|
|
61
|
-
}
|
|
62
|
-
return parsePgArrayLiteral(param);
|
|
63
66
|
}
|
|
64
67
|
return param;
|
|
65
68
|
});
|
package/src/columns.ts
CHANGED
|
@@ -63,7 +63,7 @@ type StructColType = `STRUCT (${string})`;
|
|
|
63
63
|
|
|
64
64
|
type Primitive = AnyColType | ListColType | ArrayColType | StructColType;
|
|
65
65
|
|
|
66
|
-
function coerceArrayString(value: string): unknown[] | undefined {
|
|
66
|
+
export function coerceArrayString(value: string): unknown[] | undefined {
|
|
67
67
|
const trimmed = value.trim();
|
|
68
68
|
if (!trimmed) {
|
|
69
69
|
return [];
|
|
@@ -86,7 +86,7 @@ function coerceArrayString(value: string): unknown[] | undefined {
|
|
|
86
86
|
return undefined;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
function formatLiteral(value: unknown, typeHint?: string): string {
|
|
89
|
+
export function formatLiteral(value: unknown, typeHint?: string): string {
|
|
90
90
|
if (value === null || value === undefined) {
|
|
91
91
|
return 'NULL';
|
|
92
92
|
}
|
|
@@ -123,7 +123,7 @@ function formatLiteral(value: unknown, typeHint?: string): string {
|
|
|
123
123
|
return `'${escaped}'`;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
function buildListLiteral(values: unknown[], elementType?: string): SQL {
|
|
126
|
+
export function buildListLiteral(values: unknown[], elementType?: string): SQL {
|
|
127
127
|
if (values.length === 0) {
|
|
128
128
|
return sql`[]`;
|
|
129
129
|
}
|
|
@@ -135,7 +135,7 @@ function buildListLiteral(values: unknown[], elementType?: string): SQL {
|
|
|
135
135
|
return sql`list_value(${sql.join(chunks, sql.raw(', '))})`;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
function buildStructLiteral(
|
|
138
|
+
export function buildStructLiteral(
|
|
139
139
|
value: Record<string, unknown>,
|
|
140
140
|
schema?: Record<string, Primitive>
|
|
141
141
|
): SQL {
|
|
@@ -154,7 +154,7 @@ function buildStructLiteral(
|
|
|
154
154
|
return sql`struct_pack(${sql.join(parts, sql.raw(', '))})`;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
function buildMapLiteral(
|
|
157
|
+
export function buildMapLiteral(
|
|
158
158
|
value: Record<string, unknown>,
|
|
159
159
|
valueType?: string
|
|
160
160
|
): SQL {
|
|
@@ -188,11 +188,11 @@ export const duckDbList = <TData = unknown>(
|
|
|
188
188
|
}
|
|
189
189
|
if (typeof value === 'string') {
|
|
190
190
|
const parsed = coerceArrayString(value);
|
|
191
|
-
if (parsed) {
|
|
191
|
+
if (parsed !== undefined) {
|
|
192
192
|
return parsed as TData[];
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
|
-
return
|
|
195
|
+
return value as unknown as TData[];
|
|
196
196
|
},
|
|
197
197
|
})(name);
|
|
198
198
|
|
|
@@ -219,11 +219,11 @@ export const duckDbArray = <TData = unknown>(
|
|
|
219
219
|
}
|
|
220
220
|
if (typeof value === 'string') {
|
|
221
221
|
const parsed = coerceArrayString(value);
|
|
222
|
-
if (parsed) {
|
|
222
|
+
if (parsed !== undefined) {
|
|
223
223
|
return parsed as TData[];
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
|
-
return
|
|
226
|
+
return value as unknown as TData[];
|
|
227
227
|
},
|
|
228
228
|
})(name);
|
|
229
229
|
|
package/src/dialect.ts
CHANGED
|
@@ -19,18 +19,64 @@ import {
|
|
|
19
19
|
type QueryTypingsValue,
|
|
20
20
|
} from 'drizzle-orm';
|
|
21
21
|
|
|
22
|
+
const enum SavepointSupport {
|
|
23
|
+
Unknown = 0,
|
|
24
|
+
Yes = 1,
|
|
25
|
+
No = 2,
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
export class DuckDBDialect extends PgDialect {
|
|
23
29
|
static readonly [entityKind]: string = 'DuckDBPgDialect';
|
|
30
|
+
// Track if PG JSON columns were detected during the current query preparation.
|
|
31
|
+
// Reset before each query via DuckDBSession to keep detection per-query.
|
|
24
32
|
private hasPgJsonColumn = false;
|
|
33
|
+
// Track savepoint support per-dialect instance to avoid cross-contamination
|
|
34
|
+
// when multiple database connections with different capabilities exist.
|
|
35
|
+
private savepointsSupported: SavepointSupport = SavepointSupport.Unknown;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Reset the PG JSON detection flag. Should be called before preparing a new query.
|
|
39
|
+
*/
|
|
40
|
+
resetPgJsonFlag(): void {
|
|
41
|
+
this.hasPgJsonColumn = false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Mark that a PG JSON/JSONB column was detected during query preparation.
|
|
46
|
+
*/
|
|
47
|
+
markPgJsonDetected(): void {
|
|
48
|
+
this.hasPgJsonColumn = true;
|
|
49
|
+
}
|
|
25
50
|
|
|
26
51
|
assertNoPgJsonColumns(): void {
|
|
27
52
|
if (this.hasPgJsonColumn) {
|
|
28
53
|
throw new Error(
|
|
29
|
-
|
|
54
|
+
"Pg JSON/JSONB columns are not supported in DuckDB. Replace them with duckDbJson() to use DuckDB's native JSON type."
|
|
30
55
|
);
|
|
31
56
|
}
|
|
32
57
|
}
|
|
33
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Check if savepoints are known to be unsupported for this dialect instance.
|
|
61
|
+
*/
|
|
62
|
+
areSavepointsUnsupported(): boolean {
|
|
63
|
+
return this.savepointsSupported === SavepointSupport.No;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Mark that savepoints are supported for this dialect instance.
|
|
68
|
+
*/
|
|
69
|
+
markSavepointsSupported(): void {
|
|
70
|
+
this.savepointsSupported = SavepointSupport.Yes;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Mark that savepoints are not supported for this dialect instance.
|
|
75
|
+
*/
|
|
76
|
+
markSavepointsUnsupported(): void {
|
|
77
|
+
this.savepointsSupported = SavepointSupport.No;
|
|
78
|
+
}
|
|
79
|
+
|
|
34
80
|
override async migrate(
|
|
35
81
|
migrations: MigrationMeta[],
|
|
36
82
|
session: PgSession,
|
|
@@ -117,8 +163,10 @@ export class DuckDBDialect extends PgDialect {
|
|
|
117
163
|
encoder: DriverValueEncoder<unknown, unknown>
|
|
118
164
|
): QueryTypingsValue {
|
|
119
165
|
if (is(encoder, PgJsonb) || is(encoder, PgJson)) {
|
|
120
|
-
this.
|
|
121
|
-
|
|
166
|
+
this.markPgJsonDetected();
|
|
167
|
+
throw new Error(
|
|
168
|
+
"Pg JSON/JSONB columns are not supported in DuckDB. Replace them with duckDbJson() to use DuckDB's native JSON type."
|
|
169
|
+
);
|
|
122
170
|
} else if (is(encoder, PgNumeric)) {
|
|
123
171
|
return 'decimal';
|
|
124
172
|
} else if (is(encoder, PgTime)) {
|
package/src/driver.ts
CHANGED
|
@@ -24,7 +24,7 @@ import { DuckDBDialect } from './dialect.ts';
|
|
|
24
24
|
import { DuckDBSelectBuilder } from './select-builder.ts';
|
|
25
25
|
import { aliasFields } from './sql/selection.ts';
|
|
26
26
|
import type { ExecuteInBatchesOptions, RowData } from './client.ts';
|
|
27
|
-
import { isPool } from './client.ts';
|
|
27
|
+
import { closeClientConnection, isPool } from './client.ts';
|
|
28
28
|
import {
|
|
29
29
|
createDuckDBConnectionPool,
|
|
30
30
|
resolvePoolSize,
|
|
@@ -290,7 +290,20 @@ export class DuckDBDatabase<
|
|
|
290
290
|
if (isPool(this.$client) && this.$client.close) {
|
|
291
291
|
await this.$client.close();
|
|
292
292
|
}
|
|
293
|
-
|
|
293
|
+
if (!isPool(this.$client)) {
|
|
294
|
+
await closeClientConnection(this.$client);
|
|
295
|
+
}
|
|
296
|
+
if (this.$instance) {
|
|
297
|
+
const maybeClosable = this.$instance as unknown as {
|
|
298
|
+
close?: () => Promise<void> | void;
|
|
299
|
+
closeSync?: () => void;
|
|
300
|
+
};
|
|
301
|
+
if (typeof maybeClosable.close === 'function') {
|
|
302
|
+
await maybeClosable.close();
|
|
303
|
+
} else if (typeof maybeClosable.closeSync === 'function') {
|
|
304
|
+
maybeClosable.closeSync();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
294
307
|
}
|
|
295
308
|
|
|
296
309
|
select(): DuckDBSelectBuilder<undefined>;
|
package/src/introspect.ts
CHANGED
|
@@ -535,7 +535,7 @@ function emitColumn(
|
|
|
535
535
|
return builder;
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
-
function buildDefault(defaultValue: string | null): string {
|
|
538
|
+
export function buildDefault(defaultValue: string | null): string {
|
|
539
539
|
if (!defaultValue) {
|
|
540
540
|
return '';
|
|
541
541
|
}
|
|
@@ -707,6 +707,22 @@ function mapDuckDbType(
|
|
|
707
707
|
return { builder: `text(${columnName(column.name)}) /* JSON */` };
|
|
708
708
|
}
|
|
709
709
|
|
|
710
|
+
if (upper.startsWith('ENUM')) {
|
|
711
|
+
imports.pgCore.add('text');
|
|
712
|
+
const enumLiteral = raw.replace(/^ENUM\s*/i, '').trim();
|
|
713
|
+
return {
|
|
714
|
+
builder: `text(${columnName(column.name)}) /* ENUM ${enumLiteral} */`,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if (upper.startsWith('UNION')) {
|
|
719
|
+
imports.pgCore.add('text');
|
|
720
|
+
const unionLiteral = raw.replace(/^UNION\s*/i, '').trim();
|
|
721
|
+
return {
|
|
722
|
+
builder: `text(${columnName(column.name)}) /* UNION ${unionLiteral} */`,
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
710
726
|
if (upper === 'INET') {
|
|
711
727
|
imports.local.add('duckDbInet');
|
|
712
728
|
return { builder: `duckDbInet(${columnName(column.name)})` };
|
|
@@ -788,15 +804,16 @@ function mapDuckDbType(
|
|
|
788
804
|
}
|
|
789
805
|
|
|
790
806
|
// Fallback: keep as text to avoid runtime failures.
|
|
807
|
+
// Unknown types are mapped to text with a comment indicating the original type.
|
|
791
808
|
imports.pgCore.add('text');
|
|
792
809
|
return {
|
|
793
810
|
builder: `text(${columnName(
|
|
794
811
|
column.name
|
|
795
|
-
)}) /*
|
|
812
|
+
)}) /* unsupported DuckDB type: ${upper} */`,
|
|
796
813
|
};
|
|
797
814
|
}
|
|
798
815
|
|
|
799
|
-
function parseStructFields(
|
|
816
|
+
export function parseStructFields(
|
|
800
817
|
inner: string
|
|
801
818
|
): Array<{ name: string; type: string }> {
|
|
802
819
|
const result: Array<{ name: string; type: string }> = [];
|
|
@@ -813,7 +830,7 @@ function parseStructFields(
|
|
|
813
830
|
return result;
|
|
814
831
|
}
|
|
815
832
|
|
|
816
|
-
function parseMapValue(raw: string): string {
|
|
833
|
+
export function parseMapValue(raw: string): string {
|
|
817
834
|
const inner = raw.replace(/^MAP\(/i, '').replace(/\)$/, '');
|
|
818
835
|
const parts = splitTopLevel(inner, ',');
|
|
819
836
|
if (parts.length < 2) {
|
|
@@ -822,7 +839,7 @@ function parseMapValue(raw: string): string {
|
|
|
822
839
|
return parts[1]?.trim() ?? 'TEXT';
|
|
823
840
|
}
|
|
824
841
|
|
|
825
|
-
function splitTopLevel(input: string, delimiter: string): string[] {
|
|
842
|
+
export function splitTopLevel(input: string, delimiter: string): string[] {
|
|
826
843
|
const parts: string[] = [];
|
|
827
844
|
let depth = 0;
|
|
828
845
|
let current = '';
|
|
@@ -847,7 +864,7 @@ function tableKey(schema: string, table: string): string {
|
|
|
847
864
|
return `${schema}.${table}`;
|
|
848
865
|
}
|
|
849
866
|
|
|
850
|
-
function toIdentifier(name: string): string {
|
|
867
|
+
export function toIdentifier(name: string): string {
|
|
851
868
|
const cleaned = name.replace(/[^A-Za-z0-9_]/g, '_');
|
|
852
869
|
const parts = cleaned.split('_').filter(Boolean);
|
|
853
870
|
const base = parts
|