@duckdbfan/drizzle-duckdb 0.0.6 → 1.3.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.
Files changed (55) hide show
  1. package/README.md +344 -62
  2. package/dist/bin/duckdb-introspect.d.ts +2 -0
  3. package/dist/client.d.ts +42 -0
  4. package/dist/columns.d.ts +142 -0
  5. package/dist/dialect.d.ts +27 -2
  6. package/dist/driver.d.ts +53 -37
  7. package/dist/duckdb-introspect.mjs +2890 -0
  8. package/dist/helpers.d.ts +1 -0
  9. package/dist/helpers.mjs +360 -0
  10. package/dist/index.d.ts +7 -0
  11. package/dist/index.mjs +3071 -209
  12. package/dist/introspect.d.ts +74 -0
  13. package/dist/migrator.d.ts +3 -2
  14. package/dist/olap.d.ts +46 -0
  15. package/dist/operators.d.ts +8 -0
  16. package/dist/options.d.ts +7 -0
  17. package/dist/pool.d.ts +30 -0
  18. package/dist/select-builder.d.ts +31 -0
  19. package/dist/session.d.ts +33 -8
  20. package/dist/sql/ast-transformer.d.ts +33 -0
  21. package/dist/sql/result-mapper.d.ts +9 -0
  22. package/dist/sql/selection.d.ts +2 -0
  23. package/dist/sql/visitors/array-operators.d.ts +5 -0
  24. package/dist/sql/visitors/column-qualifier.d.ts +10 -0
  25. package/dist/sql/visitors/generate-series-alias.d.ts +13 -0
  26. package/dist/sql/visitors/union-with-hoister.d.ts +11 -0
  27. package/dist/utils.d.ts +2 -5
  28. package/dist/value-wrappers-core.d.ts +42 -0
  29. package/dist/value-wrappers.d.ts +8 -0
  30. package/package.json +53 -16
  31. package/src/bin/duckdb-introspect.ts +181 -0
  32. package/src/client.ts +528 -0
  33. package/src/columns.ts +510 -1
  34. package/src/dialect.ts +111 -15
  35. package/src/driver.ts +266 -180
  36. package/src/helpers.ts +18 -0
  37. package/src/index.ts +8 -1
  38. package/src/introspect.ts +935 -0
  39. package/src/migrator.ts +10 -5
  40. package/src/olap.ts +190 -0
  41. package/src/operators.ts +27 -0
  42. package/src/options.ts +25 -0
  43. package/src/pool.ts +274 -0
  44. package/src/select-builder.ts +110 -0
  45. package/src/session.ts +306 -66
  46. package/src/sql/ast-transformer.ts +170 -0
  47. package/src/sql/result-mapper.ts +303 -0
  48. package/src/sql/selection.ts +60 -0
  49. package/src/sql/visitors/array-operators.ts +214 -0
  50. package/src/sql/visitors/column-qualifier.ts +586 -0
  51. package/src/sql/visitors/generate-series-alias.ts +291 -0
  52. package/src/sql/visitors/union-with-hoister.ts +106 -0
  53. package/src/utils.ts +2 -216
  54. package/src/value-wrappers-core.ts +168 -0
  55. package/src/value-wrappers.ts +165 -0
