@sqlrooms/duckdb 0.8.1 → 0.9.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 +106 -0
- package/dist/DuckDbSlice.d.ts.map +1 -0
- package/dist/DuckDbSlice.js +152 -0
- package/dist/DuckDbSlice.js.map +1 -0
- package/dist/connectors/DuckDbConnector.d.ts +40 -0
- package/dist/connectors/DuckDbConnector.d.ts.map +1 -0
- package/dist/connectors/DuckDbConnector.js +2 -0
- package/dist/connectors/DuckDbConnector.js.map +1 -0
- package/dist/connectors/WasmDuckDbConnector.d.ts +44 -0
- package/dist/connectors/WasmDuckDbConnector.d.ts.map +1 -0
- package/dist/connectors/WasmDuckDbConnector.js +210 -0
- package/dist/connectors/WasmDuckDbConnector.js.map +1 -0
- package/dist/connectors/load/create.d.ts +33 -0
- package/dist/connectors/load/create.d.ts.map +1 -0
- package/dist/connectors/load/create.js +33 -0
- package/dist/connectors/load/create.js.map +1 -0
- package/dist/connectors/load/load.d.ts +55 -0
- package/dist/connectors/load/load.d.ts.map +1 -0
- package/dist/connectors/load/load.js +147 -0
- package/dist/connectors/load/load.js.map +1 -0
- package/dist/connectors/load/sql-from.d.ts.map +1 -0
- package/dist/{sql-from.js → connectors/load/sql-from.js} +1 -1
- package/dist/connectors/load/sql-from.js.map +1 -0
- package/dist/duckdb-utils.d.ts +47 -0
- package/dist/duckdb-utils.d.ts.map +1 -0
- package/dist/duckdb-utils.js +65 -0
- package/dist/duckdb-utils.js.map +1 -0
- package/dist/exportToCsv.d.ts +3 -1
- package/dist/exportToCsv.d.ts.map +1 -1
- package/dist/exportToCsv.js +29 -24
- package/dist/exportToCsv.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/useDuckDb.d.ts +2 -31
- package/dist/useDuckDb.d.ts.map +1 -1
- package/dist/useDuckDb.js +3 -229
- package/dist/useDuckDb.js.map +1 -1
- package/dist/useSql.d.ts.map +1 -1
- package/dist/useSql.js +4 -3
- package/dist/useSql.js.map +1 -1
- package/package.json +8 -4
- package/dist/__tests__/arrow-utils.test.d.ts +0 -2
- package/dist/__tests__/arrow-utils.test.d.ts.map +0 -1
- package/dist/__tests__/arrow-utils.test.js +0 -187
- package/dist/__tests__/arrow-utils.test.js.map +0 -1
- package/dist/__tests__/sql-from.test.d.ts +0 -2
- package/dist/__tests__/sql-from.test.d.ts.map +0 -1
- package/dist/__tests__/sql-from.test.js +0 -71
- package/dist/__tests__/sql-from.test.js.map +0 -1
- package/dist/duckdb.d.ts +0 -49
- package/dist/duckdb.d.ts.map +0 -1
- package/dist/duckdb.js +0 -82
- package/dist/duckdb.js.map +0 -1
- package/dist/sql-from.d.ts.map +0 -1
- package/dist/sql-from.js.map +0 -1
- /package/dist/{sql-from.d.ts → connectors/load/sql-from.d.ts} +0 -0
package/dist/useDuckDb.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
2
|
-
import
|
|
3
|
-
import { DataTable } from './types';
|
|
2
|
+
import { DuckDbConnector } from './connectors/DuckDbConnector';
|
|
4
3
|
/**
|
|
5
4
|
* @deprecated DuckConn is deprecated, use DuckDb instead
|
|
6
5
|
*/
|
|
@@ -10,33 +9,5 @@ export type DuckDb = {
|
|
|
10
9
|
conn: duckdb.AsyncDuckDBConnection;
|
|
11
10
|
worker: Worker;
|
|
12
11
|
};
|
|
13
|
-
export declare
|
|
14
|
-
readonly cause: unknown;
|
|
15
|
-
readonly query: string | undefined;
|
|
16
|
-
readonly queryCallStack: string | undefined;
|
|
17
|
-
constructor(err: unknown, query: string, stack: string | undefined);
|
|
18
|
-
getMessageForUser(): string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* @deprecated getDuckConn is deprecated, use getDuckDb instead
|
|
22
|
-
*/
|
|
23
|
-
export declare const getDuckConn: typeof getDuckDb;
|
|
24
|
-
export declare function getDuckDb(): Promise<DuckDb>;
|
|
25
|
-
/**
|
|
26
|
-
* @deprecated useDuckConn is deprecated, use useDuckDb instead
|
|
27
|
-
*/
|
|
28
|
-
export declare const useDuckConn: typeof useDuckDb;
|
|
29
|
-
export declare function useDuckDb(): DuckDb;
|
|
30
|
-
export declare const isNumericDuckType: (type: string) => boolean;
|
|
31
|
-
export declare function getColValAsNumber(res: arrow.Table, column?: string | number, index?: number): number;
|
|
32
|
-
export declare const escapeVal: (val: unknown) => string;
|
|
33
|
-
export declare const escapeId: (id: string) => string;
|
|
34
|
-
export declare function getDuckTables(schema?: string): Promise<string[]>;
|
|
35
|
-
export declare function getDuckTableSchema(tableName: string, schema?: string): Promise<DataTable>;
|
|
36
|
-
export declare function getDuckTableSchemas(schema?: string): Promise<DataTable[]>;
|
|
37
|
-
export declare function checkTableExists(tableName: string, schema?: string): Promise<boolean>;
|
|
38
|
-
export declare function dropAllTables(schema?: string): Promise<void>;
|
|
39
|
-
export declare function dropTable(tableName: string): Promise<void>;
|
|
40
|
-
export declare function dropFile(fname: string): Promise<void>;
|
|
41
|
-
export declare function dropAllFiles(): Promise<void>;
|
|
12
|
+
export declare function useDuckDb(): DuckDbConnector;
|
|
42
13
|
//# sourceMappingURL=useDuckDb.d.ts.map
|
package/dist/useDuckDb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDuckDb.d.ts","sourceRoot":"","sources":["../src/useDuckDb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"useDuckDb.d.ts","sourceRoot":"","sources":["../src/useDuckDb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAC;AAE9C,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,MAAM,MAAM,MAAM,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC,qBAAqB,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,SAAS,IAAI,eAAe,CAG3C"}
|
package/dist/useDuckDb.js
CHANGED
|
@@ -1,232 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
const ENABLE_DUCK_LOGGING = false;
|
|
3
|
-
const SilentLogger = {
|
|
4
|
-
log: () => {
|
|
5
|
-
/* do nothing */
|
|
6
|
-
},
|
|
7
|
-
};
|
|
8
|
-
// TODO: shut DB down at some point
|
|
9
|
-
let duckConn;
|
|
10
|
-
let initialize;
|
|
11
|
-
export class DuckQueryError extends Error {
|
|
12
|
-
cause;
|
|
13
|
-
query;
|
|
14
|
-
queryCallStack;
|
|
15
|
-
constructor(err, query, stack) {
|
|
16
|
-
super(`DB query failed: ${err instanceof Error ? err.message : err}\n\nFull query:\n\n${query}\n\nQuery call stack:\n\n${stack}\n\n`);
|
|
17
|
-
this.cause = err;
|
|
18
|
-
this.query = query;
|
|
19
|
-
this.queryCallStack = stack;
|
|
20
|
-
Object.setPrototypeOf(this, DuckQueryError.prototype);
|
|
21
|
-
}
|
|
22
|
-
getMessageForUser() {
|
|
23
|
-
const msg = this.cause instanceof Error ? this.cause.message : this.message;
|
|
24
|
-
return msg;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* @deprecated getDuckConn is deprecated, use getDuckDb instead
|
|
29
|
-
*/
|
|
30
|
-
export const getDuckConn = getDuckDb;
|
|
31
|
-
export async function getDuckDb() {
|
|
32
|
-
if (!globalThis.Worker) {
|
|
33
|
-
return Promise.reject('No Worker support');
|
|
34
|
-
}
|
|
35
|
-
if (duckConn) {
|
|
36
|
-
return duckConn;
|
|
37
|
-
}
|
|
38
|
-
else if (initialize !== undefined) {
|
|
39
|
-
// The initialization has already been started, wait for it to finish
|
|
40
|
-
return initialize;
|
|
41
|
-
}
|
|
42
|
-
let resolve;
|
|
43
|
-
let reject;
|
|
44
|
-
initialize = new Promise((_resolve, _reject) => {
|
|
45
|
-
resolve = _resolve;
|
|
46
|
-
reject = _reject;
|
|
47
|
-
});
|
|
48
|
-
try {
|
|
49
|
-
// TODO: Consider to load locally https://github.com/duckdb/duckdb-wasm/issues/1425#issuecomment-1742156605
|
|
50
|
-
const allBundles = duckdb.getJsDelivrBundles();
|
|
51
|
-
const bestBundle = await duckdb.selectBundle(allBundles);
|
|
52
|
-
if (!bestBundle.mainWorker) {
|
|
53
|
-
throw new Error('No best bundle found for DuckDB worker');
|
|
54
|
-
}
|
|
55
|
-
const workerUrl = URL.createObjectURL(new Blob([`importScripts("${bestBundle.mainWorker}");`], {
|
|
56
|
-
type: 'text/javascript',
|
|
57
|
-
}));
|
|
58
|
-
// const worker = await duckdb.createWorker(bestBundle.mainWorker);
|
|
59
|
-
const worker = new window.Worker(workerUrl);
|
|
60
|
-
const logger = ENABLE_DUCK_LOGGING
|
|
61
|
-
? new duckdb.ConsoleLogger()
|
|
62
|
-
: SilentLogger;
|
|
63
|
-
const db = new (class extends duckdb.AsyncDuckDB {
|
|
64
|
-
onError(event) {
|
|
65
|
-
super.onError(event);
|
|
66
|
-
console.error('onError', event);
|
|
67
|
-
}
|
|
68
|
-
})(logger, worker);
|
|
69
|
-
await db.instantiate(bestBundle.mainModule, bestBundle.pthreadWorker);
|
|
70
|
-
URL.revokeObjectURL(workerUrl);
|
|
71
|
-
await db.open({
|
|
72
|
-
path: ':memory:',
|
|
73
|
-
query: {
|
|
74
|
-
// castBigIntToDouble: true
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
const conn = await db.connect();
|
|
78
|
-
// Replace conn.query to include full query in the error message
|
|
79
|
-
const connQuery = conn.query;
|
|
80
|
-
conn.query = (async (q) => {
|
|
81
|
-
const stack = new Error().stack;
|
|
82
|
-
try {
|
|
83
|
-
return await connQuery.call(conn, q);
|
|
84
|
-
}
|
|
85
|
-
catch (err) {
|
|
86
|
-
throw new DuckQueryError(err, q, stack);
|
|
87
|
-
// throw new Error(
|
|
88
|
-
// `Query failed: ${err}\n\nFull query:\n\n${q}\n\nQuery call stack:\n\n${stack}\n\n`,
|
|
89
|
-
// );
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
await conn.query(`
|
|
93
|
-
SET max_expression_depth TO 100000;
|
|
94
|
-
SET memory_limit = '10GB';
|
|
95
|
-
`);
|
|
96
|
-
duckConn = { db, conn, worker };
|
|
97
|
-
resolve(duckConn);
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
reject(err);
|
|
101
|
-
throw err;
|
|
102
|
-
}
|
|
103
|
-
return duckConn;
|
|
104
|
-
}
|
|
105
|
-
// Cache the promise to avoid multiple initialization attempts
|
|
106
|
-
let duckPromise = null;
|
|
107
|
-
/**
|
|
108
|
-
* @deprecated useDuckConn is deprecated, use useDuckDb instead
|
|
109
|
-
*/
|
|
110
|
-
export const useDuckConn = useDuckDb;
|
|
1
|
+
import { useStoreWithDuckDb } from './DuckDbSlice';
|
|
111
2
|
export function useDuckDb() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
// If we don't have a connection yet, throw the promise
|
|
116
|
-
// This will trigger Suspense
|
|
117
|
-
if (!duckConn) {
|
|
118
|
-
throw duckPromise;
|
|
119
|
-
}
|
|
120
|
-
return duckConn;
|
|
121
|
-
}
|
|
122
|
-
export const isNumericDuckType = (type) => type.indexOf('INT') >= 0 ||
|
|
123
|
-
type.indexOf('DECIMAL') >= 0 ||
|
|
124
|
-
type.indexOf('FLOAT') >= 0 ||
|
|
125
|
-
type.indexOf('REAL') >= 0 ||
|
|
126
|
-
type.indexOf('DOUBLE') >= 0;
|
|
127
|
-
export function getColValAsNumber(res, column = 0, index = 0) {
|
|
128
|
-
const v = (typeof column === 'number' ? res.getChildAt(column) : res.getChild(column))?.get(index);
|
|
129
|
-
if (v === undefined || v === null) {
|
|
130
|
-
return NaN;
|
|
131
|
-
}
|
|
132
|
-
// if it's an array (can be returned by duckdb as bigint)
|
|
133
|
-
return Number(v[0] ?? v);
|
|
134
|
-
}
|
|
135
|
-
export const escapeVal = (val) => {
|
|
136
|
-
return `'${String(val).replace(/'/g, "''")}'`;
|
|
137
|
-
};
|
|
138
|
-
export const escapeId = (id) => {
|
|
139
|
-
const str = String(id);
|
|
140
|
-
if (str.startsWith('"') && str.endsWith('"')) {
|
|
141
|
-
return str;
|
|
142
|
-
}
|
|
143
|
-
return `"${str.replace(/"/g, '""')}"`;
|
|
144
|
-
};
|
|
145
|
-
export async function getDuckTables(schema = 'main') {
|
|
146
|
-
const { conn } = await getDuckDb();
|
|
147
|
-
const tablesResults = await conn.query(`SELECT * FROM information_schema.tables
|
|
148
|
-
WHERE table_schema = '${schema}'
|
|
149
|
-
ORDER BY table_name`);
|
|
150
|
-
const tableNames = [];
|
|
151
|
-
for (let i = 0; i < tablesResults.numRows; i++) {
|
|
152
|
-
tableNames.push(tablesResults.getChild('table_name')?.get(i));
|
|
153
|
-
}
|
|
154
|
-
return tableNames;
|
|
155
|
-
}
|
|
156
|
-
export async function getDuckTableSchema(tableName, schema = 'main') {
|
|
157
|
-
const { conn } = await getDuckDb();
|
|
158
|
-
const describeResults = await conn.query(`DESCRIBE ${schema}.${tableName}`);
|
|
159
|
-
const columnNames = describeResults.getChild('column_name');
|
|
160
|
-
const columnTypes = describeResults.getChild('column_type');
|
|
161
|
-
const columns = [];
|
|
162
|
-
for (let di = 0; di < describeResults.numRows; di++) {
|
|
163
|
-
const columnName = columnNames?.get(di);
|
|
164
|
-
const columnType = columnTypes?.get(di);
|
|
165
|
-
columns.push({ name: columnName, type: columnType });
|
|
166
|
-
}
|
|
167
|
-
return {
|
|
168
|
-
tableName,
|
|
169
|
-
columns,
|
|
170
|
-
// Costly to get the row count for large tables
|
|
171
|
-
// rowCount: getColValAsNumber(
|
|
172
|
-
// await conn.query(`SELECT COUNT(*) FROM ${schema}.${tableName}`),
|
|
173
|
-
// ),
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
export async function getDuckTableSchemas(schema = 'main') {
|
|
177
|
-
const tableNames = await getDuckTables(schema);
|
|
178
|
-
const tablesInfo = [];
|
|
179
|
-
for (const tableName of tableNames) {
|
|
180
|
-
tablesInfo.push(await getDuckTableSchema(tableName, schema));
|
|
181
|
-
}
|
|
182
|
-
return tablesInfo;
|
|
183
|
-
}
|
|
184
|
-
export async function checkTableExists(tableName, schema = 'main') {
|
|
185
|
-
const { conn } = await getDuckDb();
|
|
186
|
-
const res = await conn.query(`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${schema}' AND table_name = '${tableName}'`);
|
|
187
|
-
return getColValAsNumber(res) > 0;
|
|
188
|
-
}
|
|
189
|
-
export async function dropAllTables(schema) {
|
|
190
|
-
try {
|
|
191
|
-
const { conn } = await getDuckDb();
|
|
192
|
-
if (schema && schema !== 'main') {
|
|
193
|
-
await conn.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
const res = await conn.query(`SELECT table_name, table_schema, table_type FROM information_schema.tables${schema ? ` WHERE table_schema = '${schema}'` : ''}`);
|
|
197
|
-
const schemasCol = res.getChild('table_schema');
|
|
198
|
-
const tableNamesCol = res.getChild('table_name');
|
|
199
|
-
const tableTypesCol = res.getChild('table_type');
|
|
200
|
-
for (let i = 0; i < res.numRows; i++) {
|
|
201
|
-
try {
|
|
202
|
-
const schemaName = schemasCol?.get(i);
|
|
203
|
-
const tableName = tableNamesCol?.get(i);
|
|
204
|
-
const tableType = tableTypesCol?.get(i);
|
|
205
|
-
if (tableName) {
|
|
206
|
-
const query = `DROP ${tableType === 'VIEW' ? 'VIEW' : 'TABLE'} IF EXISTS ${schemaName}.${tableName}`;
|
|
207
|
-
await conn.query(query);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
catch (err) {
|
|
211
|
-
console.error(err);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
console.error(err);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
export async function dropTable(tableName) {
|
|
221
|
-
const { conn } = await getDuckDb();
|
|
222
|
-
await conn.query(`DROP TABLE IF EXISTS ${tableName};`);
|
|
223
|
-
}
|
|
224
|
-
export async function dropFile(fname) {
|
|
225
|
-
const { db } = await getDuckDb();
|
|
226
|
-
db.dropFile(fname);
|
|
227
|
-
}
|
|
228
|
-
export async function dropAllFiles() {
|
|
229
|
-
const { db } = await getDuckDb();
|
|
230
|
-
db.dropFiles();
|
|
3
|
+
const connector = useStoreWithDuckDb((state) => state.db.connector);
|
|
4
|
+
return connector;
|
|
231
5
|
}
|
|
232
6
|
//# sourceMappingURL=useDuckDb.js.map
|
package/dist/useDuckDb.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDuckDb.js","sourceRoot":"","sources":["../src/useDuckDb.ts"],"names":[],"mappings":"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","sourcesContent":["import * as duckdb from '@duckdb/duckdb-wasm';\nimport * as arrow from 'apache-arrow';\nimport {DataTable, TableColumn} from './types';\nimport {useState, useEffect} from 'react';\n\n/**\n * @deprecated DuckConn is deprecated, use DuckDb instead\n */\nexport type DuckConn = DuckDb;\n\nexport type DuckDb = {\n db: duckdb.AsyncDuckDB;\n conn: duckdb.AsyncDuckDBConnection;\n worker: Worker;\n};\n\nconst ENABLE_DUCK_LOGGING = false;\n\nconst SilentLogger = {\n log: () => {\n /* do nothing */\n },\n};\n\n// TODO: shut DB down at some point\n\nlet duckConn: DuckDb;\nlet initialize: Promise<DuckDb> | undefined;\n\nexport class DuckQueryError extends Error {\n readonly cause: unknown;\n readonly query: string | undefined;\n readonly queryCallStack: string | undefined;\n constructor(err: unknown, query: string, stack: string | undefined) {\n super(\n `DB query failed: ${\n err instanceof Error ? err.message : err\n }\\n\\nFull query:\\n\\n${query}\\n\\nQuery call stack:\\n\\n${stack}\\n\\n`,\n );\n this.cause = err;\n this.query = query;\n this.queryCallStack = stack;\n Object.setPrototypeOf(this, DuckQueryError.prototype);\n }\n getMessageForUser() {\n const msg = this.cause instanceof Error ? this.cause.message : this.message;\n return msg;\n }\n}\n\n/**\n * @deprecated getDuckConn is deprecated, use getDuckDb instead\n */\nexport const getDuckConn = getDuckDb;\n\nexport async function getDuckDb(): Promise<DuckDb> {\n if (!globalThis.Worker) {\n return Promise.reject('No Worker support');\n }\n if (duckConn) {\n return duckConn;\n } else if (initialize !== undefined) {\n // The initialization has already been started, wait for it to finish\n return initialize;\n }\n\n let resolve: (value: DuckDb) => void;\n let reject: (reason?: unknown) => void;\n initialize = new Promise((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n });\n\n try {\n // TODO: Consider to load locally https://github.com/duckdb/duckdb-wasm/issues/1425#issuecomment-1742156605\n const allBundles = duckdb.getJsDelivrBundles();\n const bestBundle = await duckdb.selectBundle(allBundles);\n if (!bestBundle.mainWorker) {\n throw new Error('No best bundle found for DuckDB worker');\n }\n const workerUrl = URL.createObjectURL(\n new Blob([`importScripts(\"${bestBundle.mainWorker}\");`], {\n type: 'text/javascript',\n }),\n );\n // const worker = await duckdb.createWorker(bestBundle.mainWorker);\n const worker = new window.Worker(workerUrl);\n const logger = ENABLE_DUCK_LOGGING\n ? new duckdb.ConsoleLogger()\n : SilentLogger;\n const db = new (class extends duckdb.AsyncDuckDB {\n onError(event: ErrorEvent) {\n super.onError(event);\n console.error('onError', event);\n }\n })(logger, worker);\n await db.instantiate(bestBundle.mainModule, bestBundle.pthreadWorker);\n URL.revokeObjectURL(workerUrl);\n await db.open({\n path: ':memory:',\n query: {\n // castBigIntToDouble: true\n },\n });\n const conn = await db.connect();\n // Replace conn.query to include full query in the error message\n const connQuery = conn.query;\n conn.query = (async (q: string) => {\n const stack = new Error().stack;\n try {\n return await connQuery.call(conn, q);\n } catch (err) {\n throw new DuckQueryError(err, q, stack);\n // throw new Error(\n // `Query failed: ${err}\\n\\nFull query:\\n\\n${q}\\n\\nQuery call stack:\\n\\n${stack}\\n\\n`,\n // );\n }\n }) as typeof conn.query;\n await conn.query(`\n SET max_expression_depth TO 100000;\n SET memory_limit = '10GB';\n `);\n duckConn = {db, conn, worker};\n resolve!(duckConn);\n } catch (err) {\n reject!(err);\n throw err;\n }\n\n return duckConn;\n}\n\n// Cache the promise to avoid multiple initialization attempts\nlet duckPromise: Promise<DuckDb> | null = null;\n\n/**\n * @deprecated useDuckConn is deprecated, use useDuckDb instead\n */\nexport const useDuckConn = useDuckDb;\n\nexport function useDuckDb(): DuckDb {\n if (!duckPromise) {\n duckPromise = getDuckDb();\n }\n\n // If we don't have a connection yet, throw the promise\n // This will trigger Suspense\n if (!duckConn) {\n throw duckPromise;\n }\n\n return duckConn;\n}\n\nexport const isNumericDuckType = (type: string) =>\n type.indexOf('INT') >= 0 ||\n type.indexOf('DECIMAL') >= 0 ||\n type.indexOf('FLOAT') >= 0 ||\n type.indexOf('REAL') >= 0 ||\n type.indexOf('DOUBLE') >= 0;\n\nexport function getColValAsNumber(\n res: arrow.Table,\n column: string | number = 0,\n index = 0,\n): number {\n const v = (\n typeof column === 'number' ? res.getChildAt(column) : res.getChild(column)\n )?.get(index);\n if (v === undefined || v === null) {\n return NaN;\n }\n // if it's an array (can be returned by duckdb as bigint)\n return Number(v[0] ?? v);\n}\n\nexport const escapeVal = (val: unknown) => {\n return `'${String(val).replace(/'/g, \"''\")}'`;\n};\n\nexport const escapeId = (id: string) => {\n const str = String(id);\n if (str.startsWith('\"') && str.endsWith('\"')) {\n return str;\n }\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n};\n\nexport async function getDuckTables(schema = 'main'): Promise<string[]> {\n const {conn} = await getDuckDb();\n const tablesResults = await conn.query(\n `SELECT * FROM information_schema.tables \n 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\nexport async function getDuckTableSchema(\n tableName: string,\n schema = 'main',\n): Promise<DataTable> {\n const {conn} = await getDuckDb();\n const describeResults = await conn.query(`DESCRIBE ${schema}.${tableName}`);\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 // Costly to get the row count for large tables\n // rowCount: getColValAsNumber(\n // await conn.query(`SELECT COUNT(*) FROM ${schema}.${tableName}`),\n // ),\n };\n}\n\nexport async function getDuckTableSchemas(\n schema = 'main',\n): Promise<DataTable[]> {\n const tableNames = await getDuckTables(schema);\n const tablesInfo: DataTable[] = [];\n for (const tableName of tableNames) {\n tablesInfo.push(await getDuckTableSchema(tableName, schema));\n }\n return tablesInfo;\n}\n\nexport async function checkTableExists(\n tableName: string,\n schema = 'main',\n): Promise<boolean> {\n const {conn} = await getDuckDb();\n const res = await conn.query(\n `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${schema}' AND table_name = '${tableName}'`,\n );\n return getColValAsNumber(res) > 0;\n}\n\nexport async function dropAllTables(schema?: string): Promise<void> {\n try {\n const {conn} = await getDuckDb();\n if (schema && schema !== 'main') {\n await conn.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);\n } else {\n const res = await conn.query(\n `SELECT table_name, table_schema, table_type FROM information_schema.tables${\n schema ? ` WHERE table_schema = '${schema}'` : ''\n }`,\n );\n const schemasCol = res.getChild('table_schema');\n const tableNamesCol = res.getChild('table_name');\n const tableTypesCol = res.getChild('table_type');\n for (let i = 0; i < res.numRows; i++) {\n try {\n const schemaName = schemasCol?.get(i);\n const tableName = tableNamesCol?.get(i);\n const tableType = tableTypesCol?.get(i);\n if (tableName) {\n const query = `DROP ${\n tableType === 'VIEW' ? 'VIEW' : 'TABLE'\n } IF EXISTS ${schemaName}.${tableName}`;\n await conn.query(query);\n }\n } catch (err) {\n console.error(err);\n }\n }\n }\n } catch (err) {\n console.error(err);\n }\n}\n\nexport async function dropTable(tableName: string): Promise<void> {\n const {conn} = await getDuckDb();\n await conn.query(`DROP TABLE IF EXISTS ${tableName};`);\n}\n\nexport async function dropFile(fname: string): Promise<void> {\n const {db} = await getDuckDb();\n db.dropFile(fname);\n}\n\nexport async function dropAllFiles(): Promise<void> {\n const {db} = await getDuckDb();\n db.dropFiles();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useDuckDb.js","sourceRoot":"","sources":["../src/useDuckDb.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAcjD,MAAM,UAAU,SAAS;IACvB,MAAM,SAAS,GAAG,kBAAkB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;IACpE,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import * as duckdb from '@duckdb/duckdb-wasm';\nimport {useStoreWithDuckDb} from './DuckDbSlice';\nimport {DuckDbConnector} from './connectors/DuckDbConnector';\n\n/**\n * @deprecated DuckConn is deprecated, use DuckDb instead\n */\nexport type DuckConn = DuckDb;\n\nexport type DuckDb = {\n db: duckdb.AsyncDuckDB;\n conn: duckdb.AsyncDuckDBConnection;\n worker: Worker;\n};\n\nexport function useDuckDb(): DuckDbConnector {\n const connector = useStoreWithDuckDb((state) => state.db.connector);\n return connector;\n}\n"]}
|
package/dist/useSql.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSql.d.ts","sourceRoot":"","sources":["../src/useSql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"useSql.d.ts","sourceRoot":"","sources":["../src/useSql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,iCAAiC;IACjC,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC;IACxB,yEAAyE;IACzE,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC;IACzB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC5B,wDAAwD;IACxD,OAAO,IAAI,CAAC,EAAE,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC;AA+CxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4IG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAC,GAAG;IACxE,IAAI,EAAE,iBAAiB,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACzC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAgB,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC,OAAO,EAC7C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IACP,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GACA;IACD,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IACrD,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAwEF;;GAEG;AACH,eAAO,MAAM,cAAc,eAAS,CAAC"}
|
package/dist/useSql.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useStoreWithDuckDb } from './DuckDbSlice';
|
|
3
3
|
/**
|
|
4
4
|
* Creates a row accessor wrapper around an Arrow table that provides typed row access.
|
|
5
5
|
*/
|
|
@@ -50,6 +50,7 @@ export function useSql(schemaOrOptions, maybeOptions) {
|
|
|
50
50
|
const [data, setData] = useState(undefined);
|
|
51
51
|
const [error, setError] = useState(null);
|
|
52
52
|
const [isLoading, setIsLoading] = useState(false);
|
|
53
|
+
const getConnector = useStoreWithDuckDb((state) => state.db.getConnector);
|
|
53
54
|
useEffect(() => {
|
|
54
55
|
let isMounted = true;
|
|
55
56
|
const fetchData = async () => {
|
|
@@ -59,8 +60,8 @@ export function useSql(schemaOrOptions, maybeOptions) {
|
|
|
59
60
|
setIsLoading(true);
|
|
60
61
|
setError(null);
|
|
61
62
|
try {
|
|
62
|
-
const
|
|
63
|
-
const result = await
|
|
63
|
+
const connector = await getConnector();
|
|
64
|
+
const result = await connector.query(options.query);
|
|
64
65
|
// Create a row accessor that optionally validates with the schema
|
|
65
66
|
const rowAccessor = createTypedRowAccessor({
|
|
66
67
|
arrowTable: result,
|
package/dist/useSql.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSql.js","sourceRoot":"","sources":["../src/useSql.ts"],"names":[],"mappings":"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","sourcesContent":["import * as arrow from 'apache-arrow';\nimport {useEffect, useState} from 'react';\nimport {getDuckDb} from './useDuckDb';\nimport {z} from 'zod';\n\n/**\n * A wrapper interface that exposes the underlying Arrow table,\n * a typed row accessor, and the number of rows.\n */\nexport interface UseSqlQueryResult<T> {\n /** The underlying Arrow table */\n arrowTable: arrow.Table;\n /** Returns a typed row at the specified index by converting on demand */\n getRow(index: number): T;\n /** Number of rows in the table */\n length: number;\n /** Returns an iterator that yields each row in the table */\n rows(): IterableIterator<T>;\n /** Returns an array containing all rows in the table */\n toArray(): T[];\n}\n\n/**\n * @deprecated Use UseSqlQueryResult instead\n */\nexport type DuckDbQueryResult<T> = UseSqlQueryResult<T>;\n\n/**\n * Creates a row accessor wrapper around an Arrow table that provides typed row access.\n */\nfunction createTypedRowAccessor<T>({\n arrowTable,\n validate,\n}: {\n arrowTable: arrow.Table;\n validate?: (row: unknown) => T;\n}): UseSqlQueryResult<T> {\n return {\n arrowTable,\n get length() {\n return arrowTable.numRows;\n },\n getRow(index: number): T {\n const row: Record<string, unknown> = {};\n arrowTable.schema.fields.forEach((field: arrow.Field) => {\n const column = arrowTable.getChild(field.name);\n if (column) {\n row[field.name] = column.get(index);\n }\n });\n\n // If a validator is provided, use it to validate/parse the row\n if (validate) {\n return validate(row);\n }\n return row as T;\n },\n *rows(): IterableIterator<T> {\n for (let i = 0; i < this.length; i++) {\n yield this.getRow(i);\n }\n },\n toArray(): T[] {\n const result: T[] = [];\n for (let i = 0; i < this.length; i++) {\n result.push(this.getRow(i));\n }\n return result;\n },\n };\n}\n\n/**\n * A React hook for executing SQL queries with automatic state management.\n * Provides two ways to ensure type safety:\n * 1. Using TypeScript types (compile-time safety only)\n * 2. Using Zod schemas (both compile-time and runtime validation)\n *\n * @example\n * ```typescript\n * // Option 1: Using TypeScript types (faster, no runtime validation)\n * interface User {\n * id: number;\n * name: string;\n * email: string;\n * }\n *\n * const {data, isLoading, error} = useSql<User>({\n * query: 'SELECT id, name, email FROM users'\n * });\n *\n * // Option 2: Using Zod schema (slower but with runtime validation)\n * const userSchema = z.object({\n * id: z.number(),\n * name: z.string(),\n * email: z.string().email(),\n * createdAt: z.string().transform(str => new Date(str)) // Transform string to Date\n * });\n *\n * const {data: validatedData, isLoading, error} = useSql(\n * userSchema,\n * {query: 'SELECT id, name, email, created_at as createdAt FROM users'}\n * );\n * ```\n *\n * ## Error Handling\n * ```typescript\n * if (isLoading) return <div>Loading...</div>;\n * if (error) {\n * // With Zod, you can catch validation errors specifically\n * if (error instanceof z.ZodError) {\n * return <div>Validation Error: {error.errors[0].message}</div>;\n * }\n * return <div>Error: {error.message}</div>;\n * }\n * if (!data) return null;\n * ```\n *\n * ## Data Access Methods\n *\n * There are several ways to access data with different performance characteristics:\n *\n * ### 1. Typed Row Access (getRow, rows(), toArray())\n * - Provides type safety and validation\n * - Converts data to JavaScript objects\n * - Slower for large datasets due to object creation and validation\n *\n * ```typescript\n * // Iterate through rows using the rows() iterator (recommended)\n * for (const user of data.rows()) {\n * console.log(user.name, user.email);\n * }\n *\n * // Traditional for loop with index access\n * for (let i = 0; i < data.length; i++) {\n * const user = data.getRow(i);\n * console.log(`User ${i}: ${user.name} (${user.email})`);\n * }\n *\n * // Get all rows as an array\n * const allUsers = data.toArray();\n *\n * // With Zod schema, transformed fields are available\n * for (const user of validatedData.rows()) {\n * console.log(`Created: ${user.createdAt.toISOString()}`); // createdAt is a Date object\n * }\n * ```\n *\n * ### 2. Direct Arrow Table Access\n * - Much faster for large datasets\n * - Columnar access is more efficient for analytics\n * - No type safety or validation\n *\n * ```typescript\n * // For performance-critical operations with large datasets:\n * const nameColumn = data.arrowTable.getChild('name');\n * const emailColumn = data.arrowTable.getChild('email');\n *\n * // Fast columnar iteration (no object creation)\n * for (let i = 0; i < data.length; i++) {\n * console.log(nameColumn.get(i), emailColumn.get(i));\n * }\n *\n * // Note: For filtering data, it's most efficient to use SQL in your query\n * const { data } = useSql<User>({\n * query: \"SELECT * FROM users WHERE age > 30\"\n * });\n * ```\n *\n * ### 3. Using Flechette for Advanced Operations\n *\n * For more advanced Arrow operations, consider using [Flechette](https://idl.uw.edu/flechette/),\n * a faster and lighter alternative to the standard Arrow JS implementation.\n *\n * ```typescript\n * // Example using Flechette with SQL query results\n * import { tableFromIPC } from '@uwdata/flechette';\n *\n * // Convert Arrow table to Flechette table\n * const serializedData = data.arrowTable.serialize();\n * const flechetteTable = tableFromIPC(serializedData);\n *\n * // Extract all columns into a { name: array, ... } object\n * const columns = flechetteTable.toColumns();\n *\n * // Create a new table with a selected subset of columns\n * const subtable = flechetteTable.select(['name', 'email']);\n *\n * // Convert to array of objects with customization options\n * const objects = flechetteTable.toArray({\n * useDate: true, // Convert timestamps to Date objects\n * useMap: true // Create Map objects for key-value pairs\n * });\n *\n * // For large datasets, consider memory management\n * serializedData = null; // Allow garbage collection of the serialized data\n * ```\n *\n * Flechette provides several advantages:\n * - Better performance (1.3-1.6x faster value iteration, 7-11x faster row object extraction)\n * - Smaller footprint (~43k minified vs 163k for Arrow JS)\n * - Support for additional data types (including decimal-to-number conversion)\n * - More flexible data value conversion options\n *\n * @template Row The TypeScript type for each row in the result\n * @param options Configuration object containing the query and execution control\n * @returns Object containing the query result, loading state, and any error\n *\n * @template Schema The Zod schema type that defines the shape and validation of each row\n * @param schema A Zod schema that defines the expected shape and validation rules for each row\n * @param options Configuration object containing the query and execution control\n * @returns Object containing the validated query result, loading state, and any error\n */\nexport function useSql<Row>(options: {query: string; enabled?: boolean}): {\n data: UseSqlQueryResult<Row> | undefined;\n error: Error | null;\n isLoading: boolean;\n};\n\nexport function useSql<Schema extends z.ZodType>(\n schema: Schema,\n options: {\n query: string;\n enabled?: boolean;\n },\n): {\n data: UseSqlQueryResult<z.infer<Schema>> | undefined;\n error: Error | null;\n isLoading: boolean;\n};\n\n/**\n * Implementation of useSql that handles both overloads\n */\nexport function useSql<Row, Schema extends z.ZodType = z.ZodType>(\n schemaOrOptions: Schema | {query: string; enabled?: boolean},\n maybeOptions?: {query: string; enabled?: boolean},\n) {\n // Determine if we're using the schema overload\n const hasSchema = maybeOptions !== undefined;\n const options = hasSchema\n ? maybeOptions\n : (schemaOrOptions as {query: string; enabled?: boolean});\n const schema = hasSchema ? (schemaOrOptions as Schema) : undefined;\n\n const [data, setData] = useState<UseSqlQueryResult<Row> | undefined>(\n undefined,\n );\n const [error, setError] = useState<Error | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n\n useEffect(() => {\n let isMounted = true;\n\n const fetchData = async () => {\n if (!options.enabled && options.enabled !== undefined) {\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const duckDb = await getDuckDb();\n const result = await duckDb.conn.query(options.query);\n\n // Create a row accessor that optionally validates with the schema\n const rowAccessor = createTypedRowAccessor<Row>({\n arrowTable: result,\n validate: schema ? (row: unknown) => schema.parse(row) : undefined,\n });\n\n if (isMounted) {\n setData(rowAccessor);\n }\n } catch (err) {\n if (isMounted) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n if (isMounted) {\n setIsLoading(false);\n }\n }\n };\n\n fetchData();\n\n return () => {\n isMounted = false;\n };\n }, [options.query, options.enabled]);\n\n return {\n data,\n error,\n isLoading,\n };\n}\n\n/**\n * @deprecated Use useSql instead\n */\nexport const useDuckDbQuery = useSql;\n"]}
|
|
1
|
+
{"version":3,"file":"useSql.js","sourceRoot":"","sources":["../src/useSql.ts"],"names":[],"mappings":"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","sourcesContent":["import * as arrow from 'apache-arrow';\nimport {useEffect, useState} from 'react';\nimport {z} from 'zod';\nimport {useStoreWithDuckDb} from './DuckDbSlice';\n\n/**\n * A wrapper interface that exposes the underlying Arrow table,\n * a typed row accessor, and the number of rows.\n */\nexport interface UseSqlQueryResult<T> {\n /** The underlying Arrow table */\n arrowTable: arrow.Table;\n /** Returns a typed row at the specified index by converting on demand */\n getRow(index: number): T;\n /** Number of rows in the table */\n length: number;\n /** Returns an iterator that yields each row in the table */\n rows(): IterableIterator<T>;\n /** Returns an array containing all rows in the table */\n toArray(): T[];\n}\n\n/**\n * @deprecated Use UseSqlQueryResult instead\n */\nexport type DuckDbQueryResult<T> = UseSqlQueryResult<T>;\n\n/**\n * Creates a row accessor wrapper around an Arrow table that provides typed row access.\n */\nfunction createTypedRowAccessor<T>({\n arrowTable,\n validate,\n}: {\n arrowTable: arrow.Table;\n validate?: (row: unknown) => T;\n}): UseSqlQueryResult<T> {\n return {\n arrowTable,\n get length() {\n return arrowTable.numRows;\n },\n getRow(index: number): T {\n const row: Record<string, unknown> = {};\n arrowTable.schema.fields.forEach((field: arrow.Field) => {\n const column = arrowTable.getChild(field.name);\n if (column) {\n row[field.name] = column.get(index);\n }\n });\n\n // If a validator is provided, use it to validate/parse the row\n if (validate) {\n return validate(row);\n }\n return row as T;\n },\n *rows(): IterableIterator<T> {\n for (let i = 0; i < this.length; i++) {\n yield this.getRow(i);\n }\n },\n toArray(): T[] {\n const result: T[] = [];\n for (let i = 0; i < this.length; i++) {\n result.push(this.getRow(i));\n }\n return result;\n },\n };\n}\n\n/**\n * A React hook for executing SQL queries with automatic state management.\n * Provides two ways to ensure type safety:\n * 1. Using TypeScript types (compile-time safety only)\n * 2. Using Zod schemas (both compile-time and runtime validation)\n *\n * @example\n * ```typescript\n * // Option 1: Using TypeScript types (faster, no runtime validation)\n * interface User {\n * id: number;\n * name: string;\n * email: string;\n * }\n *\n * const {data, isLoading, error} = useSql<User>({\n * query: 'SELECT id, name, email FROM users'\n * });\n *\n * // Option 2: Using Zod schema (slower but with runtime validation)\n * const userSchema = z.object({\n * id: z.number(),\n * name: z.string(),\n * email: z.string().email(),\n * createdAt: z.string().transform(str => new Date(str)) // Transform string to Date\n * });\n *\n * const {data: validatedData, isLoading, error} = useSql(\n * userSchema,\n * {query: 'SELECT id, name, email, created_at as createdAt FROM users'}\n * );\n * ```\n *\n * ## Error Handling\n * ```typescript\n * if (isLoading) return <div>Loading...</div>;\n * if (error) {\n * // With Zod, you can catch validation errors specifically\n * if (error instanceof z.ZodError) {\n * return <div>Validation Error: {error.errors[0].message}</div>;\n * }\n * return <div>Error: {error.message}</div>;\n * }\n * if (!data) return null;\n * ```\n *\n * ## Data Access Methods\n *\n * There are several ways to access data with different performance characteristics:\n *\n * ### 1. Typed Row Access (getRow, rows(), toArray())\n * - Provides type safety and validation\n * - Converts data to JavaScript objects\n * - Slower for large datasets due to object creation and validation\n *\n * ```typescript\n * // Iterate through rows using the rows() iterator (recommended)\n * for (const user of data.rows()) {\n * console.log(user.name, user.email);\n * }\n *\n * // Traditional for loop with index access\n * for (let i = 0; i < data.length; i++) {\n * const user = data.getRow(i);\n * console.log(`User ${i}: ${user.name} (${user.email})`);\n * }\n *\n * // Get all rows as an array\n * const allUsers = data.toArray();\n *\n * // With Zod schema, transformed fields are available\n * for (const user of validatedData.rows()) {\n * console.log(`Created: ${user.createdAt.toISOString()}`); // createdAt is a Date object\n * }\n * ```\n *\n * ### 2. Direct Arrow Table Access\n * - Much faster for large datasets\n * - Columnar access is more efficient for analytics\n * - No type safety or validation\n *\n * ```typescript\n * // For performance-critical operations with large datasets:\n * const nameColumn = data.arrowTable.getChild('name');\n * const emailColumn = data.arrowTable.getChild('email');\n *\n * // Fast columnar iteration (no object creation)\n * for (let i = 0; i < data.length; i++) {\n * console.log(nameColumn.get(i), emailColumn.get(i));\n * }\n *\n * // Note: For filtering data, it's most efficient to use SQL in your query\n * const { data } = useSql<User>({\n * query: \"SELECT * FROM users WHERE age > 30\"\n * });\n * ```\n *\n * ### 3. Using Flechette for Advanced Operations\n *\n * For more advanced Arrow operations, consider using [Flechette](https://idl.uw.edu/flechette/),\n * a faster and lighter alternative to the standard Arrow JS implementation.\n *\n * ```typescript\n * // Example using Flechette with SQL query results\n * import { tableFromIPC } from '@uwdata/flechette';\n *\n * // Convert Arrow table to Flechette table\n * const serializedData = data.arrowTable.serialize();\n * const flechetteTable = tableFromIPC(serializedData);\n *\n * // Extract all columns into a { name: array, ... } object\n * const columns = flechetteTable.toColumns();\n *\n * // Create a new table with a selected subset of columns\n * const subtable = flechetteTable.select(['name', 'email']);\n *\n * // Convert to array of objects with customization options\n * const objects = flechetteTable.toArray({\n * useDate: true, // Convert timestamps to Date objects\n * useMap: true // Create Map objects for key-value pairs\n * });\n *\n * // For large datasets, consider memory management\n * serializedData = null; // Allow garbage collection of the serialized data\n * ```\n *\n * Flechette provides several advantages:\n * - Better performance (1.3-1.6x faster value iteration, 7-11x faster row object extraction)\n * - Smaller footprint (~43k minified vs 163k for Arrow JS)\n * - Support for additional data types (including decimal-to-number conversion)\n * - More flexible data value conversion options\n *\n * @template Row The TypeScript type for each row in the result\n * @param options Configuration object containing the query and execution control\n * @returns Object containing the query result, loading state, and any error\n *\n * @template Schema The Zod schema type that defines the shape and validation of each row\n * @param schema A Zod schema that defines the expected shape and validation rules for each row\n * @param options Configuration object containing the query and execution control\n * @returns Object containing the validated query result, loading state, and any error\n */\nexport function useSql<Row>(options: {query: string; enabled?: boolean}): {\n data: UseSqlQueryResult<Row> | undefined;\n error: Error | null;\n isLoading: boolean;\n};\n\nexport function useSql<Schema extends z.ZodType>(\n schema: Schema,\n options: {\n query: string;\n enabled?: boolean;\n },\n): {\n data: UseSqlQueryResult<z.infer<Schema>> | undefined;\n error: Error | null;\n isLoading: boolean;\n};\n\n/**\n * Implementation of useSql that handles both overloads\n */\nexport function useSql<Row, Schema extends z.ZodType = z.ZodType>(\n schemaOrOptions: Schema | {query: string; enabled?: boolean},\n maybeOptions?: {query: string; enabled?: boolean},\n) {\n // Determine if we're using the schema overload\n const hasSchema = maybeOptions !== undefined;\n const options = hasSchema\n ? maybeOptions\n : (schemaOrOptions as {query: string; enabled?: boolean});\n const schema = hasSchema ? (schemaOrOptions as Schema) : undefined;\n\n const [data, setData] = useState<UseSqlQueryResult<Row> | undefined>(\n undefined,\n );\n const [error, setError] = useState<Error | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n\n const getConnector = useStoreWithDuckDb((state) => state.db.getConnector);\n useEffect(() => {\n let isMounted = true;\n\n const fetchData = async () => {\n if (!options.enabled && options.enabled !== undefined) {\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const connector = await getConnector();\n const result = await connector.query(options.query);\n\n // Create a row accessor that optionally validates with the schema\n const rowAccessor = createTypedRowAccessor<Row>({\n arrowTable: result,\n validate: schema ? (row: unknown) => schema.parse(row) : undefined,\n });\n\n if (isMounted) {\n setData(rowAccessor);\n }\n } catch (err) {\n if (isMounted) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n if (isMounted) {\n setIsLoading(false);\n }\n }\n };\n\n fetchData();\n\n return () => {\n isMounted = false;\n };\n }, [options.query, options.enabled]);\n\n return {\n data,\n error,\n isLoading,\n };\n}\n\n/**\n * @deprecated Use useSql instead\n */\nexport const useDuckDbQuery = useSql;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlrooms/duckdb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -20,11 +20,15 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@duckdb/duckdb-wasm": "1.29.0",
|
|
23
|
+
"@sqlrooms/project": "0.9.1",
|
|
23
24
|
"apache-arrow": "^18.1.0",
|
|
24
|
-
"
|
|
25
|
+
"fast-deep-equal": "^3.1.3",
|
|
26
|
+
"immer": "^10.1.1",
|
|
27
|
+
"zod": "^3.24.1",
|
|
28
|
+
"zustand": "^5.0.3"
|
|
25
29
|
},
|
|
26
30
|
"devDependencies": {
|
|
27
|
-
"@sqlrooms/
|
|
31
|
+
"@sqlrooms/project-config": "0.9.1",
|
|
28
32
|
"@types/jest": "^29.5.12",
|
|
29
33
|
"jest": "^29.7.0",
|
|
30
34
|
"ts-jest": "^29.1.2"
|
|
@@ -38,5 +42,5 @@
|
|
|
38
42
|
"test": "jest",
|
|
39
43
|
"test:watch": "jest --watch"
|
|
40
44
|
},
|
|
41
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "8ffc60d215729cae38631a5037866c193c7520e9"
|
|
42
46
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"arrow-utils.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/arrow-utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import * as arrow from 'apache-arrow';
|
|
2
|
-
import { arrowTableToJson } from '../arrow-utils';
|
|
3
|
-
/// TODO: Revise tests for nested objects once https://github.com/apache/arrow/issues/33394 is fixed
|
|
4
|
-
describe('arrow-utils', () => {
|
|
5
|
-
describe('arrowTableToJson', () => {
|
|
6
|
-
it('should convert Arrow table to JSON array', () => {
|
|
7
|
-
// Create a simple Arrow table
|
|
8
|
-
const data = [
|
|
9
|
-
{ id: 1n, name: 'John', score: 95.5 },
|
|
10
|
-
{ id: 2n, name: 'Jane', score: 88.0 },
|
|
11
|
-
];
|
|
12
|
-
const table = arrow.tableFromJSON(data);
|
|
13
|
-
const result = arrowTableToJson(table);
|
|
14
|
-
const expected = [
|
|
15
|
-
{ id: 1, name: 'John', score: 95.5 },
|
|
16
|
-
{ id: 2, name: 'Jane', score: 88.0 },
|
|
17
|
-
];
|
|
18
|
-
expect(result).toEqual(expected);
|
|
19
|
-
});
|
|
20
|
-
it('should handle large BigInt values', () => {
|
|
21
|
-
const largeNumber = 9007199254740991n; // Number.MAX_SAFE_INTEGER
|
|
22
|
-
const data = [
|
|
23
|
-
{ id: largeNumber, value: 'test' },
|
|
24
|
-
{ id: largeNumber + 1n, value: 'test2' },
|
|
25
|
-
];
|
|
26
|
-
const table = arrow.tableFromJSON(data);
|
|
27
|
-
const result = arrowTableToJson(table);
|
|
28
|
-
const expected = [
|
|
29
|
-
{ id: 9007199254740991, value: 'test' },
|
|
30
|
-
{ id: '9007199254740992', value: 'test2' }, // Beyond MAX_SAFE_INTEGER, should be string
|
|
31
|
-
];
|
|
32
|
-
expect(result).toEqual(expected);
|
|
33
|
-
});
|
|
34
|
-
it('should handle timestamps and dates', () => {
|
|
35
|
-
const timestamp = new Date('2024-02-05T12:30:45.123Z');
|
|
36
|
-
const date = new Date('2024-02-05');
|
|
37
|
-
const data = [
|
|
38
|
-
{
|
|
39
|
-
id: 1,
|
|
40
|
-
timestamp: timestamp,
|
|
41
|
-
date: date,
|
|
42
|
-
},
|
|
43
|
-
];
|
|
44
|
-
const table = arrow.tableFromJSON(data);
|
|
45
|
-
const result = arrowTableToJson(table);
|
|
46
|
-
expect(result).toEqual([
|
|
47
|
-
{
|
|
48
|
-
id: 1,
|
|
49
|
-
timestamp: timestamp.getTime(),
|
|
50
|
-
date: date.getTime(),
|
|
51
|
-
},
|
|
52
|
-
]);
|
|
53
|
-
});
|
|
54
|
-
it('should handle nested objects (structs)', () => {
|
|
55
|
-
/// TODO: Revise tests for nested objects once https://github.com/apache/arrow/issues/33394 is fixed
|
|
56
|
-
// For now, we'll use JSON strings for nested objects as Arrow's JS API
|
|
57
|
-
// doesn't seem to have a straightforward way to create struct types
|
|
58
|
-
const data = [
|
|
59
|
-
{
|
|
60
|
-
id: 1,
|
|
61
|
-
user: JSON.stringify({
|
|
62
|
-
name: 'John',
|
|
63
|
-
address: {
|
|
64
|
-
city: 'New York',
|
|
65
|
-
country: 'USA',
|
|
66
|
-
},
|
|
67
|
-
}),
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
id: 2,
|
|
71
|
-
user: JSON.stringify({
|
|
72
|
-
name: 'Jane',
|
|
73
|
-
address: {
|
|
74
|
-
city: 'London',
|
|
75
|
-
country: 'UK',
|
|
76
|
-
},
|
|
77
|
-
}),
|
|
78
|
-
},
|
|
79
|
-
];
|
|
80
|
-
const table = arrow.tableFromJSON(data);
|
|
81
|
-
const result = arrowTableToJson(table);
|
|
82
|
-
// Parse the JSON strings back to objects for comparison
|
|
83
|
-
const parsedResult = result.map((row) => ({
|
|
84
|
-
...row,
|
|
85
|
-
user: JSON.parse(row.user),
|
|
86
|
-
}));
|
|
87
|
-
expect(parsedResult).toEqual([
|
|
88
|
-
{
|
|
89
|
-
id: 1,
|
|
90
|
-
user: {
|
|
91
|
-
name: 'John',
|
|
92
|
-
address: {
|
|
93
|
-
city: 'New York',
|
|
94
|
-
country: 'USA',
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
id: 2,
|
|
100
|
-
user: {
|
|
101
|
-
name: 'Jane',
|
|
102
|
-
address: {
|
|
103
|
-
city: 'London',
|
|
104
|
-
country: 'UK',
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
]);
|
|
109
|
-
});
|
|
110
|
-
it('should handle arrays', () => {
|
|
111
|
-
// For now, we'll use JSON strings for arrays as Arrow's JS API
|
|
112
|
-
// doesn't seem to have a straightforward way to create list types
|
|
113
|
-
const data = [
|
|
114
|
-
{
|
|
115
|
-
id: 1,
|
|
116
|
-
tags: JSON.stringify(['a', 'b']),
|
|
117
|
-
scores: JSON.stringify([1, 2, 3]),
|
|
118
|
-
},
|
|
119
|
-
];
|
|
120
|
-
const table = arrow.tableFromJSON(data);
|
|
121
|
-
const result = arrowTableToJson(table);
|
|
122
|
-
// Parse the JSON strings back to arrays for comparison
|
|
123
|
-
const parsedResult = result.map((row) => ({
|
|
124
|
-
...row,
|
|
125
|
-
tags: JSON.parse(row.tags),
|
|
126
|
-
scores: JSON.parse(row.scores),
|
|
127
|
-
}));
|
|
128
|
-
expect(parsedResult).toEqual([
|
|
129
|
-
{
|
|
130
|
-
id: 1,
|
|
131
|
-
tags: ['a', 'b'],
|
|
132
|
-
scores: [1, 2, 3],
|
|
133
|
-
},
|
|
134
|
-
]);
|
|
135
|
-
});
|
|
136
|
-
it('should handle combination of structs, BigInts, and timestamps', () => {
|
|
137
|
-
const timestamp = new Date('2024-02-05T12:30:45.123Z');
|
|
138
|
-
// const largeNumber = 9007199254740991n; // MAX_SAFE_INTEGER
|
|
139
|
-
const largeNumber = 1;
|
|
140
|
-
// Create data with JSON strings for complex structures
|
|
141
|
-
const data = [
|
|
142
|
-
{
|
|
143
|
-
id: largeNumber,
|
|
144
|
-
created_at: timestamp,
|
|
145
|
-
metadata: JSON.stringify({
|
|
146
|
-
user: {
|
|
147
|
-
id: largeNumber, // + 1n,
|
|
148
|
-
name: 'John',
|
|
149
|
-
last_login: new Date('2024-01-01T00:00:00Z').getTime(),
|
|
150
|
-
stats: {
|
|
151
|
-
score: 95.5,
|
|
152
|
-
rank: largeNumber, // + 2n,
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
tags: ['active', 'premium'],
|
|
156
|
-
}),
|
|
157
|
-
},
|
|
158
|
-
];
|
|
159
|
-
const table = arrow.tableFromJSON(data);
|
|
160
|
-
const result = arrowTableToJson(table);
|
|
161
|
-
// Parse the JSON strings back to objects for comparison
|
|
162
|
-
const parsedResult = result.map((row) => ({
|
|
163
|
-
...row,
|
|
164
|
-
metadata: JSON.parse(row.metadata),
|
|
165
|
-
}));
|
|
166
|
-
expect(parsedResult).toEqual([
|
|
167
|
-
{
|
|
168
|
-
id: 1,
|
|
169
|
-
created_at: timestamp.getTime(),
|
|
170
|
-
metadata: {
|
|
171
|
-
user: {
|
|
172
|
-
id: 1,
|
|
173
|
-
name: 'John',
|
|
174
|
-
last_login: new Date('2024-01-01T00:00:00Z').getTime(),
|
|
175
|
-
stats: {
|
|
176
|
-
score: 95.5,
|
|
177
|
-
rank: 1,
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
tags: ['active', 'premium'],
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
]);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
//# sourceMappingURL=arrow-utils.test.js.map
|