@sqlrooms/duckdb 0.8.1 → 0.9.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.
Files changed (58) hide show
  1. package/dist/DuckDbSlice.d.ts +110 -0
  2. package/dist/DuckDbSlice.d.ts.map +1 -0
  3. package/dist/DuckDbSlice.js +157 -0
  4. package/dist/DuckDbSlice.js.map +1 -0
  5. package/dist/connectors/DuckDbConnector.d.ts +40 -0
  6. package/dist/connectors/DuckDbConnector.d.ts.map +1 -0
  7. package/dist/connectors/DuckDbConnector.js +2 -0
  8. package/dist/connectors/DuckDbConnector.js.map +1 -0
  9. package/dist/connectors/WasmDuckDbConnector.d.ts +44 -0
  10. package/dist/connectors/WasmDuckDbConnector.d.ts.map +1 -0
  11. package/dist/connectors/WasmDuckDbConnector.js +210 -0
  12. package/dist/connectors/WasmDuckDbConnector.js.map +1 -0
  13. package/dist/connectors/load/create.d.ts +33 -0
  14. package/dist/connectors/load/create.d.ts.map +1 -0
  15. package/dist/connectors/load/create.js +33 -0
  16. package/dist/connectors/load/create.js.map +1 -0
  17. package/dist/connectors/load/load.d.ts +55 -0
  18. package/dist/connectors/load/load.d.ts.map +1 -0
  19. package/dist/connectors/load/load.js +147 -0
  20. package/dist/connectors/load/load.js.map +1 -0
  21. package/dist/connectors/load/sql-from.d.ts.map +1 -0
  22. package/dist/{sql-from.js → connectors/load/sql-from.js} +1 -1
  23. package/dist/connectors/load/sql-from.js.map +1 -0
  24. package/dist/duckdb-utils.d.ts +47 -0
  25. package/dist/duckdb-utils.d.ts.map +1 -0
  26. package/dist/duckdb-utils.js +65 -0
  27. package/dist/duckdb-utils.js.map +1 -0
  28. package/dist/exportToCsv.d.ts +3 -1
  29. package/dist/exportToCsv.d.ts.map +1 -1
  30. package/dist/exportToCsv.js +29 -24
  31. package/dist/exportToCsv.js.map +1 -1
  32. package/dist/index.d.ts +6 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +6 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/useDuckDb.d.ts +2 -31
  37. package/dist/useDuckDb.d.ts.map +1 -1
  38. package/dist/useDuckDb.js +3 -229
  39. package/dist/useDuckDb.js.map +1 -1
  40. package/dist/useSql.d.ts.map +1 -1
  41. package/dist/useSql.js +4 -3
  42. package/dist/useSql.js.map +1 -1
  43. package/package.json +8 -4
  44. package/dist/__tests__/arrow-utils.test.d.ts +0 -2
  45. package/dist/__tests__/arrow-utils.test.d.ts.map +0 -1
  46. package/dist/__tests__/arrow-utils.test.js +0 -187
  47. package/dist/__tests__/arrow-utils.test.js.map +0 -1
  48. package/dist/__tests__/sql-from.test.d.ts +0 -2
  49. package/dist/__tests__/sql-from.test.d.ts.map +0 -1
  50. package/dist/__tests__/sql-from.test.js +0 -71
  51. package/dist/__tests__/sql-from.test.js.map +0 -1
  52. package/dist/duckdb.d.ts +0 -49
  53. package/dist/duckdb.d.ts.map +0 -1
  54. package/dist/duckdb.js +0 -82
  55. package/dist/duckdb.js.map +0 -1
  56. package/dist/sql-from.d.ts.map +0 -1
  57. package/dist/sql-from.js.map +0 -1
  58. /package/dist/{sql-from.d.ts → connectors/load/sql-from.d.ts} +0 -0