@@ -0,0 +1 @@
1
+ export { duckDbList, duckDbArray, duckDbMap, duckDbStruct, duckDbJson, duckDbBlob, duckDbInet, duckDbInterval, duckDbTimestamp, duckDbDate, duckDbTime, duckDbArrayContains, duckDbArrayContained, duckDbArrayOverlaps, } from './columns.ts';
@@ -0,0 +1,360 @@
1
+ // src/columns.ts
2
+ import { sql } from "drizzle-orm";
3
+ import { customType } from "drizzle-orm/pg-core";
4
+
5
+ // src/value-wrappers-core.ts
6
+ var DUCKDB_VALUE_MARKER = Symbol.for("drizzle-duckdb:value");
7
+ function wrapList(data, elementType) {
8
+ return {
9
+ [DUCKDB_VALUE_MARKER]: true,
10
+ kind: "list",
11
+ data,
12
+ elementType
13
+ };
14
+ }
15
+ function wrapArray(data, elementType, fixedLength) {
16
+ return {
17
+ [DUCKDB_VALUE_MARKER]: true,
18
+ kind: "array",
19
+ data,
20
+ elementType,
21
+ fixedLength
22
+ };
23
+ }
24
+ function wrapMap(data, valueType) {
25
+ return {
26
+ [DUCKDB_VALUE_MARKER]: true,
27
+ kind: "map",
28
+ data,
29
+ valueType
30
+ };
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
+ }
41
+ function wrapBlob(data) {
42
+ return {
43
+ [DUCKDB_VALUE_MARKER]: true,
44
+ kind: "blob",
45
+ data
46
+ };
47
+ }
48
+ function wrapJson(data) {
49
+ return {
50
+ [DUCKDB_VALUE_MARKER]: true,
51
+ kind: "json",
52
+ data
53
+ };
54
+ }
55
+
56
+ // src/columns.ts
57
+ function coerceArrayString(value) {
58
+ const trimmed = value.trim();
59
+ if (!trimmed) {
60
+ return [];
61
+ }
62
+ if (trimmed.startsWith("[")) {
63
+ try {
64
+ return JSON.parse(trimmed);
65
+ } catch {
66
+ return;
67
+ }
68
+ }
69
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
70
+ try {
71
+ const json = trimmed.replace(/{/g, "[").replace(/}/g, "]");
72
+ return JSON.parse(json);
73
+ } catch {
74
+ return;
75
+ }
76
+ }
77
+ return;
78
+ }
79
+ function formatLiteral(value, typeHint) {
80
+ if (value === null || value === undefined) {
81
+ return "NULL";
82
+ }
83
+ const upperType = typeHint?.toUpperCase() ?? "";
84
+ if (value instanceof Date) {
85
+ return `'${value.toISOString()}'`;
86
+ }
87
+ if (typeof value === "number" || typeof value === "bigint") {
88
+ return value.toString();
89
+ }
90
+ if (typeof value === "boolean") {
91
+ return value ? "TRUE" : "FALSE";
92
+ }
93
+ const str = typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
94
+ const escaped = str.replace(/'/g, "''");
95
+ if (upperType.includes("CHAR") || upperType.includes("TEXT") || upperType.includes("STRING") || upperType.includes("VARCHAR")) {
96
+ return `'${escaped}'`;
97
+ }
98
+ return `'${escaped}'`;
99
+ }
100
+ function buildListLiteral(values, elementType) {
101
+ if (values.length === 0) {
102
+ return sql`[]`;
103
+ }
104
+ const chunks = values.map((v) => typeof v === "object" && !Array.isArray(v) ? sql`${v}` : sql.raw(formatLiteral(v, elementType)));
105
+ return sql`list_value(${sql.join(chunks, sql.raw(", "))})`;
106
+ }
107
+ function buildStructLiteral(value, schema) {
108
+ const parts = Object.entries(value).map(([key, val]) => {
109
+ const typeHint = schema?.[key];
110
+ if (Array.isArray(val)) {
111
+ const inner = typeof typeHint === "string" && typeHint.endsWith("[]") ? typeHint.slice(0, -2) : undefined;
112
+ return sql`${sql.identifier(key)} := ${buildListLiteral(val, inner)}`;
113
+ }
114
+ return sql`${sql.identifier(key)} := ${val}`;
115
+ });
116
+ return sql`struct_pack(${sql.join(parts, sql.raw(", "))})`;
117
+ }
118
+ function buildMapLiteral(value, valueType) {
119
+ const keys = Object.keys(value);
120
+ const vals = Object.values(value);
121
+ const keyList = buildListLiteral(keys, "TEXT");
122
+ const valList = buildListLiteral(vals, valueType?.endsWith("[]") ? valueType.slice(0, -2) : valueType);
123
+ return sql`map(${keyList}, ${valList})`;
124
+ }
125
+ var duckDbList = (name, elementType) => customType({
126
+ dataType() {
127
+ return `${elementType}[]`;
128
+ },
129
+ toDriver(value) {
130
+ return wrapList(value, elementType);
131
+ },
132
+ fromDriver(value) {
133
+ if (Array.isArray(value)) {
134
+ return value;
135
+ }
136
+ if (typeof value === "string") {
137
+ const parsed = coerceArrayString(value);
138
+ if (parsed !== undefined) {
139
+ return parsed;
140
+ }
141
+ }
142
+ return value;
143
+ }
144
+ })(name);
145
+ var duckDbArray = (name, elementType, fixedLength) => customType({
146
+ dataType() {
147
+ return fixedLength ? `${elementType}[${fixedLength}]` : `${elementType}[]`;
148
+ },
149
+ toDriver(value) {
150
+ return wrapArray(value, elementType, fixedLength);
151
+ },
152
+ fromDriver(value) {
153
+ if (Array.isArray(value)) {
154
+ return value;
155
+ }
156
+ if (typeof value === "string") {
157
+ const parsed = coerceArrayString(value);
158
+ if (parsed !== undefined) {
159
+ return parsed;
160
+ }
161
+ }
162
+ return value;
163
+ }
164
+ })(name);
165
+ var duckDbMap = (name, valueType) => customType({
166
+ dataType() {
167
+ return `MAP (STRING, ${valueType})`;
168
+ },
169
+ toDriver(value) {
170
+ if (Object.keys(value).length === 0) {
171
+ return buildMapLiteral(value, valueType);
172
+ }
173
+ return wrapMap(value, valueType);
174
+ },
175
+ fromDriver(value) {
176
+ return value;
177
+ }
178
+ })(name);
179
+ var duckDbStruct = (name, schema) => customType({
180
+ dataType() {
181
+ const fields = Object.entries(schema).map(([key, type]) => `${key} ${type}`);
182
+ return `STRUCT (${fields.join(", ")})`;
183
+ },
184
+ toDriver(value) {
185
+ return buildStructLiteral(value, schema);
186
+ },
187
+ fromDriver(value) {
188
+ if (typeof value === "string") {
189
+ try {
190
+ return JSON.parse(value);
191
+ } catch {
192
+ return value;
193
+ }
194
+ }
195
+ return value;
196
+ }
197
+ })(name);
198
+ var duckDbJson = (name) => customType({
199
+ dataType() {
200
+ return "JSON";
201
+ },
202
+ toDriver(value) {
203
+ if (typeof value === "string") {
204
+ return value;
205
+ }
206
+ if (value !== null && typeof value === "object" && "queryChunks" in value) {
207
+ return value;
208
+ }
209
+ return wrapJson(value);
210
+ },
211
+ fromDriver(value) {
212
+ if (typeof value !== "string") {
213
+ return value;
214
+ }
215
+ const trimmed = value.trim();
216
+ if (!trimmed) {
217
+ return value;
218
+ }
219
+ try {
220
+ return JSON.parse(trimmed);
221
+ } catch {
222
+ return value;
223
+ }
224
+ }
225
+ })(name);
226
+ var duckDbBlob = customType({
227
+ dataType() {
228
+ return "BLOB";
229
+ },
230
+ toDriver(value) {
231
+ return wrapBlob(value);
232
+ }
233
+ });
234
+ var duckDbInet = (name) => customType({
235
+ dataType() {
236
+ return "INET";
237
+ },
238
+ toDriver(value) {
239
+ return value;
240
+ }
241
+ })(name);
242
+ var duckDbInterval = (name) => customType({
243
+ dataType() {
244
+ return "INTERVAL";
245
+ },
246
+ toDriver(value) {
247
+ return value;
248
+ }
249
+ })(name);
250
+ function shouldBindTimestamp(options) {
251
+ const bindMode = options.bindMode ?? "auto";
252
+ if (bindMode === "bind")
253
+ return true;
254
+ if (bindMode === "literal")
255
+ return false;
256
+ const isBun = typeof process !== "undefined" && typeof process.versions?.bun !== "undefined";
257
+ if (isBun)
258
+ return false;
259
+ const forceLiteral = typeof process !== "undefined" ? process.env.DRIZZLE_DUCKDB_FORCE_LITERAL_TIMESTAMPS : undefined;
260
+ if (forceLiteral && forceLiteral !== "0") {
261
+ return false;
262
+ }
263
+ return true;
264
+ }
265
+ var duckDbTimestamp = (name, options = {}) => customType({
266
+ dataType() {
267
+ if (options.withTimezone) {
268
+ return "TIMESTAMPTZ";
269
+ }
270
+ const precision = options.precision ? `(${options.precision})` : "";
271
+ return `TIMESTAMP${precision}`;
272
+ },
273
+ toDriver(value) {
274
+ if (shouldBindTimestamp(options)) {
275
+ return wrapTimestamp(value, options.withTimezone ?? false, options.precision);
276
+ }
277
+ const iso = value instanceof Date ? value.toISOString() : value;
278
+ const normalized = iso.replace("T", " ").replace("Z", "+00");
279
+ const typeKeyword = options.withTimezone ? "TIMESTAMPTZ" : "TIMESTAMP";
280
+ return sql.raw(`${typeKeyword} '${normalized}'`);
281
+ },
282
+ fromDriver(value) {
283
+ if (value && typeof value === "object" && "kind" in value && value.kind === "timestamp") {
284
+ const wrapped = value;
285
+ return wrapped.data instanceof Date ? wrapped.data : typeof wrapped.data === "number" || typeof wrapped.data === "bigint" ? new Date(Number(wrapped.data) / 1000) : wrapped.data;
286
+ }
287
+ if (options.mode === "string") {
288
+ if (value instanceof Date) {
289
+ return value.toISOString().replace("T", " ").replace("Z", "+00");
290
+ }
291
+ return typeof value === "string" ? value : value.toString();
292
+ }
293
+ if (value instanceof Date) {
294
+ return value;
295
+ }
296
+ const stringValue = typeof value === "string" ? value : value.toString();
297
+ const hasOffset = stringValue.endsWith("Z") || /[+-]\d{2}:?\d{2}$/.test(stringValue);
298
+ const normalized = hasOffset ? stringValue.replace(" ", "T") : `${stringValue.replace(" ", "T")}Z`;
299
+ return new Date(normalized);
300
+ }
301
+ })(name);
302
+ var duckDbDate = (name) => customType({
303
+ dataType() {
304
+ return "DATE";
305
+ },
306
+ toDriver(value) {
307
+ return value;
308
+ },
309
+ fromDriver(value) {
310
+ const str = value instanceof Date ? value.toISOString().slice(0, 10) : value;
311
+ return str;
312
+ }
313
+ })(name);
314
+ var duckDbTime = (name) => customType({
315
+ dataType() {
316
+ return "TIME";
317
+ },
318
+ toDriver(value) {
319
+ return value;
320
+ },
321
+ fromDriver(value) {
322
+ if (typeof value === "bigint") {
323
+ const totalMillis = Number(value) / 1000;
324
+ const date = new Date(totalMillis);
325
+ return date.toISOString().split("T")[1].replace("Z", "");
326
+ }
327
+ return value;
328
+ }
329
+ })(name);
330
+ function toListValue(values) {
331
+ return buildListLiteral(values);
332
+ }
333
+ function duckDbArrayContains(column, values) {
334
+ const rhs = Array.isArray(values) ? toListValue(values) : values;
335
+ return sql`array_has_all(${column}, ${rhs})`;
336
+ }
337
+ function duckDbArrayContained(column, values) {
338
+ const rhs = Array.isArray(values) ? toListValue(values) : values;
339
+ return sql`array_has_all(${rhs}, ${column})`;
340
+ }
341
+ function duckDbArrayOverlaps(column, values) {
342
+ const rhs = Array.isArray(values) ? toListValue(values) : values;
343
+ return sql`array_has_any(${column}, ${rhs})`;
344
+ }
345
+ export {
346
+ duckDbTimestamp,
347
+ duckDbTime,
348
+ duckDbStruct,
349
+ duckDbMap,
350
+ duckDbList,
351
+ duckDbJson,
352
+ duckDbInterval,
353
+ duckDbInet,
354
+ duckDbDate,
355
+ duckDbBlob,
356
+ duckDbArrayOverlaps,
357
+ duckDbArrayContains,
358
+ duckDbArrayContained,
359
+ duckDbArray
360
+ };
package/dist/index.d.ts CHANGED
@@ -2,3 +2,10 @@ export * from './driver.ts';
2
2
  export * from './session.ts';
3
3
  export * from './columns.ts';
4
4
  export * from './migrator.ts';
5
+ export * from './introspect.ts';
6
+ export * from './client.ts';
7
+ export * from './pool.ts';
8
+ export * from './olap.ts';
9
+ export * from './value-wrappers.ts';
10
+ export * from './options.ts';
11
+ export * from './operators.ts';