@happyvertical/sql 0.80.0 → 0.80.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 (31) hide show
  1. package/dist/backup/index.js +210 -188
  2. package/dist/backup/index.js.map +1 -1
  3. package/dist/chunks/alter-utils-K7tiuj5L.js +135 -0
  4. package/dist/chunks/alter-utils-K7tiuj5L.js.map +1 -0
  5. package/dist/chunks/duckdb-DykBBzd6.js +1056 -0
  6. package/dist/chunks/duckdb-DykBBzd6.js.map +1 -0
  7. package/dist/chunks/duckdb-schema-utils-B-XBP8QF.js +115 -0
  8. package/dist/chunks/duckdb-schema-utils-B-XBP8QF.js.map +1 -0
  9. package/dist/chunks/postgres-cli-DzZcXSIG.js +272 -0
  10. package/dist/chunks/postgres-cli-DzZcXSIG.js.map +1 -0
  11. package/dist/chunks/schema-manager-a8RlxIE2.js +450 -0
  12. package/dist/chunks/schema-manager-a8RlxIE2.js.map +1 -0
  13. package/dist/chunks/sqlite-YgU9LCVy.js +1045 -0
  14. package/dist/chunks/sqlite-YgU9LCVy.js.map +1 -0
  15. package/dist/chunks/sqlite-native-BGtpx7Ro.js +902 -0
  16. package/dist/chunks/sqlite-native-BGtpx7Ro.js.map +1 -0
  17. package/dist/cli/claude-context.js +17 -17
  18. package/dist/cli/claude-context.js.map +1 -1
  19. package/dist/index.js +3244 -35
  20. package/dist/index.js.map +1 -1
  21. package/package.json +5 -5
  22. package/dist/chunks/duckdb-B6m78QdL.js +0 -924
  23. package/dist/chunks/duckdb-B6m78QdL.js.map +0 -1
  24. package/dist/chunks/index-CzdYvRFp.js +0 -3692
  25. package/dist/chunks/index-CzdYvRFp.js.map +0 -1
  26. package/dist/chunks/postgres-cli-Cr6bBZ3q.js +0 -209
  27. package/dist/chunks/postgres-cli-Cr6bBZ3q.js.map +0 -1
  28. package/dist/chunks/sqlite-CPCNeJIH.js +0 -937
  29. package/dist/chunks/sqlite-CPCNeJIH.js.map +0 -1
  30. package/dist/chunks/sqlite-native-DqVPKNUu.js +0 -1129
  31. package/dist/chunks/sqlite-native-DqVPKNUu.js.map +0 -1