@@ -0,0 +1,110 @@
1
+ import { ProjectState } from '@sqlrooms/project';
2
+ import * as arrow from 'apache-arrow';
3
+ import { z } from 'zod';
4
+ import { StateCreator } from 'zustand';
5
+ import { DuckDbConnector } from './connectors/DuckDbConnector';
6
+ import { DataTable } from './types';
7
+ export declare const DuckDbSliceConfig: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
8
+ export type DuckDbSliceConfig = z.infer<typeof DuckDbSliceConfig>;
9
+ export declare function createDefaultDuckDbConfig(): DuckDbSliceConfig;
10
+ /**
11
+ * State and actions for the DuckDB slice
12
+ */
13
+ export type DuckDbSliceState = {
14
+ db: {
15
+ /**
16
+ * The DuckDB connector instance
17
+ */
18
+ connector: DuckDbConnector;
19
+ schema: string;
20
+ tables: DataTable[];
21
+ tableRowCounts: {
22
+ [tableName: string]: number;
23
+ };
24
+ /**
25
+ * Set a new DuckDB connector
26
+ */
27
+ setConnector: (connector: DuckDbConnector) => void;
28
+ /**
29
+ * Initialize the connector (creates a WasmDuckDbConnector if none exists)
30
+ */
31
+ initialize: () => Promise<void>;
32
+ /**
33
+ * Close and clean up the connector
34
+ */
35
+ destroy: () => Promise<void>;
36
+ /**
37
+ * Add a table to the project.
38
+ * @param tableName - The name of the table to add.
39
+ * @param data - The data to add to the table: an arrow table or an array of records.
40
+ * @returns A promise that resolves to the table that was added.
41
+ */
42
+ addTable(tableName: string, data: arrow.Table | Record<string, unknown>[]): Promise<DataTable>;
43
+ getTable(tableName: string): DataTable | undefined;
44
+ setTableRowCount(tableName: string, rowCount: number): void;
45
+ findTableByName(tableName: string): DataTable | undefined;
46
+ /**
47
+ * Refresh table schemas from the database.
48
+ * @returns A promise that resolves to the updated tables.
49
+ */
50
+ refreshTableSchemas(): Promise<DataTable[]>;
51
+ /**
52
+ * Get the connector. If it's not initialized, it will be initialized.
53
+ */
54
+ getConnector: () => Promise<DuckDbConnector>;
55
+ /**
56
+ * Get the tables in the database
57
+ *
58
+ * @param schema - The schema to get the tables from. Defaults to 'main'. Pass '*' to get all tables.
59
+ * @returns The tables in the database.
60
+ */
61
+ getTables: (schema?: string) => Promise<string[]>;
62
+ /**
63
+ * Get the row count of a table
64
+ */
65
+ getTableRowCount: (tableName: string, schema?: string) => Promise<number>;
66
+ /**
67
+ * Get the schema of a table
68
+ */
69
+ getTableSchema: (tableName: string, schema?: string) => Promise<DataTable>;
70
+ /**
71
+ * Get the schemas of all tables in the database.
72
+ *
73
+ * @param schema - The schema to get the tables from. Defaults to 'main'. Pass '*' to get all schemas.
74
+ * @returns The schemas of all tables in the database.
75
+ */
76
+ getTableSchemas: (schema?: string) => Promise<DataTable[]>;
77
+ /**
78
+ * Check if a table exists
79
+ */
80
+ checkTableExists: (tableName: string, schema?: string) => Promise<boolean>;
81
+ /**
82
+ * Drop a table
83
+ */
84
+ dropTable: (tableName: string) => Promise<void>;
85
+ /**
86
+ * Check if a table exists
87
+ */
88
+ tableExists: (tableName: string, schema?: string) => Promise<boolean>;
89
+ /**
90
+ * Create a table from a query.
91
+ * @param tableName - The name of the table to create.
92
+ * @param query - The query to create the table from.
93
+ * @returns The table that was created.
94
+ */
95
+ createTableFromQuery: (tableName: string, query: string) => Promise<{
96
+ tableName: string;
97
+ rowCount: number;
98
+ }>;
99
+ };
100
+ };
101
+ /**
102
+ * Create a DuckDB slice for managing the connector
103
+ */
104
+ export declare function createDuckDbSlice({ connector, }: {
105
+ connector?: DuckDbConnector;
106
+ }): StateCreator<DuckDbSliceState>;
107
+ type ProjectStateWithDuckDb = ProjectState<DuckDbSliceConfig> & DuckDbSliceState;
108
+ export declare function useStoreWithDuckDb<T>(selector: (state: ProjectStateWithDuckDb) => T): T;
109
+ export {};
110
+ //# sourceMappingURL=DuckDbSlice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuckDbSlice.d.ts","sourceRoot":"","sources":["../src/DuckDbSlice.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EAEb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAC;AACrC,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAG7D,OAAO,EAAC,SAAS,EAAc,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,iBAAiB,gDAE5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,wBAAgB,yBAAyB,IAAI,iBAAiB,CAI7D;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE;QACF;;WAEG;QACH,SAAS,EAAE,eAAe,CAAC;QAC3B,MAAM,EAAE,MAAM,CAAC;QAEf,MAAM,EAAE,SAAS,EAAE,CAAC;QACpB,cAAc,EAAE;YAAC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAA;SAAC,CAAC;QAE9C;;WAEG;QACH,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;QAEnD;;WAEG;QACH,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QAEhC;;WAEG;QACH,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QAE7B;;;;;WAKG;QACH,QAAQ,CACN,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAC5C,OAAO,CAAC,SAAS,CAAC,CAAC;QACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;QACnD,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5D,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;QAC1D;;;WAGG;QACH,mBAAmB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;QAC5C;;WAEG;QACH,YAAY,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;QAE7C;;;;;WAKG;QACH,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAElD;;WAEG;QACH,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;QAE1E;;WAEG;QACH,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;QAE3E;;;;;WAKG;QACH,eAAe,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;QAE3D;;WAEG;QACH,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;QAE3E;;WAEG;QACH,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QAEhD;;WAEG;QACH,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;QAEtE;;;;;WAKG;QACH,oBAAoB,EAAE,CACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,KACV,OAAO,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAC,CAAC,CAAC;KACrD,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAqC,GACtC,EAAE;IACD,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B,GAAG,YAAY,CAAC,gBAAgB,CAAC,CA2LjC;AAED,KAAK,sBAAsB,GAAG,YAAY,CAAC,iBAAiB,CAAC,GAC3D,gBAAgB,CAAC;AAEnB,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,CAAC,GAC7C,CAAC,CAMH"}
@@ -0,0 +1,157 @@
1
+ import { createBaseSlice, useBaseProjectStore, } from '@sqlrooms/project';
2
+ import * as arrow from 'apache-arrow';
3
+ import { produce } from 'immer';
4
+ import deepEquals from 'fast-deep-equal';
5
+ import { z } from 'zod';
6
+ import { WasmDuckDbConnector } from './connectors/WasmDuckDbConnector';
7
+ import { getColValAsNumber } from './duckdb-utils';
8
+ export const DuckDbSliceConfig = z.object({
9
+ // nothing yet
10
+ });
11
+ export function createDefaultDuckDbConfig() {
12
+ return {
13
+ // nothing yet
14
+ };
15
+ }
16
+ /**
17
+ * Create a DuckDB slice for managing the connector
18
+ */
19
+ export function createDuckDbSlice({ connector = new WasmDuckDbConnector(), }) {
20
+ return createBaseSlice((set, get) => {
21
+ return {
22
+ db: {
23
+ connector, // Will be initialized during init
24
+ schema: 'main',
25
+ tables: [],
26
+ tableRowCounts: {},
27
+ setConnector: (connector) => {
28
+ set(produce((state) => {
29
+ state.config.dataSources = [];
30
+ state.db.connector = connector;
31
+ }));
32
+ },
33
+ initialize: async () => {
34
+ await get().db.connector.initialize();
35
+ },
36
+ getConnector: async () => {
37
+ await get().db.initialize();
38
+ return get().db.connector;
39
+ },
40
+ destroy: async () => {
41
+ try {
42
+ if (get().db.connector) {
43
+ await get().db.connector.destroy();
44
+ }
45
+ }
46
+ catch (err) {
47
+ console.error('Error during DuckDB shutdown:', err);
48
+ }
49
+ },
50
+ async createTableFromQuery(tableName, query) {
51
+ const connector = await get().db.getConnector();
52
+ const rowCount = getColValAsNumber(await connector.query(`CREATE OR REPLACE TABLE main.${tableName} AS (
53
+ ${query}
54
+ )`));
55
+ return { tableName, rowCount };
56
+ },
57
+ async getTables(schema = 'main') {
58
+ const connector = await get().db.getConnector();
59
+ const tablesResults = await connector.query(`SELECT * FROM information_schema.tables
60
+ ${schema === '*' ? '' : `WHERE table_schema = '${schema}'`}
61
+ ORDER BY table_name`);
62
+ const tableNames = [];
63
+ for (let i = 0; i < tablesResults.numRows; i++) {
64
+ tableNames.push(tablesResults.getChild('table_name')?.get(i));
65
+ }
66
+ return tableNames;
67
+ },
68
+ async getTableSchema(tableName, schema = 'main') {
69
+ const connector = await get().db.getConnector();
70
+ const describeResults = await connector.query(`DESCRIBE ${schema}.${tableName}`);
71
+ const columnNames = describeResults.getChild('column_name');
72
+ const columnTypes = describeResults.getChild('column_type');
73
+ const columns = [];
74
+ for (let di = 0; di < describeResults.numRows; di++) {
75
+ const columnName = columnNames?.get(di);
76
+ const columnType = columnTypes?.get(di);
77
+ columns.push({ name: columnName, type: columnType });
78
+ }
79
+ return {
80
+ tableName,
81
+ columns,
82
+ };
83
+ },
84
+ async getTableRowCount(tableName, schema = 'main') {
85
+ const connector = await get().db.getConnector();
86
+ const result = await connector.query(`SELECT COUNT(*) FROM ${schema}.${tableName}`);
87
+ return getColValAsNumber(result);
88
+ },
89
+ async getTableSchemas(schema = 'main') {
90
+ const tableNames = await get().db.getTables(schema);
91
+ const tablesInfo = [];
92
+ for (const tableName of tableNames) {
93
+ tablesInfo.push(await get().db.getTableSchema(tableName, schema));
94
+ }
95
+ return tablesInfo;
96
+ },
97
+ async checkTableExists(tableName, schema = 'main') {
98
+ const connector = await get().db.getConnector();
99
+ const res = await connector.query(`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${schema}' AND table_name = '${tableName}'`);
100
+ return getColValAsNumber(res) > 0;
101
+ },
102
+ async dropTable(tableName) {
103
+ const connector = await get().db.getConnector();
104
+ await connector.query(`DROP TABLE IF EXISTS ${tableName};`);
105
+ },
106
+ async tableExists(tableName, schema = 'main') {
107
+ const connector = await get().db.getConnector();
108
+ const result = await connector.query(`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${schema}' AND table_name = '${tableName}'`);
109
+ return Number(result.getChildAt(0)?.get(0)) > 0;
110
+ },
111
+ async addTable(tableName, data) {
112
+ const { tables } = get().db;
113
+ const table = tables.find((t) => t.tableName === tableName);
114
+ if (table) {
115
+ return table;
116
+ }
117
+ const { db } = get();
118
+ if (data instanceof arrow.Table) {
119
+ await db.connector.loadArrow(data, tableName);
120
+ }
121
+ else {
122
+ await db.connector.loadObjects(data, tableName);
123
+ }
124
+ const newTable = await db.getTableSchema(tableName);
125
+ set((state) => produce(state, (draft) => {
126
+ draft.db.tables.push(newTable);
127
+ }));
128
+ return newTable;
129
+ },
130
+ setTableRowCount: (tableName, rowCount) => set((state) => produce(state, (draft) => {
131
+ draft.db.tableRowCounts[tableName] = rowCount;
132
+ })),
133
+ getTable(tableName) {
134
+ return get().db.tables.find((t) => t.tableName === tableName);
135
+ },
136
+ findTableByName(tableName) {
137
+ return get().db.tables.find((t) => t.tableName === tableName);
138
+ },
139
+ async refreshTableSchemas() {
140
+ const newTables = await get().db.getTableSchemas();
141
+ const currentTables = get().db.tables;
142
+ // Only update if there's an actual change in the schemas
143
+ if (!deepEquals(newTables, currentTables)) {
144
+ set((state) => produce(state, (draft) => {
145
+ draft.db.tables = newTables;
146
+ }));
147
+ }
148
+ return newTables;
149
+ },
150
+ },
151
+ };
152
+ });
153
+ }
154
+ export function useStoreWithDuckDb(selector) {
155
+ return useBaseProjectStore((state) => selector(state));
156
+ }
157
+ //# sourceMappingURL=DuckDbSlice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuckDbSlice.js","sourceRoot":"","sources":["../src/DuckDbSlice.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createBaseSlice,\n ProjectState,\n useBaseProjectStore,\n} from '@sqlrooms/project';\nimport * as arrow from 'apache-arrow';\nimport {produce} from 'immer';\nimport deepEquals from 'fast-deep-equal';\nimport {z} from 'zod';\nimport {StateCreator} from 'zustand';\nimport {DuckDbConnector} from './connectors/DuckDbConnector';\nimport {WasmDuckDbConnector} from './connectors/WasmDuckDbConnector';\nimport {getColValAsNumber} from './duckdb-utils';\nimport {DataTable, TableColumn} from './types';\n\nexport const DuckDbSliceConfig = z.object({\n // nothing yet\n});\nexport type DuckDbSliceConfig = z.infer<typeof DuckDbSliceConfig>;\n\nexport function createDefaultDuckDbConfig(): DuckDbSliceConfig {\n return {\n // nothing yet\n };\n}\n\n/**\n * State and actions for the DuckDB slice\n */\nexport type DuckDbSliceState = {\n db: {\n /**\n * The DuckDB connector instance\n */\n connector: DuckDbConnector;\n schema: string;\n\n tables: DataTable[];\n tableRowCounts: {[tableName: string]: number};\n\n /**\n * Set a new DuckDB connector\n */\n setConnector: (connector: DuckDbConnector) => void;\n\n /**\n * Initialize the connector (creates a WasmDuckDbConnector if none exists)\n */\n initialize: () => Promise<void>;\n\n /**\n * Close and clean up the connector\n */\n destroy: () => Promise<void>;\n\n /**\n * Add a table to the project.\n * @param tableName - The name of the table to add.\n * @param data - The data to add to the table: an arrow table or an array of records.\n * @returns A promise that resolves to the table that was added.\n */\n addTable(\n tableName: string,\n data: arrow.Table | Record<string, unknown>[],\n ): Promise<DataTable>;\n getTable(tableName: string): DataTable | undefined;\n setTableRowCount(tableName: string, rowCount: number): void;\n findTableByName(tableName: string): DataTable | undefined;\n /**\n * Refresh table schemas from the database.\n * @returns A promise that resolves to the updated tables.\n */\n refreshTableSchemas(): Promise<DataTable[]>;\n /**\n * Get the connector. If it's not initialized, it will be initialized.\n */\n getConnector: () => Promise<DuckDbConnector>;\n\n /**\n * Get the tables in the database\n *\n * @param schema - The schema to get the tables from. Defaults to 'main'. Pass '*' to get all tables.\n * @returns The tables in the database.\n */\n getTables: (schema?: string) => Promise<string[]>;\n\n /**\n * Get the row count of a table\n */\n getTableRowCount: (tableName: string, schema?: string) => Promise<number>;\n\n /**\n * Get the schema of a table\n */\n getTableSchema: (tableName: string, schema?: string) => Promise<DataTable>;\n\n /**\n * Get the schemas of all tables in the database.\n *\n * @param schema - The schema to get the tables from. Defaults to 'main'. Pass '*' to get all schemas.\n * @returns The schemas of all tables in the database.\n */\n getTableSchemas: (schema?: string) => Promise<DataTable[]>;\n\n /**\n * Check if a table exists\n */\n checkTableExists: (tableName: string, schema?: string) => Promise<boolean>;\n\n /**\n * Drop a table\n */\n dropTable: (tableName: string) => Promise<void>;\n\n /**\n * Check if a table exists\n */\n tableExists: (tableName: string, schema?: string) => Promise<boolean>;\n\n /**\n * Create a table from a query.\n * @param tableName - The name of the table to create.\n * @param query - The query to create the table from.\n * @returns The table that was created.\n */\n createTableFromQuery: (\n tableName: string,\n query: string,\n ) => Promise<{tableName: string; rowCount: number}>;\n };\n};\n\n/**\n * Create a DuckDB slice for managing the connector\n */\nexport function createDuckDbSlice({\n connector = new WasmDuckDbConnector(),\n}: {\n connector?: DuckDbConnector;\n}): StateCreator<DuckDbSliceState> {\n return createBaseSlice<DuckDbSliceConfig, DuckDbSliceState>((set, get) => {\n return {\n db: {\n connector, // Will be initialized during init\n schema: 'main',\n tables: [],\n tableRowCounts: {},\n\n setConnector: (connector: DuckDbConnector) => {\n set(\n produce((state) => {\n state.config.dataSources = [];\n state.db.connector = connector;\n }),\n );\n },\n\n initialize: async () => {\n await get().db.connector.initialize();\n },\n\n getConnector: async () => {\n await get().db.initialize();\n return get().db.connector;\n },\n\n destroy: async () => {\n try {\n if (get().db.connector) {\n await get().db.connector.destroy();\n }\n } catch (err) {\n console.error('Error during DuckDB shutdown:', err);\n }\n },\n\n async createTableFromQuery(tableName: string, query: string) {\n const connector = await get().db.getConnector();\n const rowCount = getColValAsNumber(\n await connector.query(\n `CREATE OR REPLACE TABLE main.${tableName} AS (\n ${query}\n )`,\n ),\n );\n return {tableName, rowCount};\n },\n\n async getTables(schema = 'main'): Promise<string[]> {\n const connector = await get().db.getConnector();\n const tablesResults = await connector.query(\n `SELECT * FROM information_schema.tables \n ${schema === '*' ? '' : `WHERE table_schema = '${schema}'`}\n ORDER BY table_name`,\n );\n const tableNames: string[] = [];\n for (let i = 0; i < tablesResults.numRows; i++) {\n tableNames.push(tablesResults.getChild('table_name')?.get(i));\n }\n return tableNames;\n },\n\n async getTableSchema(\n tableName: string,\n schema = 'main',\n ): Promise<DataTable> {\n const connector = await get().db.getConnector();\n const describeResults = await connector.query(\n `DESCRIBE ${schema}.${tableName}`,\n );\n const columnNames = describeResults.getChild('column_name');\n const columnTypes = describeResults.getChild('column_type');\n const columns: TableColumn[] = [];\n for (let di = 0; di < describeResults.numRows; di++) {\n const columnName = columnNames?.get(di);\n const columnType = columnTypes?.get(di);\n columns.push({name: columnName, type: columnType});\n }\n return {\n tableName,\n columns,\n };\n },\n\n async getTableRowCount(\n tableName: string,\n schema = 'main',\n ): Promise<number> {\n const connector = await get().db.getConnector();\n const result = await connector.query(\n `SELECT COUNT(*) FROM ${schema}.${tableName}`,\n );\n return getColValAsNumber(result);\n },\n\n async getTableSchemas(schema = 'main'): Promise<DataTable[]> {\n const tableNames = await get().db.getTables(schema);\n const tablesInfo: DataTable[] = [];\n for (const tableName of tableNames) {\n tablesInfo.push(await get().db.getTableSchema(tableName, schema));\n }\n return tablesInfo;\n },\n\n async checkTableExists(\n tableName: string,\n schema = 'main',\n ): Promise<boolean> {\n const connector = await get().db.getConnector();\n const res = await connector.query(\n `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${schema}' AND table_name = '${tableName}'`,\n );\n return getColValAsNumber(res) > 0;\n },\n\n async dropTable(tableName: string): Promise<void> {\n const connector = await get().db.getConnector();\n await connector.query(`DROP TABLE IF EXISTS ${tableName};`);\n },\n\n async tableExists(\n tableName: string,\n schema = 'main',\n ): Promise<boolean> {\n const connector = await get().db.getConnector();\n const result = await connector.query(\n `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${schema}' AND table_name = '${tableName}'`,\n );\n return Number(result.getChildAt(0)?.get(0)) > 0;\n },\n\n async addTable(tableName, data) {\n const {tables} = get().db;\n const table = tables.find((t) => t.tableName === tableName);\n if (table) {\n return table;\n }\n\n const {db} = get();\n if (data instanceof arrow.Table) {\n await db.connector.loadArrow(data, tableName);\n } else {\n await db.connector.loadObjects(data, tableName);\n }\n const newTable = await db.getTableSchema(tableName);\n\n set((state) =>\n produce(state, (draft) => {\n draft.db.tables.push(newTable);\n }),\n );\n return newTable;\n },\n\n setTableRowCount: (tableName, rowCount) =>\n set((state) =>\n produce(state, (draft) => {\n draft.db.tableRowCounts[tableName] = rowCount;\n }),\n ),\n\n getTable(tableName) {\n return get().db.tables.find((t) => t.tableName === tableName);\n },\n\n findTableByName(tableName: string) {\n return get().db.tables.find((t) => t.tableName === tableName);\n },\n\n async refreshTableSchemas(): Promise<DataTable[]> {\n const newTables = await get().db.getTableSchemas();\n const currentTables = get().db.tables;\n\n // Only update if there's an actual change in the schemas\n if (!deepEquals(newTables, currentTables)) {\n set((state) =>\n produce(state, (draft) => {\n draft.db.tables = newTables;\n }),\n );\n }\n return newTables;\n },\n },\n };\n });\n}\n\ntype ProjectStateWithDuckDb = ProjectState<DuckDbSliceConfig> &\n DuckDbSliceState;\n\nexport function useStoreWithDuckDb<T>(\n selector: (state: ProjectStateWithDuckDb) => T,\n): T {\n return useBaseProjectStore<\n DuckDbSliceConfig,\n ProjectState<DuckDbSliceConfig>,\n T\n >((state) => selector(state as unknown as ProjectStateWithDuckDb));\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { LoadFileOptions, StandardLoadOptions } from '@sqlrooms/project-config';
2
+ import * as arrow from 'apache-arrow';
3
+ export interface DuckDbConnector {
4
+ /**
5
+ * Initialize the connector
6
+ */
7
+ initialize(): Promise<void>;
8
+ /**
9
+ * Destroy the connector and clean up resources
10
+ */
11
+ destroy(): Promise<void>;
12
+ /**
13
+ * Execute a SQL query and return the result as an Arrow table
14
+ * @param query SQL query to execute
15
+ */
16
+ query(query: string): Promise<arrow.Table>;
17
+ /**
18
+ * Load a file into DuckDB and create a table
19
+ * @param fileName - Path to the file to load
20
+ * @param tableName - Name of the table to create
21
+ * @param opts - Load options
22
+ */
23
+ loadFile(fileName: string | File, tableName: string, opts?: LoadFileOptions): Promise<void>;
24
+ /**
25
+ * Load an arrow table or an arrow IPC stream into DuckDB
26
+ * @param table - Arrow table or arrow IPC stream to load
27
+ * @param tableName - Name of the table to create
28
+ */
29
+ loadArrow(table: arrow.Table | Uint8Array, tableName: string, opts?: {
30
+ schema?: string;
31
+ }): Promise<void>;
32
+ /**
33
+ * Load JavaScript objects into DuckDB
34
+ * @param data - Array of objects to load
35
+ * @param tableName - Name of the table to create
36
+ * @param opts - Load options
37
+ */
38
+ loadObjects(data: Record<string, unknown>[], tableName: string, opts?: StandardLoadOptions): Promise<void>;
39
+ }
40
+ //# sourceMappingURL=DuckDbConnector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuckDbConnector.d.ts","sourceRoot":"","sources":["../../src/connectors/DuckDbConnector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAE,mBAAmB,EAAC,MAAM,0BAA0B,CAAC;AAC9E,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5B;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE3C;;;;;OAKG;IACH,QAAQ,CACN,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,eAAe,GACrB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;OAIG;IACH,SAAS,CACP,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,UAAU,EAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAC,GACvB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;OAKG;IACH,WAAW,CACT,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=DuckDbConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuckDbConnector.js","sourceRoot":"","sources":["../../src/connectors/DuckDbConnector.ts"],"names":[],"mappings":"","sourcesContent":["import {LoadFileOptions, StandardLoadOptions} from '@sqlrooms/project-config';\nimport * as arrow from 'apache-arrow';\n\nexport interface DuckDbConnector {\n /**\n * Initialize the connector\n */\n initialize(): Promise<void>;\n\n /**\n * Destroy the connector and clean up resources\n */\n destroy(): Promise<void>;\n\n /**\n * Execute a SQL query and return the result as an Arrow table\n * @param query SQL query to execute\n */\n query(query: string): Promise<arrow.Table>;\n\n /**\n * Load a file into DuckDB and create a table\n * @param fileName - Path to the file to load\n * @param tableName - Name of the table to create\n * @param opts - Load options\n */\n loadFile(\n fileName: string | File,\n tableName: string,\n opts?: LoadFileOptions,\n ): Promise<void>;\n\n /**\n * Load an arrow table or an arrow IPC stream into DuckDB\n * @param table - Arrow table or arrow IPC stream to load\n * @param tableName - Name of the table to create\n */\n loadArrow(\n table: arrow.Table | Uint8Array,\n tableName: string,\n opts?: {schema?: string},\n ): Promise<void>;\n\n /**\n * Load JavaScript objects into DuckDB\n * @param data - Array of objects to load\n * @param tableName - Name of the table to create\n * @param opts - Load options\n */\n loadObjects(\n data: Record<string, unknown>[],\n tableName: string,\n opts?: StandardLoadOptions,\n ): Promise<void>;\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import * as duckdb from '@duckdb/duckdb-wasm';
2
+ import { DuckDBQueryConfig } from '@duckdb/duckdb-wasm';
3
+ import { LoadFileOptions, StandardLoadOptions } from '@sqlrooms/project-config';
4
+ import * as arrow from 'apache-arrow';
5
+ import { DuckDbConnector } from './DuckDbConnector';
6
+ export declare class WasmDuckDbConnector implements DuckDbConnector {
7
+ private logging;
8
+ private dbPath;
9
+ private initializationQuery;
10
+ private db;
11
+ private conn;
12
+ private worker;
13
+ private initialized;
14
+ private initializing;
15
+ private queryConfig?;
16
+ constructor({ logging, initializationQuery, dbPath, queryConfig, }?: {
17
+ dbPath?: string;
18
+ queryConfig?: DuckDBQueryConfig;
19
+ initializationQuery?: string;
20
+ logging?: boolean;
21
+ });
22
+ initialize(): Promise<void>;
23
+ private initializeInternal;
24
+ destroy(): Promise<void>;
25
+ private ensureInitialized;
26
+ query(query: string): Promise<arrow.Table>;
27
+ loadFile(file: string | File, tableName: string, opts?: LoadFileOptions): Promise<void>;
28
+ loadArrow(file: arrow.Table | Uint8Array, tableName: string, opts?: {
29
+ schema?: string;
30
+ }): Promise<void>;
31
+ loadObjects(file: Record<string, unknown>[], tableName: string, opts?: StandardLoadOptions): Promise<void>;
32
+ private withTempRegisteredFile;
33
+ getDb(): duckdb.AsyncDuckDB;
34
+ getConnection(): duckdb.AsyncDuckDBConnection;
35
+ }
36
+ export declare class DuckQueryError extends Error {
37
+ readonly cause: unknown;
38
+ readonly query: string | undefined;
39
+ readonly queryCallStack: string | undefined;
40
+ constructor(err: unknown, query: string, stack: string | undefined);
41
+ getDetailedMessage(): string;
42
+ getMessageForUser(): string;
43
+ }
44
+ //# sourceMappingURL=WasmDuckDbConnector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WasmDuckDbConnector.d.ts","sourceRoot":"","sources":["../../src/connectors/WasmDuckDbConnector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAqB,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAEL,eAAe,EACf,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAGlD,qBAAa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,EAAE,CAAmC;IAC7C,OAAO,CAAC,IAAI,CAA6C;IACzD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,WAAW,CAAC,CAAoB;gBAE5B,EACV,OAAe,EACf,mBAAwB,EACxB,MAAmB,EACnB,WAAW,GACZ,GAAE;QACD,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,iBAAiB,CAAC;QAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;KACd;IAOA,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAiBnB,kBAAkB;IAkE1B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAyBhB,iBAAiB;IAMzB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAQ1C,QAAQ,CACZ,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,eAAe;IAalB,SAAS,CACb,IAAI,EAAE,KAAK,CAAC,KAAK,GAAG,UAAU,EAC9B,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAC;IAcpB,WAAW,CACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,mBAAmB;YASd,sBAAsB;IA8BpC,KAAK,IAAI,MAAM,CAAC,WAAW;IAO3B,aAAa,IAAI,MAAM,CAAC,qBAAqB;CAM9C;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;gBAChC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS;IAOlE,kBAAkB;IAOlB,iBAAiB;CAIlB"}
@@ -0,0 +1,210 @@
1
+ import * as duckdb from '@duckdb/duckdb-wasm';
2
+ import { DuckDBDataProtocol } from '@duckdb/duckdb-wasm';
3
+ import { isSpatialLoadFileOptions, } from '@sqlrooms/project-config';
4
+ import * as arrow from 'apache-arrow';
5
+ import { load, loadObjects, loadSpatial } from './load/load';
6
+ export class WasmDuckDbConnector {
7
+ logging;
8
+ dbPath;
9
+ initializationQuery;
10
+ db = null;
11
+ conn = null;
12
+ worker = null;
13
+ initialized = false;
14
+ initializing = null;
15
+ queryConfig;
16
+ constructor({ logging = false, initializationQuery = '', dbPath = ':memory:', queryConfig, } = {}) {
17
+ this.dbPath = dbPath;
18
+ this.queryConfig = queryConfig;
19
+ this.initializationQuery = initializationQuery;
20
+ this.logging = logging;
21
+ }
22
+ async initialize() {
23
+ if (this.initialized) {
24
+ return;
25
+ }
26
+ if (this.initializing) {
27
+ return this.initializing;
28
+ }
29
+ if (!globalThis.Worker) {
30
+ throw new Error('No Worker support in this environment');
31
+ }
32
+ this.initializing = this.initializeInternal();
33
+ return this.initializing;
34
+ }
35
+ async initializeInternal() {
36
+ try {
37
+ const allBundles = duckdb.getJsDelivrBundles();
38
+ const bestBundle = await duckdb.selectBundle(allBundles);
39
+ if (!bestBundle.mainWorker) {
40
+ throw new Error('No best bundle found for DuckDB worker');
41
+ }
42
+ const workerUrl = URL.createObjectURL(new Blob([`importScripts("${bestBundle.mainWorker}");`], {
43
+ type: 'text/javascript',
44
+ }));
45
+ const worker = new window.Worker(workerUrl);
46
+ const logger = this.logging
47
+ ? new duckdb.ConsoleLogger()
48
+ : {
49
+ // Silently log
50
+ log: () => {
51
+ /* do nothing */
52
+ },
53
+ };
54
+ const db = new (class extends duckdb.AsyncDuckDB {
55
+ onError(event) {
56
+ super.onError(event);
57
+ console.error('DuckDB worker error:', event);
58
+ }
59
+ })(logger, worker);
60
+ await db.instantiate(bestBundle.mainModule, bestBundle.pthreadWorker);
61
+ URL.revokeObjectURL(workerUrl);
62
+ await db.open({
63
+ path: this.dbPath,
64
+ query: this.queryConfig,
65
+ });
66
+ const conn = await db.connect();
67
+ // Add error handling to conn.query
68
+ const originalQuery = conn.query;
69
+ conn.query = (async (q) => {
70
+ const stack = new Error().stack;
71
+ try {
72
+ return await originalQuery.call(conn, q);
73
+ }
74
+ catch (err) {
75
+ throw new DuckQueryError(err, q, stack);
76
+ }
77
+ });
78
+ if (this.initializationQuery) {
79
+ await conn.query(this.initializationQuery);
80
+ }
81
+ this.db = db;
82
+ this.conn = conn;
83
+ this.worker = worker;
84
+ this.initialized = true;
85
+ }
86
+ catch (err) {
87
+ this.initialized = false;
88
+ this.initializing = null;
89
+ throw err;
90
+ }
91
+ }
92
+ async destroy() {
93
+ try {
94
+ if (this.conn) {
95
+ await this.conn.close();
96
+ this.conn = null;
97
+ }
98
+ if (this.db) {
99
+ await this.db.terminate();
100
+ this.db = null;
101
+ }
102
+ if (this.worker) {
103
+ this.worker.terminate();
104
+ this.worker = null;
105
+ }
106
+ this.initialized = false;
107
+ this.initializing = null;
108
+ }
109
+ catch (err) {
110
+ console.error('Error during DuckDB shutdown:', err);
111
+ throw err;
112
+ }
113
+ }
114
+ async ensureInitialized() {
115
+ if (!this.initialized) {
116
+ await this.initialize();
117
+ }
118
+ }
119
+ async query(query) {
120
+ await this.ensureInitialized();
121
+ if (!this.conn) {
122
+ throw new Error('DuckDB connection not initialized');
123
+ }
124
+ return await this.conn.query(query);
125
+ }
126
+ async loadFile(file, tableName, opts) {
127
+ await this.withTempRegisteredFile(file, async (conn, fileName) => {
128
+ if (opts && isSpatialLoadFileOptions(opts)) {
129
+ await conn.query(loadSpatial(tableName, fileName, opts));
130
+ }
131
+ else {
132
+ await conn.query(load(opts?.method ?? 'auto', tableName, fileName, opts));
133
+ }
134
+ });
135
+ }
136
+ async loadArrow(file, tableName, opts) {
137
+ await this.ensureInitialized();
138
+ if (!this.conn) {
139
+ throw new Error('DuckDB connection not initialized');
140
+ }
141
+ const options = { name: tableName, schema: opts?.schema };
142
+ if (file instanceof arrow.Table) {
143
+ await this.conn.insertArrowTable(file, options);
144
+ }
145
+ else {
146
+ await this.conn.insertArrowFromIPCStream(file, options);
147
+ }
148
+ }
149
+ async loadObjects(file, tableName, opts) {
150
+ await this.ensureInitialized();
151
+ if (!this.conn) {
152
+ throw new Error('DuckDB connection not initialized');
153
+ }
154
+ await this.conn.query(loadObjects(tableName, file, opts));
155
+ }
156
+ async withTempRegisteredFile(file, action) {
157
+ await this.ensureInitialized();
158
+ if (!this.conn || !this.db) {
159
+ throw new Error('DuckDB connection not initialized');
160
+ }
161
+ let fileName;
162
+ if (file instanceof File) {
163
+ fileName = file.name;
164
+ await this.db.registerFileHandle(fileName, file, DuckDBDataProtocol.BROWSER_FILEREADER, true);
165
+ }
166
+ else {
167
+ fileName = file;
168
+ }
169
+ try {
170
+ await action(this.conn, fileName);
171
+ }
172
+ finally {
173
+ await this.db.dropFile(fileName);
174
+ }
175
+ }
176
+ getDb() {
177
+ if (!this.db) {
178
+ throw new Error('DuckDB not initialized');
179
+ }
180
+ return this.db;
181
+ }
182
+ getConnection() {
183
+ if (!this.conn) {
184
+ throw new Error('DuckDB connection not initialized');
185
+ }
186
+ return this.conn;
187
+ }
188
+ }
189
+ export class DuckQueryError extends Error {
190
+ cause;
191
+ query;
192
+ queryCallStack;
193
+ constructor(err, query, stack) {
194
+ super(err instanceof Error ? err.message : `${err}`);
195
+ this.cause = err;
196
+ this.query = query;
197
+ this.queryCallStack = stack;
198
+ Object.setPrototypeOf(this, DuckQueryError.prototype);
199
+ }
200
+ getDetailedMessage() {
201
+ const { message, query, queryCallStack: stack } = this;
202
+ return (`DB query failed: ${message}` +
203
+ `\n\nFull query:\n\n${query}\n\nQuery call stack:\n\n${stack}\n\n`);
204
+ }
205
+ getMessageForUser() {
206
+ const { message } = this;
207
+ return message;
208
+ }
209
+ }
210
+ //# sourceMappingURL=WasmDuckDbConnector.js.map