@sqlrooms/duckdb 0.29.0-rc.7 → 0.29.0-rc.9

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/LICENSE.md CHANGED
@@ -1,6 +1,7 @@
1
1
  MIT License
2
2
 
3
- Copyright 2025 SQLRooms Contributors
3
+ Copyright 2024-2026 SQLRooms Contributors
4
+ Copyright Vis.gl contributors
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
7
 
package/README.md CHANGED
@@ -4,7 +4,7 @@ A powerful wrapper around DuckDB-WASM that provides React hooks and utilities fo
4
4
 
5
5
  ### React Integration & Type Safety
6
6
 
7
- - **React Hooks**: Seamless integration with React applications via `useSql`
7
+ - **React Hooks**: Seamless integration with React applications via `useSql` and `useDataTable`
8
8
  - **Runtime Validation**: Optional Zod schema validation for query results with type transformations
9
9
  - **Typed Row Accessors**: Type-safe row access with validation and multiple iteration methods
10
10
 
@@ -57,6 +57,55 @@ function UserList() {
57
57
 
58
58
  For more information and examples on using the `useSql` hook, see the [useSql API documentation](/api/duckdb/functions/useSql).
59
59
 
60
+ ### Monitoring WebSocket DuckDB Connections
61
+
62
+ `createWebSocketDuckDbConnector()` exposes the persistent WebSocket lifecycle
63
+ through `connectionStatus`, `subscribeConnectionStatus()`, and the optional
64
+ `onConnectionStatusChange` callback. Use this for live UI affordances such as
65
+ lost-connection dialogs. Call `reconnect()` to reopen the socket and rerun the
66
+ connector initialization SQL without destroying the connector instance.
67
+
68
+ ```tsx
69
+ import {createWebSocketDuckDbConnector} from '@sqlrooms/duckdb';
70
+
71
+ const connector = createWebSocketDuckDbConnector({
72
+ wsUrl: 'ws://localhost:4000',
73
+ onConnectionStatusChange: (status) => {
74
+ console.log('DuckDB WebSocket status:', status);
75
+ },
76
+ });
77
+
78
+ const unsubscribe = connector.subscribeConnectionStatus((status) => {
79
+ if (status === 'disconnected') {
80
+ console.warn('DuckDB WebSocket disconnected');
81
+ }
82
+ });
83
+
84
+ await connector.reconnect();
85
+ ```
86
+
87
+ ### Looking up Table Metadata
88
+
89
+ Use `useDataTable()` in React components or `db.findTable()` from the room
90
+ store. String references are parsed like SQL identifiers, so use quotes for
91
+ literal dots in table names.
92
+
93
+ ```tsx
94
+ import {useDataTable} from '@sqlrooms/duckdb';
95
+
96
+ function TableColumns() {
97
+ const table = useDataTable('"memory"."main"."earthquakes"');
98
+
99
+ return (
100
+ <ul>
101
+ {table?.columns.map((column) => (
102
+ <li key={column.name}>{column.name}</li>
103
+ ))}
104
+ </ul>
105
+ );
106
+ }
107
+ ```
108
+
60
109
  ### Using Zod for Runtime Validation
61
110
 
62
111
  ```tsx
@@ -207,11 +256,12 @@ function DatabaseManager() {
207
256
  ### Working with Qualified Table Names
208
257
 
209
258
  ```tsx
210
- import {makeQualifiedTableName} from '@sqlrooms/duckdb';
259
+ import {quoteTableReference, resolveTableReference} from '@sqlrooms/duckdb';
211
260
  import {useRoomStore} from './store';
212
261
  import {Button} from '@sqlrooms/ui';
213
262
 
214
263
  function QualifiedTableOps() {
264
+ const qualifyTableName = useRoomStore((state) => state.db.qualifyTableName);
215
265
  const createTableFromQuery = useRoomStore(
216
266
  (state) => state.db.createTableFromQuery,
217
267
  );
@@ -219,14 +269,21 @@ function QualifiedTableOps() {
219
269
  const checkTableExists = useRoomStore((state) => state.db.checkTableExists);
220
270
 
221
271
  const run = async () => {
222
- // Support for database.schema.table naming
223
- const qualifiedTable = makeQualifiedTableName({
272
+ // Store-aware qualification knows which database is the default.
273
+ const qualifiedTable = qualifyTableName({
224
274
  database: 'mydb',
225
275
  schema: 'public',
226
276
  table: 'users',
227
277
  });
278
+ // toString() is the canonical portable table ID; toFullString() includes
279
+ // the database when explicit catalog qualification is needed.
280
+ const tableSql = quoteTableReference(qualifiedTable.toString());
281
+ const resolved = resolveTableReference([{table: qualifiedTable}], 'users');
228
282
 
229
283
  await createTableFromQuery(qualifiedTable, 'SELECT * FROM source_table');
284
+ console.log('Quoted table reference:', tableSql);
285
+ console.log('Fully qualified reference:', qualifiedTable.toFullString());
286
+ console.log('Resolved table:', resolved.table?.table.toString());
230
287
  const tableExists = await checkTableExists(qualifiedTable);
231
288
  console.log('Table exists after create:', tableExists);
232
289
  await dropTable(qualifiedTable);
@@ -34,6 +34,13 @@ export type DuckDbSliceState = {
34
34
  schema: string;
35
35
  currentSchema: string | undefined;
36
36
  currentDatabase: string | undefined;
37
+ /**
38
+ * Create a context-aware qualified table name.
39
+ *
40
+ * String inputs are treated as table identifier names, not SQL references;
41
+ * use findTable() when resolving an existing table reference.
42
+ */
43
+ qualifyTableName(tableName: string | QualifiedTableNameInput): QualifiedTableName;
37
44
  /**
38
45
  * Cache of refreshed table schemas
39
46
  */
@@ -84,7 +91,7 @@ export type DuckDbSliceState = {
84
91
  */
85
92
  loadTableSchemas(filter?: LoadTableSchemasFilter): Promise<DataTable[]>;
86
93
  /**
87
- * @deprecated Use findTableByName instead
94
+ * @deprecated Use findTable instead
88
95
  */
89
96
  getTable(tableName: string): DataTable | undefined;
90
97
  /**
@@ -92,12 +99,18 @@ export type DuckDbSliceState = {
92
99
  */
93
100
  setTableRowCount(tableName: string | QualifiedTableName, rowCount: number): void;
94
101
  /**
95
- * Find a table by name in the last refreshed table schemas.
96
- * If no schema or database is provided, the table will be found in the current schema
97
- * and database (from last table schemas refresh).
98
- * @param tableName - The name of the table to find or a qualified table name.
102
+ * Find a table by reference in the last refreshed table schemas.
103
+ * String references are parsed as SQL identifiers, so dots separate
104
+ * database/schema/table segments unless quoted. If no schema or database is
105
+ * provided, the table is resolved in the current schema and database from
106
+ * the last schema refresh.
107
+ * @param tableName - The table reference to find.
99
108
  * @returns The table or undefined if not found.
100
109
  */
110
+ findTable(tableName: string | QualifiedTableName): DataTable | undefined;
111
+ /**
112
+ * @deprecated Use findTable instead
113
+ */
101
114
  findTableByName(tableName: string | QualifiedTableName): DataTable | undefined;
102
115
  /**
103
116
  * Refresh table schemas from the database.
@@ -192,6 +205,7 @@ export type DuckDbSliceState = {
192
205
  }>;
193
206
  };
194
207
  };
208
+ type QualifiedTableNameInput = Pick<QualifiedTableName, 'database' | 'schema' | 'table' | 'defaultDatabase'>;
195
209
  export type CreateDuckDbSliceProps = {
196
210
  connector?: DuckDbConnector;
197
211
  /**
@@ -222,4 +236,5 @@ export declare function createDuckDbSlice({ connector, loadTableSchemasFilter, l
222
236
  * @returns The selected value of type `T`
223
237
  */
224
238
  export declare function useStoreWithDuckDb<T>(selector: (state: BaseRoomStoreState & DuckDbSliceState) => T): T;
239
+ export {};
225
240
  //# sourceMappingURL=DuckDbSlice.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DuckDbSlice.d.ts","sourceRoot":"","sources":["../src/DuckDbSlice.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DuckDbSlice.d.ts","sourceRoot":"","sources":["../src/DuckDbSlice.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { createDbSchemaTrees, escapeVal, getColValAsNumber, isQualifiedTableName, joinStatements, makeQualifiedTableName, separateLastStatement, } from '@sqlrooms/duckdb-core';
1
+ import { createDbSchemaTrees, escapeVal, getColValAsNumber, isQualifiedTableName, joinStatements, makeQualifiedTableName, parseQualifiedSqlIdentifier, separateLastStatement, } from '@sqlrooms/duckdb-core';
2
2
  import { createSlice, registerCommandsForOwner, unregisterCommandsForOwner, useBaseRoomStore, } from '@sqlrooms/room-store';
3
3
  import * as arrow from 'apache-arrow';
4
4
  import deepEquals from 'fast-deep-equal';
@@ -15,7 +15,7 @@ const DUCKDB_TEMP_DATABASE = 'temp';
15
15
  * Hides `__sqlrooms_*` names and DuckDB's `temp` database.
16
16
  */
17
17
  export const defaultLoadTableSchemasFilter = (table) => {
18
- if (table.table?.startsWith(INTERNAL_SQLROOMS_PREFIX) ||
18
+ if (table.table.startsWith(INTERNAL_SQLROOMS_PREFIX) ||
19
19
  table.database?.startsWith(INTERNAL_SQLROOMS_PREFIX) ||
20
20
  table.schema?.startsWith(INTERNAL_SQLROOMS_PREFIX)) {
21
21
  return false;
@@ -95,17 +95,55 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
95
95
  return defaultLoadSchemaCatalogFilter(entry);
96
96
  }));
97
97
  return createSlice((set, get, store) => {
98
+ const parseTableReferenceParts = (tableName) => {
99
+ const parsed = parseQualifiedSqlIdentifier(tableName);
100
+ return parsed?.table
101
+ ? {
102
+ database: parsed.database,
103
+ schema: parsed.schema,
104
+ table: parsed.table,
105
+ }
106
+ : { table: tableName };
107
+ };
98
108
  /**
99
109
  * Internal helper to load a table schema by exact name, bypassing the visibility filter.
100
110
  * Used when performing exact lookups (e.g., checking if a specific table exists).
101
111
  */
102
112
  const loadTableSchemaByName = async (tableName) => {
103
- const qualifiedName = isQualifiedTableName(tableName)
104
- ? tableName
105
- : makeQualifiedTableName({ table: tableName });
113
+ const tableNameParts = typeof tableName === 'string'
114
+ ? parseTableReferenceParts(tableName)
115
+ : tableName;
116
+ const qualifiedName = get().db.qualifyTableName(tableNameParts);
106
117
  const connector = await get().db.getConnector();
107
- const [table] = await loadTableSchemas(connector, qualifiedName);
108
- return table;
118
+ const [table] = await loadTableSchemas(connector, {
119
+ database: qualifiedName.database,
120
+ schema: qualifiedName.schema,
121
+ table: qualifiedName.table,
122
+ defaultDatabase: get().db.currentDatabase,
123
+ });
124
+ return table &&
125
+ table.table.table === qualifiedName.table &&
126
+ (!qualifiedName.schema ||
127
+ table.table.schema === qualifiedName.schema) &&
128
+ (!qualifiedName.database ||
129
+ table.table.database === qualifiedName.database)
130
+ ? table
131
+ : undefined;
132
+ };
133
+ const throwIfUnresolvedExplicitDatabaseReference = (tableName, table) => {
134
+ if (table) {
135
+ return;
136
+ }
137
+ const database = typeof tableName === 'string'
138
+ ? parseTableReferenceParts(tableName).database
139
+ : tableName.database;
140
+ if (!database) {
141
+ return;
142
+ }
143
+ const qualifiedName = isQualifiedTableName(tableName)
144
+ ? get().db.qualifyTableName(tableName)
145
+ : get().db.qualifyTableName(parseTableReferenceParts(tableName));
146
+ throw new Error(`Relation "${qualifiedName}" not found.`);
109
147
  };
110
148
  return {
111
149
  db: {
@@ -118,6 +156,16 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
118
156
  tableRowCounts: {},
119
157
  schemaTrees: undefined,
120
158
  queryCache: {},
159
+ qualifyTableName(tableName) {
160
+ const { currentDatabase, currentSchema } = get().db;
161
+ const parts = typeof tableName === 'string' ? { table: tableName } : tableName;
162
+ return makeQualifiedTableName({
163
+ database: parts.database ?? currentDatabase,
164
+ schema: parts.schema ?? currentSchema,
165
+ table: parts.table,
166
+ defaultDatabase: parts.defaultDatabase ?? currentDatabase,
167
+ });
168
+ },
121
169
  setConnector: (connector) => {
122
170
  set(produce((state) => {
123
171
  state.config.dataSources = [];
@@ -172,14 +220,13 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
172
220
  async createTableFromQuery(tableName, query, options) {
173
221
  const { replace = true, temp = false, view = false, allowMultipleStatements = false, abortSignal, } = options || {};
174
222
  // For temp tables/views, DuckDB requires the "temp" database
175
- const baseQualifiedName = isQualifiedTableName(tableName)
176
- ? tableName
177
- : makeQualifiedTableName({ table: tableName });
223
+ const baseQualifiedName = get().db.qualifyTableName(tableName);
178
224
  const qualifiedName = temp
179
225
  ? makeQualifiedTableName({
180
226
  table: baseQualifiedName.table,
181
227
  schema: baseQualifiedName.schema,
182
228
  database: 'temp',
229
+ defaultDatabase: get().db.currentDatabase,
183
230
  })
184
231
  : baseQualifiedName;
185
232
  const connector = await get().db.getConnector();
@@ -233,14 +280,14 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
233
280
  * @deprecated Use .loadTableRowCount() instead
234
281
  */
235
282
  async getTableRowCount(table, schema = 'main') {
236
- return get().db.loadTableRowCount({ table, schema });
283
+ return get().db.loadTableRowCount(get().db.qualifyTableName({ table, schema }));
237
284
  },
238
285
  async loadTableRowCount(tableName) {
239
286
  const { schema, database, table } = typeof tableName === 'string'
240
- ? { table: tableName }
241
- : tableName || {};
287
+ ? get().db.qualifyTableName(parseTableReferenceParts(tableName))
288
+ : get().db.qualifyTableName(tableName);
242
289
  const connector = await get().db.getConnector();
243
- const result = await connector.query(`SELECT COUNT(*) FROM ${makeQualifiedTableName({
290
+ const result = await connector.query(`SELECT COUNT(*) FROM ${get().db.qualifyTableName({
244
291
  schema,
245
292
  database,
246
293
  table,
@@ -258,19 +305,23 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
258
305
  return loadTableSchemas(connector, {
259
306
  ...filter,
260
307
  filterFunction: loadTableSchemasFilter,
308
+ defaultDatabase: get().db.currentDatabase,
261
309
  });
262
310
  },
263
311
  async checkTableExists(tableName) {
264
- const table = await loadTableSchemaByName(tableName);
312
+ const table = get().db.findTable(tableName) ??
313
+ (await loadTableSchemaByName(tableName));
265
314
  return Boolean(table);
266
315
  },
267
316
  async dropRelation(tableName) {
268
317
  const connector = await get().db.getConnector();
269
- const qualifiedTable = isQualifiedTableName(tableName)
270
- ? tableName
271
- : makeQualifiedTableName({ table: tableName });
272
- const table = get().db.findTableByName(qualifiedTable) ??
273
- (await loadTableSchemaByName(qualifiedTable));
318
+ const table = get().db.findTable(tableName) ??
319
+ (await loadTableSchemaByName(tableName));
320
+ throwIfUnresolvedExplicitDatabaseReference(tableName, table);
321
+ const qualifiedTable = table?.table ??
322
+ (isQualifiedTableName(tableName)
323
+ ? get().db.qualifyTableName(tableName)
324
+ : get().db.qualifyTableName(parseTableReferenceParts(tableName)));
274
325
  const isView = table?.isView;
275
326
  if (isView) {
276
327
  await connector.query(`DROP VIEW IF EXISTS ${qualifiedTable};`);
@@ -282,11 +333,13 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
282
333
  },
283
334
  async dropTable(tableName) {
284
335
  const connector = await get().db.getConnector();
285
- const qualifiedTable = isQualifiedTableName(tableName)
286
- ? tableName
287
- : makeQualifiedTableName({ table: tableName });
288
- const table = get().db.findTableByName(qualifiedTable) ??
289
- (await loadTableSchemaByName(qualifiedTable));
336
+ const table = get().db.findTable(tableName) ??
337
+ (await loadTableSchemaByName(tableName));
338
+ throwIfUnresolvedExplicitDatabaseReference(tableName, table);
339
+ const qualifiedTable = table?.table ??
340
+ (isQualifiedTableName(tableName)
341
+ ? get().db.qualifyTableName(tableName)
342
+ : get().db.qualifyTableName(parseTableReferenceParts(tableName)));
290
343
  if (table?.isView) {
291
344
  throw new Error(`"${qualifiedTable}" is a view. Use dropRelation() to remove views.`);
292
345
  }
@@ -294,9 +347,7 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
294
347
  get().db.refreshTableSchemas();
295
348
  },
296
349
  async addTable(tableName, data) {
297
- const qualifiedName = isQualifiedTableName(tableName)
298
- ? tableName
299
- : makeQualifiedTableName({ table: tableName });
350
+ const qualifiedName = get().db.qualifyTableName(tableName);
300
351
  const { db } = get();
301
352
  if (data instanceof arrow.Table) {
302
353
  // TODO: make sure the table is replaced
@@ -318,27 +369,51 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
318
369
  return newTable;
319
370
  },
320
371
  async setTableRowCount(tableName, rowCount) {
321
- const qualifiedName = isQualifiedTableName(tableName)
322
- ? tableName
323
- : makeQualifiedTableName({ table: tableName });
372
+ const qualifiedName = get().db.qualifyTableName(tableName);
324
373
  set((state) => produce(state, (draft) => {
325
374
  draft.db.tableRowCounts[qualifiedName.toString()] = rowCount;
326
375
  }));
327
376
  },
328
377
  getTable(tableName) {
329
- return get().db.findTableByName(tableName);
378
+ return get().db.findTable(tableName);
330
379
  },
331
- findTableByName(tableName) {
332
- const { table, schema, database } = {
333
- schema: get().db.currentSchema,
334
- database: get().db.currentDatabase,
335
- ...(typeof tableName === 'string'
336
- ? { table: tableName }
337
- : tableName),
380
+ findTable(tableName) {
381
+ const { currentSchema, currentDatabase, tables } = get().db;
382
+ const findMatchingTable = ({ table, schema, database, }) => table
383
+ ? tables.find((t) => t.table.table === table &&
384
+ (!schema || t.table.schema === schema) &&
385
+ (!database || t.table.database === database))
386
+ : undefined;
387
+ const findUniqueMatchingTable = ({ table, schema, }) => {
388
+ if (!table)
389
+ return undefined;
390
+ const matches = tables.filter((t) => t.table.table === table &&
391
+ (!schema || t.table.schema === schema));
392
+ return matches.length === 1 ? matches[0] : undefined;
393
+ };
394
+ const resolvedTableName = typeof tableName === 'string'
395
+ ? (parseQualifiedSqlIdentifier(tableName) ?? {})
396
+ : tableName;
397
+ const { table, schema, database, defaultDatabase } = {
398
+ schema: currentSchema,
399
+ database: currentDatabase,
400
+ ...resolvedTableName,
338
401
  };
339
- return get().db.tables.find((t) => t.table.table === table &&
340
- (!schema || t.table.schema === schema) &&
341
- (!database || t.table.database === database));
402
+ const exactMatch = findMatchingTable({ table, schema, database });
403
+ if (exactMatch)
404
+ return exactMatch;
405
+ const isStaleDefaultDatabaseReference = typeof tableName !== 'string' &&
406
+ database &&
407
+ defaultDatabase &&
408
+ database === defaultDatabase &&
409
+ database !== currentDatabase;
410
+ if (isStaleDefaultDatabaseReference) {
411
+ return findUniqueMatchingTable({ table, schema });
412
+ }
413
+ return undefined;
414
+ },
415
+ findTableByName(tableName) {
416
+ return get().db.findTable(tableName);
342
417
  },
343
418
  async refreshTableSchemas() {
344
419
  if (refreshPromise) {
@@ -355,16 +430,23 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
355
430
  pendingSchemaRefresh = false;
356
431
  const connector = await get().db.getConnector();
357
432
  const result = await connector.query(`SELECT current_schema() AS schema, current_database() AS database`);
433
+ const currentSchemaValue = result.getChild('schema')?.get(0);
434
+ const currentDatabaseValue = result
435
+ .getChild('database')
436
+ ?.get(0);
437
+ const currentSchema = currentSchemaValue == null
438
+ ? undefined
439
+ : String(currentSchemaValue);
440
+ const currentDatabase = currentDatabaseValue == null
441
+ ? undefined
442
+ : String(currentDatabaseValue);
358
443
  set((state) => produce(state, (draft) => {
359
- draft.db.currentSchema = result
360
- .getChild('schema')
361
- ?.get(0);
362
- draft.db.currentDatabase = result
363
- .getChild('database')
364
- ?.get(0);
444
+ draft.db.currentSchema = currentSchema;
445
+ draft.db.currentDatabase = currentDatabase;
365
446
  }));
366
447
  schemasWithTables = await loadSchemaCatalog(connector, {
367
448
  filterFunction: effectiveSchemaCatalogFilter,
449
+ defaultDatabase: currentDatabase,
368
450
  });
369
451
  } while (pendingSchemaRefresh);
370
452
  const newTables = schemasWithTables.flatMap((s) => s.tables);
@@ -414,7 +496,12 @@ export function createDuckDbSlice({ connector = createWasmDuckDbConnector(), loa
414
496
  set((state) => produce(state, (draft) => {
415
497
  draft.db.queryCache[queryKey] = queryHandle;
416
498
  }));
417
- queryHandle.result.finally(() => {
499
+ queryHandle.result
500
+ .catch(() => {
501
+ // Prevent unhandled promise rejection warnings.
502
+ // Callers handle errors via await/try-catch.
503
+ })
504
+ .finally(() => {
418
505
  // remove from cache after completion
419
506
  set((state) => produce(state, (draft) => {
420
507
  delete draft.db.queryCache[queryKey];