@@ -1,924 +0,0 @@
1
- import { readdir } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join, extname, basename } from "node:path";
4
- import { DatabaseError } from "@happyvertical/utils";
5
- import { r as resolveSchemas, v as validateTableName, a as validateIndexName, b as validateColumnName, g as generateCreateIndexStatement, f as formatDbError, c as generateAddColumnStatement, D as DatabaseSchemaManager, e as convertUniqueIndexesToInlineConstraints, d as buildWhere } from "./index-CzdYvRFp.js";
6
- async function createTablesFromSchemas(connection, schemas) {
7
- for (const [tableName, schema] of Object.entries(schemas)) {
8
- try {
9
- const result = await connection.runAndReadAll(
10
- `SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`
11
- );
12
- const rows = result.getRowObjects();
13
- if (rows.length > 0) {
14
- console.log(`[duckdb] Table ${tableName} already exists, skipping`);
15
- continue;
16
- }
17
- console.log(`[duckdb] Creating table ${tableName} from provided schema`);
18
- const transformed = convertUniqueIndexesToInlineConstraints(
19
- schema.ddl,
20
- schema.indexes
21
- );
22
- await connection.run(transformed.ddl);
23
- if (transformed.indexes && transformed.indexes.length > 0) {
24
- for (const indexSQL of transformed.indexes) {
25
- try {
26
- await connection.run(indexSQL);
27
- } catch (error) {
28
- console.warn(
29
- `[duckdb] Failed to create index for ${tableName}: ${error instanceof Error ? error.message : String(error)}`
30
- );
31
- }
32
- }
33
- }
34
- if (schema.triggers && schema.triggers.length > 0) {
35
- for (const triggerSQL of schema.triggers) {
36
- try {
37
- await connection.run(triggerSQL);
38
- } catch (error) {
39
- console.warn(
40
- `[duckdb] Failed to create trigger for ${tableName}: ${error instanceof Error ? error.message : String(error)}`
41
- );
42
- }
43
- }
44
- }
45
- } catch (error) {
46
- throw new DatabaseError(
47
- `Failed to create table ${tableName} from schema`,
48
- {
49
- tableName,
50
- schema,
51
- originalError: error instanceof Error ? error.message : String(error)
52
- }
53
- );
54
- }
55
- }
56
- }
57
- function resolveDuckDBUrl(url) {
58
- if (url === ":memory:") {
59
- return url;
60
- }
61
- if (url.startsWith(":memory:")) {
62
- const suffix = url.slice(":memory:".length);
63
- const tempPath = join(tmpdir(), `duckdb-memory-${suffix}.db`);
64
- return tempPath;
65
- }
66
- return url;
67
- }
68
- async function createDuckDBConnection(options) {
69
- const {
70
- url = ":memory:",
71
- dataDir = "./data",
72
- autoRegisterJSON = true
73
- } = options;
74
- const schemas = resolveSchemas(options.schemas) ?? {};
75
- const resolvedUrl = resolveDuckDBUrl(url);
76
- try {
77
- const duckdbModule = "@duckdb/node-api";
78
- const { DuckDBInstance } = await import(
79
- /* @vite-ignore */
80
- duckdbModule
81
- );
82
- const instance = await DuckDBInstance.create(resolvedUrl);
83
- const connection = await instance.connect();
84
- if (schemas && Object.keys(schemas).length > 0) {
85
- await createTablesFromSchemas(connection, schemas);
86
- }
87
- if (autoRegisterJSON && dataDir) {
88
- await registerJSONFiles(connection, dataDir);
89
- }
90
- return connection;
91
- } catch (error) {
92
- const errorMessage = error instanceof Error ? error.message : String(error);
93
- throw new DatabaseError(
94
- `Failed to create DuckDB connection: ${errorMessage}`,
95
- {
96
- url,
97
- originalError: errorMessage
98
- }
99
- );
100
- }
101
- }
102
- async function registerJSONFiles(connection, dataDir) {
103
- try {
104
- const files = await readdir(dataDir);
105
- const jsonFiles = files.filter(
106
- (file) => extname(file).toLowerCase() === ".json"
107
- );
108
- for (const file of jsonFiles) {
109
- const filePath = join(dataDir, file);
110
- const tableName = basename(file, ".json");
111
- await connection.run(
112
- `CREATE OR REPLACE VIEW ${tableName} AS SELECT * FROM read_json('${filePath}', auto_detect=true, format='auto', timestampformat='iso', dateformat='iso')`
113
- );
114
- }
115
- } catch (error) {
116
- if (error.code !== "ENOENT") {
117
- throw new DatabaseError(`Failed to register JSON files from ${dataDir}`, {
118
- dataDir,
119
- originalError: error instanceof Error ? error.message : String(error)
120
- });
121
- }
122
- }
123
- }
124
- function convertBigInts(obj) {
125
- if (obj === null || obj === void 0) return obj;
126
- if (typeof obj === "bigint") return Number(obj);
127
- if (typeof obj === "object" && !Array.isArray(obj) && "micros" in obj && Object.keys(obj).length === 1 && (typeof obj.micros === "number" || typeof obj.micros === "bigint")) {
128
- const micros = typeof obj.micros === "bigint" ? Number(obj.micros) : obj.micros;
129
- const milliseconds = micros / 1e3;
130
- return new Date(milliseconds);
131
- }
132
- if (Array.isArray(obj)) return obj.map(convertBigInts);
133
- if (typeof obj === "object") {
134
- const result = {};
135
- for (const key in obj) {
136
- result[key] = convertBigInts(obj[key]);
137
- }
138
- return result;
139
- }
140
- return obj;
141
- }
142
- async function getDatabase(options = {}) {
143
- const connection = await createDuckDBConnection(options);
144
- const writeStrategy = options.writeStrategy || "none";
145
- const dataDir = options.dataDir || "./data";
146
- const url = resolveDuckDBUrl(options.url || ":memory:");
147
- const insert = async (table2, data) => {
148
- const records = Array.isArray(data) ? data : [data];
149
- if (records.length === 0) {
150
- return { operation: "insert", affected: 0 };
151
- }
152
- const keys = Object.keys(records[0]);
153
- const values = [];
154
- let paramIdx = 1;
155
- const placeholders = records.map((record) => {
156
- const rowPlaceholders = keys.map((key) => {
157
- const value = record[key];
158
- if (value === null || value === void 0) {
159
- return "NULL";
160
- } else if (value === "" && typeof value === "string") {
161
- values.push(value);
162
- return `CAST($${paramIdx++} AS TEXT)`;
163
- } else if (value instanceof Date) {
164
- values.push(value.toISOString());
165
- return `$${paramIdx++}`;
166
- } else if (Array.isArray(value)) {
167
- values.push(JSON.stringify(value));
168
- return `CAST($${paramIdx++} AS JSON)`;
169
- } else if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype) {
170
- values.push(JSON.stringify(value));
171
- return `CAST($${paramIdx++} AS JSON)`;
172
- } else {
173
- values.push(value);
174
- return `$${paramIdx++}`;
175
- }
176
- });
177
- return `(${rowPlaceholders.join(", ")})`;
178
- }).join(", ");
179
- const sql = `INSERT INTO ${table2} (${keys.join(", ")}) VALUES ${placeholders}`;
180
- try {
181
- await connection.run(sql, values);
182
- const affected = records.length;
183
- if (writeStrategy === "immediate") {
184
- await exportTableToJSON(connection, table2, dataDir);
185
- }
186
- return { operation: "insert", affected };
187
- } catch (e) {
188
- throw new DatabaseError("Failed to insert records into table", {
189
- table: table2,
190
- sql,
191
- values,
192
- originalError: formatDbError(e)
193
- });
194
- }
195
- };
196
- const get = async (table2, where) => {
197
- const { sql: whereClause, values } = buildWhere(where, 1, "duckdb");
198
- const sql = `SELECT * FROM ${table2} ${whereClause} LIMIT 1`;
199
- try {
200
- const reader = await connection.runAndReadAll(sql, values);
201
- const rows = reader.getRowObjects();
202
- return rows.length > 0 ? convertBigInts(rows[0]) : null;
203
- } catch (e) {
204
- throw new DatabaseError("Failed to retrieve record from table", {
205
- table: table2,
206
- sql,
207
- values,
208
- originalError: formatDbError(e)
209
- });
210
- }
211
- };
212
- const list = async (table2, where) => {
213
- const { sql: whereClause, values } = buildWhere(where, 1, "duckdb");
214
- const sql = `SELECT * FROM ${table2} ${whereClause}`;
215
- try {
216
- const reader = await connection.runAndReadAll(sql, values);
217
- return convertBigInts(reader.getRowObjects());
218
- } catch (e) {
219
- throw new DatabaseError("Failed to list records from table", {
220
- table: table2,
221
- sql,
222
- values,
223
- originalError: formatDbError(e)
224
- });
225
- }
226
- };
227
- const update = async (table2, where, data) => {
228
- const keys = Object.keys(data);
229
- const setClause = keys.map((key, idx) => `${key} = $${idx + 1}`).join(", ");
230
- const { sql: whereClause, values: whereValues } = buildWhere(
231
- where,
232
- keys.length + 1,
233
- "duckdb"
234
- );
235
- const sql = `UPDATE ${table2} SET ${setClause} ${whereClause}`;
236
- const values = [...Object.values(data), ...whereValues];
237
- try {
238
- await connection.run(sql, values);
239
- if (writeStrategy === "immediate") {
240
- await exportTableToJSON(connection, table2, dataDir);
241
- }
242
- return { operation: "update", affected: 1 };
243
- } catch (e) {
244
- throw new DatabaseError("Failed to update records in table", {
245
- table: table2,
246
- sql,
247
- values,
248
- originalError: formatDbError(e)
249
- });
250
- }
251
- };
252
- const buildDuckDBValueExpression = (value, values, paramIdx) => {
253
- if (value === null || value === void 0) {
254
- return "NULL";
255
- }
256
- if (value === "" && typeof value === "string") {
257
- values.push(value);
258
- return `CAST($${paramIdx.value++} AS TEXT)`;
259
- }
260
- if (value instanceof Date) {
261
- values.push(value.toISOString());
262
- return `$${paramIdx.value++}`;
263
- }
264
- if (Array.isArray(value)) {
265
- values.push(JSON.stringify(value));
266
- return `CAST($${paramIdx.value++} AS JSON)`;
267
- }
268
- if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype) {
269
- values.push(JSON.stringify(value));
270
- return `CAST($${paramIdx.value++} AS JSON)`;
271
- }
272
- values.push(value);
273
- return `$${paramIdx.value++}`;
274
- };
275
- const executeNullAwareDuckDBUpsert = async (table2, conflictColumns, data) => {
276
- const conflictWhere = Object.fromEntries(
277
- conflictColumns.map((col) => [col, data[col]])
278
- );
279
- const existing = await get(table2, conflictWhere);
280
- if (!existing) {
281
- return insert(table2, data);
282
- }
283
- const keys = Object.keys(data);
284
- const values = [];
285
- const paramIdx = { value: 1 };
286
- const assignments = keys.map((key) => {
287
- const valueExpr = buildDuckDBValueExpression(data[key], values, paramIdx);
288
- return `"${key}" = ${valueExpr}`;
289
- });
290
- const { sql: whereClause, values: whereValues } = buildWhere(
291
- conflictWhere,
292
- paramIdx.value,
293
- "duckdb"
294
- );
295
- const sql = `UPDATE ${table2} SET ${assignments.join(", ")} ${whereClause}`;
296
- try {
297
- await connection.run(sql, [...values, ...whereValues]);
298
- if (writeStrategy === "immediate") {
299
- await exportTableToJSON(connection, table2, dataDir);
300
- }
301
- return { operation: "upsert", affected: 1 };
302
- } catch (e) {
303
- throw new DatabaseError("Failed to upsert record into table", {
304
- table: table2,
305
- sql,
306
- values: [...values, ...whereValues],
307
- conflictColumns,
308
- originalError: formatDbError(e)
309
- });
310
- }
311
- };
312
- const upsert = async (table2, conflictColumns, data, options2) => {
313
- const missingColumns = conflictColumns.filter((col) => !(col in data));
314
- if (missingColumns.length > 0) {
315
- throw new DatabaseError("Conflict columns missing from data", {
316
- table: table2,
317
- conflictColumns,
318
- missingColumns,
319
- availableColumns: Object.keys(data),
320
- hint: "All columns specified in ON CONFLICT must be present in the data being inserted. Undefined values should be replaced with null or an appropriate default."
321
- });
322
- }
323
- if (!options2?.nullsDistinct && conflictColumns.some((col) => data[col] === null)) {
324
- return executeNullAwareDuckDBUpsert(table2, conflictColumns, data);
325
- }
326
- const keys = Object.keys(data);
327
- const dataValues = Object.values(data);
328
- const placeholders = [];
329
- const values = [];
330
- let paramIdx = 1;
331
- for (const value of dataValues) {
332
- if (value === null || value === void 0) {
333
- placeholders.push("NULL");
334
- } else if (value === "" && typeof value === "string") {
335
- placeholders.push(`CAST($${paramIdx} AS TEXT)`);
336
- values.push(value);
337
- paramIdx++;
338
- } else if (value instanceof Date) {
339
- placeholders.push(`$${paramIdx}`);
340
- values.push(value.toISOString());
341
- paramIdx++;
342
- } else if (Array.isArray(value)) {
343
- placeholders.push(`CAST($${paramIdx} AS JSON)`);
344
- values.push(JSON.stringify(value));
345
- paramIdx++;
346
- } else if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype) {
347
- placeholders.push(`CAST($${paramIdx} AS JSON)`);
348
- values.push(JSON.stringify(value));
349
- paramIdx++;
350
- } else {
351
- placeholders.push(`$${paramIdx}`);
352
- values.push(value);
353
- paramIdx++;
354
- }
355
- }
356
- const updateSetParts = [];
357
- for (let i = 0; i < keys.length; i++) {
358
- const key = keys[i];
359
- const value = dataValues[i];
360
- if (value === null || value === void 0) {
361
- updateSetParts.push(`${key} = NULL`);
362
- } else if (value === "" && typeof value === "string") {
363
- updateSetParts.push(`${key} = CAST($${paramIdx} AS TEXT)`);
364
- values.push(value);
365
- paramIdx++;
366
- } else if (value instanceof Date) {
367
- updateSetParts.push(`${key} = $${paramIdx}`);
368
- values.push(value.toISOString());
369
- paramIdx++;
370
- } else if (Array.isArray(value)) {
371
- updateSetParts.push(`${key} = CAST($${paramIdx} AS JSON)`);
372
- values.push(JSON.stringify(value));
373
- paramIdx++;
374
- } else if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype) {
375
- updateSetParts.push(`${key} = CAST($${paramIdx} AS JSON)`);
376
- values.push(JSON.stringify(value));
377
- paramIdx++;
378
- } else {
379
- updateSetParts.push(`${key} = $${paramIdx}`);
380
- values.push(value);
381
- paramIdx++;
382
- }
383
- }
384
- const conflict = conflictColumns.map((col) => `"${col}"`).join(", ");
385
- const quotedKeys = keys.map((key) => `"${key}"`).join(", ");
386
- const quotedUpdateSetParts = keys.map((key, i) => {
387
- const part = updateSetParts[i];
388
- const valueExpr = part.substring(part.indexOf("=") + 1).trim();
389
- return `"${key}" = ${valueExpr}`;
390
- });
391
- const sql = `INSERT INTO ${table2} (${quotedKeys}) VALUES (${placeholders.join(", ")}) ON CONFLICT(${conflict}) DO UPDATE SET ${quotedUpdateSetParts.join(", ")}`;
392
- try {
393
- await connection.run(sql, values);
394
- if (writeStrategy === "immediate") {
395
- await exportTableToJSON(connection, table2, dataDir);
396
- }
397
- return { operation: "upsert", affected: 1 };
398
- } catch (e) {
399
- throw new DatabaseError("Failed to upsert record into table", {
400
- table: table2,
401
- sql,
402
- values,
403
- conflictColumns,
404
- originalError: formatDbError(e)
405
- });
406
- }
407
- };
408
- const getOrInsert = async (table2, where, data) => {
409
- const result = await get(table2, where);
410
- if (result) return result;
411
- await insert(table2, data);
412
- const inserted = await get(table2, where);
413
- if (!inserted) {
414
- throw new DatabaseError("Failed to insert and retrieve record", {
415
- table: table2,
416
- where,
417
- data
418
- });
419
- }
420
- return inserted;
421
- };
422
- const deleteRecords = async (table2, where) => {
423
- validateTableName(table2);
424
- const keys = Object.keys(where);
425
- if (keys.length === 0) {
426
- throw new DatabaseError(
427
- "DELETE requires at least one WHERE condition to prevent accidental deletion of all records",
428
- { table: table2 }
429
- );
430
- }
431
- const { sql: whereClause, values } = buildWhere(where, 1, "duckdb");
432
- const sql = `DELETE FROM ${table2} ${whereClause}`;
433
- try {
434
- await connection.run(sql, values);
435
- if (writeStrategy === "immediate") {
436
- await exportTableToJSON(connection, table2, dataDir);
437
- }
438
- return { operation: "delete", affected: 1 };
439
- } catch (e) {
440
- throw new DatabaseError("Failed to delete records from table", {
441
- table: table2,
442
- sql,
443
- values,
444
- originalError: formatDbError(e)
445
- });
446
- }
447
- };
448
- const count = async (table2, where) => {
449
- validateTableName(table2);
450
- try {
451
- if (!where || Object.keys(where).length === 0) {
452
- const result2 = await connection.runAndReadAll(
453
- `SELECT COUNT(*) as count FROM ${table2}`
454
- );
455
- const rows2 = result2.getRowObjects();
456
- return Number(rows2[0]?.count) || 0;
457
- }
458
- const { sql: whereClause, values } = buildWhere(where, 1, "duckdb");
459
- const sql = `SELECT COUNT(*) as count FROM ${table2} ${whereClause}`;
460
- const result = await connection.runAndReadAll(sql, values);
461
- const rows = result.getRowObjects();
462
- return Number(rows[0]?.count) || 0;
463
- } catch (e) {
464
- throw new DatabaseError("Failed to count records in table", {
465
- table: table2,
466
- where,
467
- originalError: formatDbError(e)
468
- });
469
- }
470
- };
471
- const tableExists = async (tableName) => {
472
- try {
473
- await connection.runAndReadAll(`SELECT * FROM ${tableName} LIMIT 1`);
474
- return true;
475
- } catch {
476
- return false;
477
- }
478
- };
479
- const exportTableToJSON = async (connection2, table2, dataDir2) => {
480
- const filePath = join(dataDir2, `${table2}.json`);
481
- await connection2.run(
482
- `COPY (SELECT * FROM ${table2}) TO '${filePath}' (FORMAT JSON, ARRAY true)`
483
- );
484
- };
485
- const exportTable = async (table2) => {
486
- if (writeStrategy === "none") {
487
- throw new DatabaseError(
488
- "Cannot export table: write strategy is set to none",
489
- { table: table2, writeStrategy }
490
- );
491
- }
492
- await exportTableToJSON(connection, table2, dataDir);
493
- };
494
- const table = (tableName) => ({
495
- insert: (data) => insert(tableName, data),
496
- get: (where) => get(tableName, where),
497
- list: (where) => list(tableName, where)
498
- });
499
- const parseTemplate = (strings, ...vars) => {
500
- let sql = strings[0];
501
- const values = [];
502
- for (let i = 0; i < vars.length; i++) {
503
- values.push(vars[i]);
504
- sql += `$${i + 1}${strings[i + 1]}`;
505
- }
506
- return { sql, values };
507
- };
508
- const many = async (strings, ...vars) => {
509
- const { sql, values } = parseTemplate(strings, ...vars);
510
- try {
511
- const reader = await connection.runAndReadAll(sql, values);
512
- return convertBigInts(reader.getRowObjects());
513
- } catch (e) {
514
- throw new DatabaseError("Failed to execute many query", {
515
- sql,
516
- values,
517
- originalError: formatDbError(e)
518
- });
519
- }
520
- };
521
- const single = async (strings, ...vars) => {
522
- const { sql, values } = parseTemplate(strings, ...vars);
523
- try {
524
- const reader = await connection.runAndReadAll(sql, values);
525
- const rows = reader.getRowObjects();
526
- return rows[0] ? convertBigInts(rows[0]) : null;
527
- } catch (e) {
528
- throw new DatabaseError("Failed to execute single query", {
529
- sql,
530
- values,
531
- originalError: formatDbError(e)
532
- });
533
- }
534
- };
535
- const pluck = async (strings, ...vars) => {
536
- const { sql, values } = parseTemplate(strings, ...vars);
537
- try {
538
- const reader = await connection.runAndReadAll(sql, values);
539
- const rows = reader.getRowObjects();
540
- if (rows.length === 0) return null;
541
- const firstRow = rows[0];
542
- const firstKey = Object.keys(firstRow)[0];
543
- return convertBigInts(firstRow[firstKey]);
544
- } catch (e) {
545
- throw new DatabaseError("Failed to execute pluck query", {
546
- sql,
547
- values,
548
- originalError: formatDbError(e)
549
- });
550
- }
551
- };
552
- const execute = async (strings, ...vars) => {
553
- const { sql, values } = parseTemplate(strings, ...vars);
554
- try {
555
- await connection.run(sql, values);
556
- } catch (e) {
557
- throw new DatabaseError("Failed to execute query", {
558
- sql,
559
- values,
560
- originalError: formatDbError(e)
561
- });
562
- }
563
- };
564
- const query = async (str, ...values) => {
565
- const sql = str;
566
- const args = Array.isArray(values[0]) ? values[0] : values;
567
- try {
568
- const reader = await connection.runAndReadAll(sql, args);
569
- const rows = convertBigInts(reader.getRowObjects());
570
- return {
571
- command: sql.split(" ")[0].toUpperCase(),
572
- rowCount: rows.length,
573
- oid: null,
574
- fields: rows.length > 0 ? Object.keys(rows[0]).map((name) => ({
575
- name,
576
- tableID: 0,
577
- columnID: 0,
578
- dataTypeID: 0,
579
- dataTypeSize: -1,
580
- dataTypeModifier: -1,
581
- format: "text"
582
- })) : [],
583
- rows
584
- };
585
- } catch (e) {
586
- throw new DatabaseError("Failed to execute raw query", {
587
- sql,
588
- args,
589
- originalError: formatDbError(e)
590
- });
591
- }
592
- };
593
- const oo = many;
594
- const oO = single;
595
- const ox = pluck;
596
- const xx = execute;
597
- const syncSchema = async (schema) => {
598
- const statements = schema.trim().split(";").filter((stmt) => stmt.trim() !== "");
599
- const createTableStatements = [];
600
- const indexStatements = [];
601
- for (const stmt of statements) {
602
- const trimmed = stmt.trim();
603
- if (!trimmed) continue;
604
- if (trimmed.startsWith("CREATE INDEX") || trimmed.startsWith("CREATE UNIQUE INDEX")) {
605
- indexStatements.push(trimmed);
606
- } else if (trimmed.startsWith("CREATE TABLE")) {
607
- createTableStatements.push(trimmed);
608
- }
609
- }
610
- for (const ddl of createTableStatements) {
611
- const { ddl: transformedDDL, indexes: remainingIndexes } = convertUniqueIndexesToInlineConstraints(ddl, indexStatements);
612
- try {
613
- await connection.run(transformedDDL);
614
- } catch (e) {
615
- console.error("Schema sync error (CREATE TABLE):", e);
616
- }
617
- for (const indexSQL of remainingIndexes) {
618
- try {
619
- await connection.run(indexSQL);
620
- } catch (e) {
621
- console.error("Schema sync error (CREATE INDEX):", e);
622
- }
623
- }
624
- }
625
- };
626
- const initializeSchemas = async (options2) => {
627
- const schemaManager = new DatabaseSchemaManager();
628
- const currentDb = {
629
- url,
630
- client: connection,
631
- query,
632
- insert,
633
- update,
634
- upsert,
635
- get,
636
- list,
637
- getOrInsert,
638
- delete: deleteRecords,
639
- count,
640
- table,
641
- tableExists,
642
- many,
643
- single,
644
- pluck,
645
- execute,
646
- oo,
647
- oO,
648
- ox,
649
- xx,
650
- syncSchema
651
- };
652
- await schemaManager.initializeSchemas(currentDb, options2);
653
- };
654
- const getTableSchema = async (table2) => {
655
- validateTableName(table2);
656
- try {
657
- const exists = await tableExists(table2);
658
- if (!exists) {
659
- return null;
660
- }
661
- const columnRows = await many`
662
- SELECT
663
- column_name,
664
- data_type,
665
- is_nullable,
666
- column_default
667
- FROM information_schema.columns
668
- WHERE table_name = ${table2}
669
- ORDER BY ordinal_position
670
- `;
671
- const pkRows = await many`
672
- SELECT column_name
673
- FROM duckdb_constraints() con
674
- JOIN duckdb_columns() cols
675
- ON con.table_oid = cols.table_oid
676
- WHERE con.table_name = ${table2}
677
- AND con.constraint_type = 'PRIMARY KEY'
678
- AND cols.column_name = ANY(con.constraint_column_names)
679
- `;
680
- const pkColumns = new Set(pkRows.map((row) => row.column_name));
681
- const columns = {};
682
- for (const row of columnRows) {
683
- const colName = row.column_name;
684
- columns[colName] = {
685
- type: row.data_type,
686
- primaryKey: pkColumns.has(colName),
687
- notNull: row.is_nullable === "NO",
688
- defaultValue: row.column_default
689
- };
690
- }
691
- const indexRows = await many`
692
- SELECT
693
- index_name,
694
- sql
695
- FROM duckdb_indexes()
696
- WHERE table_name = ${table2}
697
- AND NOT is_primary
698
- `;
699
- const indexes = [];
700
- for (const row of indexRows) {
701
- const indexName = row.index_name;
702
- const indexDef = row.sql || "";
703
- const colMatch = indexDef.match(/\(([^)]+)\)/);
704
- const indexColumns = colMatch ? colMatch[1].split(",").map((col) => col.trim()) : [];
705
- const isUnique = indexDef.toUpperCase().includes("UNIQUE");
706
- indexes.push({
707
- name: indexName,
708
- columns: indexColumns,
709
- unique: isUnique
710
- });
711
- }
712
- const fkRows = await many`
713
- SELECT
714
- constraint_column_names,
715
- constraint_column_indexes,
716
- constraint_text
717
- FROM duckdb_constraints()
718
- WHERE table_name = ${table2}
719
- AND constraint_type = 'FOREIGN KEY'
720
- `;
721
- const foreignKeys = [];
722
- for (const fkRow of fkRows) {
723
- const fkText = fkRow.constraint_text || "";
724
- const referencesMatch = fkText.match(
725
- /REFERENCES\s+(\w+)\s*\(([^)]+)\)/i
726
- );
727
- if (referencesMatch && Array.isArray(fkRow.constraint_column_names) && fkRow.constraint_column_names.length > 0) {
728
- const columnName = fkRow.constraint_column_names[0];
729
- const referencedTable = referencesMatch[1];
730
- const referencedColumn = referencesMatch[2].trim();
731
- const onDeleteMatch = fkText.match(/ON DELETE\s+(\w+)/i);
732
- const onUpdateMatch = fkText.match(/ON UPDATE\s+(\w+)/i);
733
- foreignKeys.push({
734
- column: columnName,
735
- referencesTable: referencedTable,
736
- referencesColumn: referencedColumn,
737
- onDelete: onDeleteMatch ? onDeleteMatch[1] : void 0,
738
- onUpdate: onUpdateMatch ? onUpdateMatch[1] : void 0
739
- });
740
- }
741
- }
742
- return {
743
- tableName: table2,
744
- columns,
745
- indexes,
746
- foreignKeys
747
- };
748
- } catch (e) {
749
- throw new DatabaseError("Failed to retrieve table schema", {
750
- table: table2,
751
- originalError: formatDbError(e)
752
- });
753
- }
754
- };
755
- const alterTable = {
756
- /**
757
- * Adds a new column to an existing table
758
- *
759
- * @param table - Table name
760
- * @param column - Column definition with name
761
- * @returns Promise that resolves when column is added
762
- * @throws Error if the alter operation fails
763
- */
764
- addColumn: async (table2, column) => {
765
- validateTableName(table2);
766
- validateColumnName(column.name);
767
- try {
768
- const sql = generateAddColumnStatement(table2, column, "duckdb");
769
- await connection.run(sql);
770
- } catch (e) {
771
- throw new DatabaseError("Failed to add column to table", {
772
- table: table2,
773
- column: column.name,
774
- originalError: formatDbError(e)
775
- });
776
- }
777
- },
778
- /**
779
- * Adds a new index to an existing table
780
- *
781
- * @param table - Table name
782
- * @param index - Index definition
783
- * @returns Promise that resolves when index is created
784
- * @throws Error if the create index operation fails
785
- */
786
- addIndex: async (table2, index) => {
787
- validateTableName(table2);
788
- validateIndexName(index.name);
789
- for (const col of index.columns) {
790
- validateColumnName(col);
791
- }
792
- try {
793
- const sql = generateCreateIndexStatement(table2, index);
794
- await connection.run(sql);
795
- } catch (e) {
796
- throw new DatabaseError("Failed to create index on table", {
797
- table: table2,
798
- index: index.name,
799
- originalError: formatDbError(e)
800
- });
801
- }
802
- }
803
- };
804
- const transaction = async (callback) => {
805
- try {
806
- await connection.run("BEGIN TRANSACTION");
807
- const txDb = {
808
- url,
809
- client: connection,
810
- insert,
811
- get,
812
- list,
813
- update,
814
- upsert,
815
- getOrInsert,
816
- delete: deleteRecords,
817
- count,
818
- table,
819
- many,
820
- single,
821
- pluck,
822
- execute,
823
- query,
824
- oo,
825
- oO,
826
- ox,
827
- xx,
828
- tableExists,
829
- syncSchema,
830
- transaction
831
- };
832
- const result = await callback(txDb);
833
- await connection.run("COMMIT");
834
- return result;
835
- } catch (error) {
836
- await connection.run("ROLLBACK");
837
- throw error;
838
- }
839
- };
840
- const beginTransaction = async () => {
841
- await connection.run("BEGIN TRANSACTION");
842
- let active = true;
843
- const commit = async () => {
844
- if (!active) {
845
- throw new DatabaseError("Transaction already ended", {});
846
- }
847
- await connection.run("COMMIT");
848
- active = false;
849
- };
850
- const rollback = async () => {
851
- if (!active) {
852
- throw new DatabaseError("Transaction already ended", {});
853
- }
854
- await connection.run("ROLLBACK");
855
- active = false;
856
- };
857
- const isActive = () => active;
858
- const txHandle = {
859
- url,
860
- client: connection,
861
- insert,
862
- get,
863
- list,
864
- update,
865
- upsert,
866
- getOrInsert,
867
- delete: deleteRecords,
868
- count,
869
- table,
870
- many,
871
- single,
872
- pluck,
873
- execute,
874
- query,
875
- oo,
876
- oO,
877
- ox,
878
- xx,
879
- tableExists,
880
- syncSchema,
881
- transaction,
882
- commit,
883
- rollback,
884
- isActive
885
- };
886
- return txHandle;
887
- };
888
- return {
889
- url,
890
- requiresSchemaCheck: true,
891
- client: connection,
892
- query,
893
- insert,
894
- update,
895
- upsert,
896
- get,
897
- list,
898
- getOrInsert,
899
- delete: deleteRecords,
900
- count,
901
- table,
902
- tableExists,
903
- many,
904
- single,
905
- pluck,
906
- execute,
907
- oo,
908
- oO,
909
- ox,
910
- xx,
911
- syncSchema,
912
- initializeSchemas,
913
- transaction,
914
- beginTransaction,
915
- getTableSchema,
916
- alterTable,
917
- // DuckDB-specific export method
918
- exportTable
919
- };
920
- }
921
- export {
922
- getDatabase
923
- };
924
- //# sourceMappingURL=duckdb-B6m78QdL.js.map