@sqlrooms/duckdb 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DuckDbSlice.d.ts +102 -23
- package/dist/DuckDbSlice.d.ts.map +1 -1
- package/dist/DuckDbSlice.js +201 -63
- package/dist/DuckDbSlice.js.map +1 -1
- package/dist/connectors/BaseDuckDbConnector.d.ts +22 -5
- package/dist/connectors/BaseDuckDbConnector.d.ts.map +1 -1
- package/dist/connectors/BaseDuckDbConnector.js +66 -15
- package/dist/connectors/BaseDuckDbConnector.js.map +1 -1
- package/dist/connectors/DuckDbConnector.d.ts +237 -4
- package/dist/connectors/DuckDbConnector.d.ts.map +1 -1
- package/dist/connectors/DuckDbConnector.js.map +1 -1
- package/dist/connectors/WasmDuckDbConnector.d.ts +2 -1
- package/dist/connectors/WasmDuckDbConnector.d.ts.map +1 -1
- package/dist/connectors/WasmDuckDbConnector.js +90 -17
- package/dist/connectors/WasmDuckDbConnector.js.map +1 -1
- package/dist/duckdb-utils.d.ts +67 -0
- package/dist/duckdb-utils.d.ts.map +1 -1
- package/dist/duckdb-utils.js +195 -0
- package/dist/duckdb-utils.js.map +1 -1
- package/dist/exportToCsv.js +1 -1
- package/dist/exportToCsv.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/schemaTree.d.ts +8 -0
- package/dist/schemaTree.d.ts.map +1 -0
- package/dist/schemaTree.js +76 -0
- package/dist/schemaTree.js.map +1 -0
- package/dist/typeCategories.d.ts +16 -0
- package/dist/typeCategories.d.ts.map +1 -0
- package/dist/typeCategories.js +72 -0
- package/dist/typeCategories.js.map +1 -0
- package/dist/types.d.ts +35 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/useSql.d.ts.map +1 -1
- package/dist/useSql.js +11 -7
- package/dist/useSql.js.map +1 -1
- package/package.json +13 -12
package/dist/DuckDbSlice.d.ts
CHANGED
|
@@ -2,10 +2,15 @@ import { ProjectState } from '@sqlrooms/project';
|
|
|
2
2
|
import * as arrow from 'apache-arrow';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { StateCreator } from 'zustand';
|
|
5
|
-
import { DuckDbConnector } from './connectors/DuckDbConnector';
|
|
6
|
-
import {
|
|
5
|
+
import { DuckDbConnector, QueryHandle } from './connectors/DuckDbConnector';
|
|
6
|
+
import { QualifiedTableName } from './duckdb-utils';
|
|
7
|
+
import { DataTable, DbSchemaNode } from './types';
|
|
7
8
|
export declare const DuckDbSliceConfig: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
8
9
|
export type DuckDbSliceConfig = z.infer<typeof DuckDbSliceConfig>;
|
|
10
|
+
export type SchemaAndDatabase = {
|
|
11
|
+
schema?: string;
|
|
12
|
+
database?: string;
|
|
13
|
+
};
|
|
9
14
|
export declare function createDefaultDuckDbConfig(): DuckDbSliceConfig;
|
|
10
15
|
/**
|
|
11
16
|
* State and actions for the DuckDB slice
|
|
@@ -16,11 +21,36 @@ export type DuckDbSliceState = {
|
|
|
16
21
|
* The DuckDB connector instance
|
|
17
22
|
*/
|
|
18
23
|
connector: DuckDbConnector;
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated We shouldn't limit the schema to a single one.
|
|
26
|
+
*/
|
|
19
27
|
schema: string;
|
|
28
|
+
currentSchema: string | undefined;
|
|
29
|
+
currentDatabase: string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Cache of refreshed table schemas
|
|
32
|
+
*/
|
|
20
33
|
tables: DataTable[];
|
|
34
|
+
/**
|
|
35
|
+
* Cache of row counts for tables
|
|
36
|
+
*/
|
|
21
37
|
tableRowCounts: {
|
|
22
38
|
[tableName: string]: number;
|
|
23
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* Cache of schema trees for tables
|
|
42
|
+
*/
|
|
43
|
+
schemaTrees?: DbSchemaNode[];
|
|
44
|
+
/**
|
|
45
|
+
* Cache of currently running query handles
|
|
46
|
+
*/
|
|
47
|
+
queryCache: {
|
|
48
|
+
[key: string]: QueryHandle;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Whether the table schemas are being refreshed
|
|
52
|
+
*/
|
|
53
|
+
isRefreshingTableSchemas: boolean;
|
|
24
54
|
/**
|
|
25
55
|
* Set a new DuckDB connector
|
|
26
56
|
*/
|
|
@@ -39,10 +69,29 @@ export type DuckDbSliceState = {
|
|
|
39
69
|
* @param data - The data to add to the table: an arrow table or an array of records.
|
|
40
70
|
* @returns A promise that resolves to the table that was added.
|
|
41
71
|
*/
|
|
42
|
-
addTable(tableName: string, data: arrow.Table | Record<string, unknown>[]): Promise<DataTable>;
|
|
72
|
+
addTable(tableName: string | QualifiedTableName, data: arrow.Table | Record<string, unknown>[]): Promise<DataTable>;
|
|
73
|
+
/**
|
|
74
|
+
* Load the schemas of the tables in the database.
|
|
75
|
+
*/
|
|
76
|
+
loadTableSchemas(filter?: SchemaAndDatabase & {
|
|
77
|
+
table?: string;
|
|
78
|
+
}): Promise<DataTable[]>;
|
|
79
|
+
/**
|
|
80
|
+
* @deprecated Use findTableByName instead
|
|
81
|
+
*/
|
|
43
82
|
getTable(tableName: string): DataTable | undefined;
|
|
44
|
-
|
|
45
|
-
|
|
83
|
+
/**
|
|
84
|
+
* @internal Avoid using this directly, it's for internal use.
|
|
85
|
+
*/
|
|
86
|
+
setTableRowCount(tableName: string | QualifiedTableName, rowCount: number): void;
|
|
87
|
+
/**
|
|
88
|
+
* Find a table by name in the last refreshed table schemas.
|
|
89
|
+
* If no schema or database is provided, the table will be found in the current schema
|
|
90
|
+
* and database (from last table schemas refresh).
|
|
91
|
+
* @param tableName - The name of the table to find or a qualified table name.
|
|
92
|
+
* @returns The table or undefined if not found.
|
|
93
|
+
*/
|
|
94
|
+
findTableByName(tableName: string | QualifiedTableName): DataTable | undefined;
|
|
46
95
|
/**
|
|
47
96
|
* Refresh table schemas from the database.
|
|
48
97
|
* @returns A promise that resolves to the updated tables.
|
|
@@ -53,45 +102,75 @@ export type DuckDbSliceState = {
|
|
|
53
102
|
*/
|
|
54
103
|
getConnector: () => Promise<DuckDbConnector>;
|
|
55
104
|
/**
|
|
56
|
-
*
|
|
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.
|
|
105
|
+
* @deprecated Use .loadTableRowCount() instead
|
|
60
106
|
*/
|
|
61
|
-
|
|
107
|
+
getTableRowCount: (table: string, schema?: string) => Promise<number>;
|
|
62
108
|
/**
|
|
63
|
-
*
|
|
109
|
+
* Load the row count of a table
|
|
64
110
|
*/
|
|
65
|
-
|
|
111
|
+
loadTableRowCount: (tableName: string | QualifiedTableName) => Promise<number>;
|
|
66
112
|
/**
|
|
67
|
-
*
|
|
113
|
+
* Execute a query with query handle (not result) caching and deduplication
|
|
114
|
+
* @param query - The SQL query to execute
|
|
115
|
+
* @returns The QueryHandle for the query or null if disabled
|
|
68
116
|
*/
|
|
69
|
-
|
|
117
|
+
executeSql: (query: string) => Promise<QueryHandle | null>;
|
|
70
118
|
/**
|
|
71
|
-
*
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
119
|
+
* @deprecated Use .tables or .loadTableSchemas() instead
|
|
120
|
+
*/
|
|
121
|
+
getTables: (schema?: string) => Promise<string[]>;
|
|
122
|
+
/**
|
|
123
|
+
* @deprecated Use .loadTableSchemas() instead
|
|
124
|
+
*/
|
|
125
|
+
getTableSchema: (tableName: string, schema?: string) => Promise<DataTable | undefined>;
|
|
126
|
+
/**
|
|
127
|
+
* @deprecated Use .tables or .loadTableSchemas() instead
|
|
75
128
|
*/
|
|
76
129
|
getTableSchemas: (schema?: string) => Promise<DataTable[]>;
|
|
77
130
|
/**
|
|
78
131
|
* Check if a table exists
|
|
79
132
|
*/
|
|
80
|
-
checkTableExists: (tableName: string
|
|
133
|
+
checkTableExists: (tableName: string | QualifiedTableName) => Promise<boolean>;
|
|
81
134
|
/**
|
|
82
|
-
*
|
|
135
|
+
* Delete a table with optional schema and database
|
|
136
|
+
* @param tableName - The name of the table to delete
|
|
137
|
+
* @param options - Optional parameters including schema and database
|
|
83
138
|
*/
|
|
84
|
-
dropTable: (tableName: string) => Promise<void>;
|
|
139
|
+
dropTable: (tableName: string | QualifiedTableName) => Promise<void>;
|
|
85
140
|
/**
|
|
86
141
|
* Create a table from a query.
|
|
87
142
|
* @param tableName - The name of the table to create.
|
|
88
143
|
* @param query - The query to create the table from.
|
|
89
144
|
* @returns The table that was created.
|
|
90
145
|
*/
|
|
91
|
-
createTableFromQuery: (tableName: string, query: string) => Promise<{
|
|
92
|
-
tableName: string;
|
|
146
|
+
createTableFromQuery: (tableName: string | QualifiedTableName, query: string) => Promise<{
|
|
147
|
+
tableName: string | QualifiedTableName;
|
|
93
148
|
rowCount: number;
|
|
94
149
|
}>;
|
|
150
|
+
/**
|
|
151
|
+
* Parse a SQL SELECT statement to JSON
|
|
152
|
+
* @param sql - The SQL SELECT statement to parse.
|
|
153
|
+
* @returns A promise that resolves to the parsed JSON.
|
|
154
|
+
*/
|
|
155
|
+
sqlSelectToJson: (sql: string) => Promise<{
|
|
156
|
+
error: true;
|
|
157
|
+
error_type: string;
|
|
158
|
+
error_message: string;
|
|
159
|
+
error_subtype: string;
|
|
160
|
+
position: string;
|
|
161
|
+
} | {
|
|
162
|
+
error: false;
|
|
163
|
+
statements: {
|
|
164
|
+
node: {
|
|
165
|
+
from_table: {
|
|
166
|
+
alias: string;
|
|
167
|
+
show_type: string;
|
|
168
|
+
table_name: string;
|
|
169
|
+
};
|
|
170
|
+
select_list: Record<string, unknown>[];
|
|
171
|
+
};
|
|
172
|
+
}[];
|
|
173
|
+
}>;
|
|
95
174
|
};
|
|
96
175
|
};
|
|
97
176
|
/**
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"DuckDbSlice.d.ts","sourceRoot":"","sources":["../src/DuckDbSlice.ts"],"names":[],"mappings":"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"}
|
package/dist/DuckDbSlice.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createBaseSlice, useBaseProjectStore, } from '@sqlrooms/project';
|
|
2
2
|
import * as arrow from 'apache-arrow';
|
|
3
|
-
import { produce } from 'immer';
|
|
4
3
|
import deepEquals from 'fast-deep-equal';
|
|
4
|
+
import { produce } from 'immer';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { WasmDuckDbConnector } from './connectors/WasmDuckDbConnector';
|
|
7
|
-
import { getColValAsNumber } from './duckdb-utils';
|
|
7
|
+
import { escapeId, escapeVal, getColValAsNumber, isQualifiedTableName, makeQualifiedTableName, splitSqlStatements, } from './duckdb-utils';
|
|
8
|
+
import { createDbSchemaTrees as createDbSchemaTrees } from './schemaTree';
|
|
8
9
|
export const DuckDbSliceConfig = z.object({
|
|
9
10
|
// nothing yet
|
|
10
11
|
});
|
|
@@ -21,9 +22,14 @@ export function createDuckDbSlice({ connector = new WasmDuckDbConnector(), }) {
|
|
|
21
22
|
return {
|
|
22
23
|
db: {
|
|
23
24
|
connector, // Will be initialized during init
|
|
24
|
-
schema: 'main',
|
|
25
|
+
schema: 'main', // TODO: remove schema, we should not limit the schema to a single one.
|
|
26
|
+
currentSchema: undefined,
|
|
27
|
+
currentDatabase: undefined,
|
|
28
|
+
isRefreshingTableSchemas: false,
|
|
25
29
|
tables: [],
|
|
26
30
|
tableRowCounts: {},
|
|
31
|
+
schemaTree: undefined,
|
|
32
|
+
queryCache: {},
|
|
27
33
|
setConnector: (connector) => {
|
|
28
34
|
set(produce((state) => {
|
|
29
35
|
state.config.dataSources = [];
|
|
@@ -32,9 +38,9 @@ export function createDuckDbSlice({ connector = new WasmDuckDbConnector(), }) {
|
|
|
32
38
|
},
|
|
33
39
|
initialize: async () => {
|
|
34
40
|
await get().db.connector.initialize();
|
|
41
|
+
await get().db.refreshTableSchemas();
|
|
35
42
|
},
|
|
36
43
|
getConnector: async () => {
|
|
37
|
-
await get().db.initialize();
|
|
38
44
|
return get().db.connector;
|
|
39
45
|
},
|
|
40
46
|
destroy: async () => {
|
|
@@ -48,99 +54,231 @@ export function createDuckDbSlice({ connector = new WasmDuckDbConnector(), }) {
|
|
|
48
54
|
}
|
|
49
55
|
},
|
|
50
56
|
async createTableFromQuery(tableName, query) {
|
|
57
|
+
const qualifiedName = isQualifiedTableName(tableName)
|
|
58
|
+
? tableName
|
|
59
|
+
: makeQualifiedTableName({ table: tableName });
|
|
51
60
|
const connector = await get().db.getConnector();
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
const statements = splitSqlStatements(query);
|
|
62
|
+
if (statements.length !== 1) {
|
|
63
|
+
throw new Error('Query must contain exactly one statement');
|
|
64
|
+
}
|
|
65
|
+
const statement = statements[0];
|
|
66
|
+
const parsedQuery = await get().db.sqlSelectToJson(statement);
|
|
67
|
+
if (parsedQuery.error) {
|
|
68
|
+
throw new Error('Query is not a valid SELECT statement');
|
|
69
|
+
}
|
|
70
|
+
const rowCount = getColValAsNumber(await connector.query(`CREATE OR REPLACE TABLE ${qualifiedName} AS (
|
|
71
|
+
${statements[0]}
|
|
72
|
+
)`).result);
|
|
55
73
|
return { tableName, rowCount };
|
|
56
74
|
},
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
for (let i = 0; i < tablesResults.numRows; i++) {
|
|
64
|
-
tableNames.push(tablesResults.getChild('table_name')?.get(i));
|
|
65
|
-
}
|
|
66
|
-
return tableNames;
|
|
75
|
+
/**
|
|
76
|
+
* @deprecated Use .tables or .loadTableSchemas() instead
|
|
77
|
+
*/
|
|
78
|
+
async getTables(schema) {
|
|
79
|
+
const tableSchemas = await get().db.loadTableSchemas({ schema });
|
|
80
|
+
return tableSchemas.map((t) => t.table.table);
|
|
67
81
|
},
|
|
82
|
+
/**
|
|
83
|
+
* @deprecated Use .loadTableSchemas() instead
|
|
84
|
+
*/
|
|
68
85
|
async getTableSchema(tableName, schema = 'main') {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
tableName,
|
|
81
|
-
columns,
|
|
82
|
-
};
|
|
86
|
+
const newLocal = await get().db.loadTableSchemas({
|
|
87
|
+
schema,
|
|
88
|
+
table: tableName,
|
|
89
|
+
});
|
|
90
|
+
return newLocal[0];
|
|
91
|
+
},
|
|
92
|
+
/**
|
|
93
|
+
* @deprecated Use .loadTableRowCount() instead
|
|
94
|
+
*/
|
|
95
|
+
async getTableRowCount(table, schema = 'main') {
|
|
96
|
+
return get().db.loadTableRowCount({ table, schema });
|
|
83
97
|
},
|
|
84
|
-
async
|
|
98
|
+
async loadTableRowCount(tableName) {
|
|
99
|
+
const { schema, database, table } = typeof tableName === 'string'
|
|
100
|
+
? { table: tableName }
|
|
101
|
+
: tableName || {};
|
|
85
102
|
const connector = await get().db.getConnector();
|
|
86
|
-
const result = await connector.query(`SELECT COUNT(*) FROM ${
|
|
103
|
+
const result = await connector.query(`SELECT COUNT(*) FROM ${makeQualifiedTableName({
|
|
104
|
+
schema,
|
|
105
|
+
database,
|
|
106
|
+
table,
|
|
107
|
+
})}`).result;
|
|
87
108
|
return getColValAsNumber(result);
|
|
88
109
|
},
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
/**
|
|
111
|
+
* @deprecated Use .loadTableSchemas() instead
|
|
112
|
+
*/
|
|
113
|
+
async getTableSchemas(schema) {
|
|
114
|
+
return await get().db.loadTableSchemas({ schema });
|
|
115
|
+
},
|
|
116
|
+
async loadTableSchemas(filter) {
|
|
117
|
+
const { schema, database, table } = filter || {};
|
|
118
|
+
const describeResults = await connector.query(`FROM (DESCRIBE)
|
|
119
|
+
SELECT
|
|
120
|
+
database, schema,
|
|
121
|
+
name, column_names, column_types
|
|
122
|
+
${schema || database || table
|
|
123
|
+
? `WHERE ${[
|
|
124
|
+
schema ? `schema = '${escapeId(schema)}'` : '',
|
|
125
|
+
database ? `database = '${escapeId(database)}'` : '',
|
|
126
|
+
table ? `name = '${escapeId(table)}'` : '',
|
|
127
|
+
]
|
|
128
|
+
.filter(Boolean)
|
|
129
|
+
.join(' AND ')}`
|
|
130
|
+
: ''}`).result;
|
|
131
|
+
const newTables = [];
|
|
132
|
+
for (let i = 0; i < describeResults.numRows; i++) {
|
|
133
|
+
const database = describeResults.getChild('database')?.get(i);
|
|
134
|
+
const schema = describeResults.getChild('schema')?.get(i);
|
|
135
|
+
const table = describeResults.getChild('name')?.get(i);
|
|
136
|
+
const columnNames = describeResults
|
|
137
|
+
.getChild('column_names')
|
|
138
|
+
?.get(i);
|
|
139
|
+
const columnTypes = describeResults
|
|
140
|
+
.getChild('column_types')
|
|
141
|
+
?.get(i);
|
|
142
|
+
const columns = [];
|
|
143
|
+
for (let di = 0; di < columnNames.length; di++) {
|
|
144
|
+
columns.push({
|
|
145
|
+
name: columnNames.get(di),
|
|
146
|
+
type: columnTypes.get(di),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
newTables.push({
|
|
150
|
+
table: makeQualifiedTableName({ database, schema, table }),
|
|
151
|
+
database,
|
|
152
|
+
schema,
|
|
153
|
+
tableName: table,
|
|
154
|
+
columns,
|
|
155
|
+
});
|
|
94
156
|
}
|
|
95
|
-
return
|
|
157
|
+
return newTables;
|
|
96
158
|
},
|
|
97
|
-
async checkTableExists(tableName
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
159
|
+
async checkTableExists(tableName) {
|
|
160
|
+
const qualifiedName = isQualifiedTableName(tableName)
|
|
161
|
+
? tableName
|
|
162
|
+
: makeQualifiedTableName({ table: tableName });
|
|
163
|
+
const table = (await get().db.loadTableSchemas(qualifiedName))[0];
|
|
164
|
+
if (!table) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
101
168
|
},
|
|
102
169
|
async dropTable(tableName) {
|
|
103
170
|
const connector = await get().db.getConnector();
|
|
104
|
-
|
|
171
|
+
const qualifiedTable = isQualifiedTableName(tableName)
|
|
172
|
+
? tableName
|
|
173
|
+
: makeQualifiedTableName({ table: tableName });
|
|
174
|
+
await connector.query(`DROP TABLE IF EXISTS ${qualifiedTable};`)
|
|
175
|
+
.result;
|
|
176
|
+
await get().db.refreshTableSchemas();
|
|
105
177
|
},
|
|
106
178
|
async addTable(tableName, data) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return table;
|
|
111
|
-
}
|
|
179
|
+
const qualifiedName = isQualifiedTableName(tableName)
|
|
180
|
+
? tableName
|
|
181
|
+
: makeQualifiedTableName({ table: tableName });
|
|
112
182
|
const { db } = get();
|
|
113
183
|
if (data instanceof arrow.Table) {
|
|
114
|
-
|
|
184
|
+
// TODO: make sure the table is replaced
|
|
185
|
+
await db.connector.loadArrow(data, qualifiedName.toString());
|
|
115
186
|
}
|
|
116
187
|
else {
|
|
117
|
-
await db.connector.loadObjects(data,
|
|
188
|
+
await db.connector.loadObjects(data, qualifiedName.toString(), {
|
|
189
|
+
replace: true,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
const newTable = (await db.loadTableSchemas(qualifiedName))[0];
|
|
193
|
+
if (!newTable) {
|
|
194
|
+
throw new Error('Failed to add table');
|
|
118
195
|
}
|
|
119
|
-
const newTable = await db.getTableSchema(tableName);
|
|
120
196
|
set((state) => produce(state, (draft) => {
|
|
121
197
|
draft.db.tables.push(newTable);
|
|
122
198
|
}));
|
|
199
|
+
await get().db.refreshTableSchemas();
|
|
123
200
|
return newTable;
|
|
124
201
|
},
|
|
125
|
-
setTableRowCount
|
|
126
|
-
|
|
127
|
-
|
|
202
|
+
async setTableRowCount(tableName, rowCount) {
|
|
203
|
+
const qualifiedName = isQualifiedTableName(tableName)
|
|
204
|
+
? tableName
|
|
205
|
+
: makeQualifiedTableName({ table: tableName });
|
|
206
|
+
set((state) => produce(state, (draft) => {
|
|
207
|
+
draft.db.tableRowCounts[qualifiedName.toString()] = rowCount;
|
|
208
|
+
}));
|
|
209
|
+
},
|
|
128
210
|
getTable(tableName) {
|
|
129
|
-
return get().db.
|
|
211
|
+
return get().db.findTableByName(tableName);
|
|
130
212
|
},
|
|
131
213
|
findTableByName(tableName) {
|
|
132
|
-
|
|
214
|
+
const { table, schema, database } = {
|
|
215
|
+
schema: get().db.currentSchema,
|
|
216
|
+
database: get().db.currentDatabase,
|
|
217
|
+
...(typeof tableName === 'string' ? { table: tableName } : tableName),
|
|
218
|
+
};
|
|
219
|
+
return get().db.tables.find((t) => t.table.table === table &&
|
|
220
|
+
(!schema || t.table.schema === schema) &&
|
|
221
|
+
(!database || t.table.database === database));
|
|
133
222
|
},
|
|
134
223
|
async refreshTableSchemas() {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
224
|
+
set((state) => produce(state, (draft) => {
|
|
225
|
+
draft.db.isRefreshingTableSchemas = true;
|
|
226
|
+
}));
|
|
227
|
+
try {
|
|
228
|
+
const connector = await get().db.getConnector();
|
|
229
|
+
const result = await connector.query(`SELECT current_schema() AS schema, current_database() AS database`).result;
|
|
139
230
|
set((state) => produce(state, (draft) => {
|
|
140
|
-
draft.db.
|
|
231
|
+
draft.db.currentSchema = result.getChild('schema')?.get(0);
|
|
232
|
+
draft.db.currentDatabase = result.getChild('database')?.get(0);
|
|
141
233
|
}));
|
|
234
|
+
const newTables = await get().db.loadTableSchemas();
|
|
235
|
+
// Only update if there's an actual change in the schemas
|
|
236
|
+
if (!deepEquals(newTables, get().db.tables)) {
|
|
237
|
+
set((state) => produce(state, (draft) => {
|
|
238
|
+
draft.db.tables = newTables;
|
|
239
|
+
draft.db.schemaTrees = createDbSchemaTrees(newTables);
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
return newTables;
|
|
142
243
|
}
|
|
143
|
-
|
|
244
|
+
catch (err) {
|
|
245
|
+
get().project.captureException(err);
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
set((state) => produce(state, (draft) => {
|
|
250
|
+
draft.db.isRefreshingTableSchemas = false;
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
async sqlSelectToJson(sql) {
|
|
255
|
+
const connector = await get().db.getConnector();
|
|
256
|
+
const parsedQuery = (await connector.query(`SELECT json_serialize_sql(${escapeVal(sql)})`).result)
|
|
257
|
+
.getChildAt(0)
|
|
258
|
+
?.get(0);
|
|
259
|
+
return JSON.parse(parsedQuery);
|
|
260
|
+
},
|
|
261
|
+
async executeSql(query) {
|
|
262
|
+
// Create a unique key for this query
|
|
263
|
+
const queryKey = `${query}`;
|
|
264
|
+
const connector = await get().db.getConnector();
|
|
265
|
+
// Check if we already have a cached query for this key
|
|
266
|
+
const existingQuery = get().db.queryCache[queryKey];
|
|
267
|
+
if (existingQuery) {
|
|
268
|
+
return existingQuery;
|
|
269
|
+
}
|
|
270
|
+
const queryHandle = connector.query(query);
|
|
271
|
+
// Cache the query handle immediately
|
|
272
|
+
set((state) => produce(state, (draft) => {
|
|
273
|
+
draft.db.queryCache[queryKey] = queryHandle;
|
|
274
|
+
}));
|
|
275
|
+
queryHandle.result.finally(() => {
|
|
276
|
+
// remove from cache after completion
|
|
277
|
+
set((state) => produce(state, (draft) => {
|
|
278
|
+
delete draft.db.queryCache[queryKey];
|
|
279
|
+
}));
|
|
280
|
+
});
|
|
281
|
+
return queryHandle;
|
|
144
282
|
},
|
|
145
283
|
},
|
|
146
284
|
};
|
package/dist/DuckDbSlice.js.map
CHANGED
|
@@ -1 +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 * 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 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"]}
|
|
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 deepEquals from 'fast-deep-equal';\nimport {produce} from 'immer';\nimport {z} from 'zod';\nimport {StateCreator} from 'zustand';\nimport {DuckDbConnector, QueryHandle} from './connectors/DuckDbConnector';\nimport {WasmDuckDbConnector} from './connectors/WasmDuckDbConnector';\nimport {\n escapeId,\n escapeVal,\n getColValAsNumber,\n isQualifiedTableName,\n makeQualifiedTableName,\n QualifiedTableName,\n splitSqlStatements,\n} from './duckdb-utils';\nimport {createDbSchemaTrees as createDbSchemaTrees} from './schemaTree';\nimport {DataTable, TableColumn, DbSchemaNode} from './types';\n\nexport const DuckDbSliceConfig = z.object({\n // nothing yet\n});\nexport type DuckDbSliceConfig = z.infer<typeof DuckDbSliceConfig>;\n\nexport type SchemaAndDatabase = {\n schema?: string;\n database?: string;\n};\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 /**\n * @deprecated We shouldn't limit the schema to a single one.\n */\n schema: string;\n\n currentSchema: string | undefined;\n currentDatabase: string | undefined;\n\n /**\n * Cache of refreshed table schemas\n */\n tables: DataTable[];\n /**\n * Cache of row counts for tables\n */\n tableRowCounts: {[tableName: string]: number};\n /**\n * Cache of schema trees for tables\n */\n schemaTrees?: DbSchemaNode[];\n /**\n * Cache of currently running query handles\n */\n queryCache: {[key: string]: QueryHandle};\n /**\n * Whether the table schemas are being refreshed\n */\n isRefreshingTableSchemas: boolean;\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 | QualifiedTableName,\n data: arrow.Table | Record<string, unknown>[],\n ): Promise<DataTable>;\n\n /**\n * Load the schemas of the tables in the database.\n */\n loadTableSchemas(\n filter?: SchemaAndDatabase & {table?: string},\n ): Promise<DataTable[]>;\n\n /**\n * @deprecated Use findTableByName instead\n */\n getTable(tableName: string): DataTable | undefined;\n\n /**\n * @internal Avoid using this directly, it's for internal use.\n */\n setTableRowCount(\n tableName: string | QualifiedTableName,\n rowCount: number,\n ): void;\n\n /**\n * Find a table by name in the last refreshed table schemas.\n * If no schema or database is provided, the table will be found in the current schema\n * and database (from last table schemas refresh).\n * @param tableName - The name of the table to find or a qualified table name.\n * @returns The table or undefined if not found.\n */\n findTableByName(\n tableName: string | QualifiedTableName,\n ): DataTable | undefined;\n\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 * @deprecated Use .loadTableRowCount() instead\n */\n getTableRowCount: (table: string, schema?: string) => Promise<number>;\n\n /**\n * Load the row count of a table\n */\n loadTableRowCount: (\n tableName: string | QualifiedTableName,\n ) => Promise<number>;\n\n /**\n * Execute a query with query handle (not result) caching and deduplication\n * @param query - The SQL query to execute\n * @returns The QueryHandle for the query or null if disabled\n */\n executeSql: (query: string) => Promise<QueryHandle | null>;\n\n /**\n * @deprecated Use .tables or .loadTableSchemas() instead\n */\n getTables: (schema?: string) => Promise<string[]>;\n\n /**\n * @deprecated Use .loadTableSchemas() instead\n */\n getTableSchema: (\n tableName: string,\n schema?: string,\n ) => Promise<DataTable | undefined>;\n\n /**\n * @deprecated Use .tables or .loadTableSchemas() instead\n */\n getTableSchemas: (schema?: string) => Promise<DataTable[]>;\n\n /**\n * Check if a table exists\n */\n checkTableExists: (\n tableName: string | QualifiedTableName,\n ) => Promise<boolean>;\n\n /**\n * Delete a table with optional schema and database\n * @param tableName - The name of the table to delete\n * @param options - Optional parameters including schema and database\n */\n dropTable: (tableName: string | QualifiedTableName) => Promise<void>;\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 | QualifiedTableName,\n query: string,\n ) => Promise<{tableName: string | QualifiedTableName; rowCount: number}>;\n\n /**\n * Parse a SQL SELECT statement to JSON\n * @param sql - The SQL SELECT statement to parse.\n * @returns A promise that resolves to the parsed JSON.\n */\n sqlSelectToJson: (sql: string) => Promise<\n | {\n error: true;\n error_type: string;\n error_message: string;\n error_subtype: string;\n position: string;\n }\n | {\n error: false;\n statements: {\n node: {\n from_table: {\n alias: string;\n show_type: string;\n table_name: string;\n };\n select_list: Record<string, unknown>[];\n };\n }[];\n }\n >;\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', // TODO: remove schema, we should not limit the schema to a single one.\n currentSchema: undefined,\n currentDatabase: undefined,\n isRefreshingTableSchemas: false,\n tables: [],\n tableRowCounts: {},\n schemaTree: undefined,\n queryCache: {},\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 await get().db.refreshTableSchemas();\n },\n\n getConnector: async () => {\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(\n tableName: string | QualifiedTableName,\n query: string,\n ) {\n const qualifiedName = isQualifiedTableName(tableName)\n ? tableName\n : makeQualifiedTableName({table: tableName});\n\n const connector = await get().db.getConnector();\n\n const statements = splitSqlStatements(query);\n if (statements.length !== 1) {\n throw new Error('Query must contain exactly one statement');\n }\n const statement = statements[0] as string;\n const parsedQuery = await get().db.sqlSelectToJson(statement);\n if (parsedQuery.error) {\n throw new Error('Query is not a valid SELECT statement');\n }\n\n const rowCount = getColValAsNumber(\n await connector.query(\n `CREATE OR REPLACE TABLE ${qualifiedName} AS (\n ${statements[0]}\n )`,\n ).result,\n );\n return {tableName, rowCount};\n },\n\n /**\n * @deprecated Use .tables or .loadTableSchemas() instead\n */\n async getTables(schema) {\n const tableSchemas = await get().db.loadTableSchemas({schema});\n return tableSchemas.map((t) => t.table.table);\n },\n\n /**\n * @deprecated Use .loadTableSchemas() instead\n */\n async getTableSchema(tableName: string, schema = 'main') {\n const newLocal = await get().db.loadTableSchemas({\n schema,\n table: tableName,\n });\n return newLocal[0];\n },\n\n /**\n * @deprecated Use .loadTableRowCount() instead\n */\n async getTableRowCount(table, schema = 'main') {\n return get().db.loadTableRowCount({table, schema});\n },\n\n async loadTableRowCount(tableName: string | QualifiedTableName) {\n const {schema, database, table} =\n typeof tableName === 'string'\n ? {table: tableName}\n : tableName || {};\n const connector = await get().db.getConnector();\n const result = await connector.query(\n `SELECT COUNT(*) FROM ${makeQualifiedTableName({\n schema,\n database,\n table,\n })}`,\n ).result;\n return getColValAsNumber(result);\n },\n\n /**\n * @deprecated Use .loadTableSchemas() instead\n */\n async getTableSchemas(schema) {\n return await get().db.loadTableSchemas({schema});\n },\n\n async loadTableSchemas(\n filter?: SchemaAndDatabase & {table?: string},\n ): Promise<DataTable[]> {\n const {schema, database, table} = filter || {};\n const describeResults = await connector.query(\n `FROM (DESCRIBE)\n SELECT \n database, schema,\n name, column_names, column_types\n ${\n schema || database || table\n ? `WHERE ${[\n schema ? `schema = '${escapeId(schema)}'` : '',\n database ? `database = '${escapeId(database)}'` : '',\n table ? `name = '${escapeId(table)}'` : '',\n ]\n .filter(Boolean)\n .join(' AND ')}`\n : ''\n }`,\n ).result;\n\n const newTables: DataTable[] = [];\n for (let i = 0; i < describeResults.numRows; i++) {\n const database = describeResults.getChild('database')?.get(i);\n const schema = describeResults.getChild('schema')?.get(i);\n const table = describeResults.getChild('name')?.get(i);\n const columnNames = describeResults\n .getChild('column_names')\n ?.get(i);\n const columnTypes = describeResults\n .getChild('column_types')\n ?.get(i);\n const columns: TableColumn[] = [];\n for (let di = 0; di < columnNames.length; di++) {\n columns.push({\n name: columnNames.get(di),\n type: columnTypes.get(di),\n });\n }\n newTables.push({\n table: makeQualifiedTableName({database, schema, table}),\n database,\n schema,\n tableName: table,\n columns,\n });\n }\n return newTables;\n },\n\n async checkTableExists(tableName: string | QualifiedTableName) {\n const qualifiedName = isQualifiedTableName(tableName)\n ? tableName\n : makeQualifiedTableName({table: tableName});\n const table = (await get().db.loadTableSchemas(qualifiedName))[0];\n if (!table) {\n return false;\n }\n return true;\n },\n\n async dropTable(tableName): Promise<void> {\n const connector = await get().db.getConnector();\n const qualifiedTable = isQualifiedTableName(tableName)\n ? tableName\n : makeQualifiedTableName({table: tableName});\n await connector.query(`DROP TABLE IF EXISTS ${qualifiedTable};`)\n .result;\n await get().db.refreshTableSchemas();\n },\n\n async addTable(tableName, data) {\n const qualifiedName = isQualifiedTableName(tableName)\n ? tableName\n : makeQualifiedTableName({table: tableName});\n\n const {db} = get();\n if (data instanceof arrow.Table) {\n // TODO: make sure the table is replaced\n await db.connector.loadArrow(data, qualifiedName.toString());\n } else {\n await db.connector.loadObjects(data, qualifiedName.toString(), {\n replace: true,\n });\n }\n const newTable = (await db.loadTableSchemas(qualifiedName))[0];\n if (!newTable) {\n throw new Error('Failed to add table');\n }\n set((state) =>\n produce(state, (draft) => {\n draft.db.tables.push(newTable);\n }),\n );\n await get().db.refreshTableSchemas();\n return newTable;\n },\n\n async setTableRowCount(tableName, rowCount) {\n const qualifiedName = isQualifiedTableName(tableName)\n ? tableName\n : makeQualifiedTableName({table: tableName});\n set((state) =>\n produce(state, (draft) => {\n draft.db.tableRowCounts[qualifiedName.toString()] = rowCount;\n }),\n );\n },\n\n getTable(tableName) {\n return get().db.findTableByName(tableName);\n },\n\n findTableByName(tableName: string | QualifiedTableName) {\n const {table, schema, database} = {\n schema: get().db.currentSchema,\n database: get().db.currentDatabase,\n ...(typeof tableName === 'string' ? {table: tableName} : tableName),\n };\n return get().db.tables.find(\n (t) =>\n t.table.table === table &&\n (!schema || t.table.schema === schema) &&\n (!database || t.table.database === database),\n );\n },\n\n async refreshTableSchemas(): Promise<DataTable[]> {\n set((state) =>\n produce(state, (draft) => {\n draft.db.isRefreshingTableSchemas = true;\n }),\n );\n try {\n const connector = await get().db.getConnector();\n const result = await connector.query(\n `SELECT current_schema() AS schema, current_database() AS database`,\n ).result;\n set((state) =>\n produce(state, (draft) => {\n draft.db.currentSchema = result.getChild('schema')?.get(0);\n draft.db.currentDatabase = result.getChild('database')?.get(0);\n }),\n );\n const newTables = await get().db.loadTableSchemas();\n // Only update if there's an actual change in the schemas\n if (!deepEquals(newTables, get().db.tables)) {\n set((state) =>\n produce(state, (draft) => {\n draft.db.tables = newTables;\n draft.db.schemaTrees = createDbSchemaTrees(newTables);\n }),\n );\n }\n return newTables;\n } catch (err) {\n get().project.captureException(err);\n return [];\n } finally {\n set((state) =>\n produce(state, (draft) => {\n draft.db.isRefreshingTableSchemas = false;\n }),\n );\n }\n },\n\n async sqlSelectToJson(sql: string) {\n const connector = await get().db.getConnector();\n const parsedQuery = (\n await connector.query(\n `SELECT json_serialize_sql(${escapeVal(sql)})`,\n ).result\n )\n .getChildAt(0)\n ?.get(0);\n return JSON.parse(parsedQuery);\n },\n\n async executeSql(query: string): Promise<QueryHandle | null> {\n // Create a unique key for this query\n const queryKey = `${query}`;\n const connector = await get().db.getConnector();\n\n // Check if we already have a cached query for this key\n const existingQuery = get().db.queryCache[queryKey];\n if (existingQuery) {\n return existingQuery;\n }\n\n const queryHandle = connector.query(query);\n // Cache the query handle immediately\n set((state) =>\n produce(state, (draft) => {\n draft.db.queryCache[queryKey] = queryHandle;\n }),\n );\n\n queryHandle.result.finally(() => {\n // remove from cache after completion\n set((state) =>\n produce(state, (draft) => {\n delete draft.db.queryCache[queryKey];\n }),\n );\n });\n\n return queryHandle;\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"]}
|