@sqlrooms/duckdb 0.0.1-alpha.0 → 0.0.2
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/duckdb.d.ts +34 -0
- package/dist/duckdb.d.ts.map +1 -1
- package/dist/duckdb.js +41 -116
- package/dist/sql-from.d.ts +18 -0
- package/dist/sql-from.d.ts.map +1 -0
- package/dist/sql-from.js +68 -0
- package/package.json +11 -7
- package/CHANGELOG.md +0 -10
- package/dist/duckdb copy.d.ts +0 -28
- package/dist/duckdb copy.d.ts.map +0 -1
- package/dist/duckdb copy.js +0 -140
- package/dist/useDuckConn.d.ts +0 -42
- package/dist/useDuckConn.d.ts.map +0 -1
- package/dist/useDuckConn.js +0 -244
package/dist/duckdb.d.ts
CHANGED
|
@@ -1,15 +1,49 @@
|
|
|
1
|
+
import * as arrow from 'apache-arrow';
|
|
2
|
+
/**
|
|
3
|
+
* Create a table from a query.
|
|
4
|
+
* @param tableName - The name of the table to create.
|
|
5
|
+
* @param query - The query to create the table from.
|
|
6
|
+
* @returns The table that was created.
|
|
7
|
+
*/
|
|
1
8
|
export declare function createTableFromQuery(tableName: string, query: string): Promise<{
|
|
2
9
|
tableName: string;
|
|
3
10
|
rowCount: number;
|
|
4
11
|
}>;
|
|
12
|
+
/**
|
|
13
|
+
* Create a view from a registered file.
|
|
14
|
+
* @param filePath - The path to the file to create the view from.
|
|
15
|
+
* @param schema - The schema to create the view in.
|
|
16
|
+
* @param tableName - The name of the table to create.
|
|
17
|
+
* @param opts - The options to create the view with.
|
|
18
|
+
* @returns The view that was created.
|
|
19
|
+
*/
|
|
5
20
|
export declare function createViewFromRegisteredFile(filePath: string, schema: string, tableName: string, opts?: {
|
|
6
21
|
mode: 'table' | 'view';
|
|
7
22
|
}): Promise<{
|
|
8
23
|
tableName: string;
|
|
9
24
|
rowCount: number;
|
|
10
25
|
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Create a view from a file.
|
|
28
|
+
* @param filePath - The path to the file to create the view from.
|
|
29
|
+
* @param schema - The schema to create the view in.
|
|
30
|
+
* @param tableName - The name of the table to create.
|
|
31
|
+
* @param file - The file to create the view from.
|
|
32
|
+
*/
|
|
11
33
|
export declare function createViewFromFile(filePath: string, schema: string, tableName: string, file: File | Uint8Array): Promise<{
|
|
12
34
|
tableName: string;
|
|
13
35
|
rowCount: number;
|
|
14
36
|
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Create a table from an Arrow table.
|
|
39
|
+
* @param tableName - The name of the table to create.
|
|
40
|
+
* @param arrowTable - The Arrow table to create the table from.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createTableFromArrowTable(tableName: string, data: arrow.Table): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Create a table from an array of objects.
|
|
45
|
+
* @param tableName - The name of the table to create.
|
|
46
|
+
* @param data - The array of objects to create the table from.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createTableFromObjects(tableName: string, data: Record<string, unknown>[]): Promise<void>;
|
|
15
49
|
//# sourceMappingURL=duckdb.d.ts.map
|
package/dist/duckdb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duckdb.d.ts","sourceRoot":"","sources":["../src/duckdb.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"duckdb.d.ts","sourceRoot":"","sources":["../src/duckdb.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;GAU1E;AAED;;;;;;;GAOG;AACH,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;IAEL,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;CACxB,GACA,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,CAAC,CAwBhD;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,IAAI,GAAG,UAAU,GACtB,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,CAAC,CAgBhD;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,KAAK,CAAC,KAAK,iBAIlB;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,iBAKhC"}
|
package/dist/duckdb.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { DuckDBDataProtocol } from '@duckdb/duckdb-wasm';
|
|
2
2
|
import { escapeVal, getColValAsNumber, getDuckDb } from './useDuckDb';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { sqlFrom } from './sql-from';
|
|
4
|
+
/**
|
|
5
|
+
* Create a table from a query.
|
|
6
|
+
* @param tableName - The name of the table to create.
|
|
7
|
+
* @param query - The query to create the table from.
|
|
8
|
+
* @returns The table that was created.
|
|
9
|
+
*/
|
|
6
10
|
export async function createTableFromQuery(tableName, query) {
|
|
7
11
|
const { conn } = await getDuckDb();
|
|
8
12
|
const rowCount = getColValAsNumber(await conn.query(`CREATE OR REPLACE TABLE main.${tableName} AS (
|
|
@@ -10,20 +14,18 @@ export async function createTableFromQuery(tableName, query) {
|
|
|
10
14
|
)`));
|
|
11
15
|
return { tableName, rowCount };
|
|
12
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a view from a registered file.
|
|
19
|
+
* @param filePath - The path to the file to create the view from.
|
|
20
|
+
* @param schema - The schema to create the view in.
|
|
21
|
+
* @param tableName - The name of the table to create.
|
|
22
|
+
* @param opts - The options to create the view with.
|
|
23
|
+
* @returns The view that was created.
|
|
24
|
+
*/
|
|
13
25
|
export async function createViewFromRegisteredFile(filePath, schema, tableName, opts) {
|
|
14
26
|
const { mode = 'table' } = opts ?? {};
|
|
15
27
|
const { conn } = await getDuckDb();
|
|
16
28
|
const fileNameLower = filePath.toLowerCase();
|
|
17
|
-
// let rowCount: number;
|
|
18
|
-
// if (fileNameLower.endsWith('.json')) {
|
|
19
|
-
// await conn.insertJSONFromPath(filePath, {schema, name: tableName});
|
|
20
|
-
// // TODO: for JSON we can use insertJSONFromPath https://github.com/duckdb/duckdb-wasm/issues/1262
|
|
21
|
-
// // fileNameLower.endsWith('.json') || fileNameLower.endsWith('.ndjson')
|
|
22
|
-
// // ? `read_json_auto(${escapeVal(fileName)})`
|
|
23
|
-
// rowCount = getColValAsNumber(
|
|
24
|
-
// await conn.query(`SELECT COUNT(*) FROM ${schema}.${tableName}`),
|
|
25
|
-
// );
|
|
26
|
-
// } else {
|
|
27
29
|
const quotedFileName = escapeVal(filePath);
|
|
28
30
|
const readFileQuery = fileNameLower.endsWith('.json') ||
|
|
29
31
|
fileNameLower.endsWith('.geojson') ||
|
|
@@ -34,30 +36,21 @@ export async function createViewFromRegisteredFile(filePath, schema, tableName,
|
|
|
34
36
|
: fileNameLower.endsWith('.csv') || fileNameLower.endsWith('.tsv')
|
|
35
37
|
? `read_csv(${quotedFileName}, SAMPLE_SIZE=-1, AUTO_DETECT=TRUE)`
|
|
36
38
|
: quotedFileName;
|
|
37
|
-
// const readFileQuery = fileNameLower.endsWith('.csv')
|
|
38
|
-
// ? `read_csv(${quotedFileName}, SAMPLE_SIZE=-1, AUTO_DETECT=TRUE)`
|
|
39
|
-
// : quotedFileName;
|
|
40
39
|
// TODO: tableName generate
|
|
41
40
|
const rowCount = getColValAsNumber(await conn.query(`CREATE OR REPLACE ${mode} ${schema}.${tableName} AS
|
|
42
41
|
SELECT * FROM ${readFileQuery}`));
|
|
43
42
|
// }
|
|
44
43
|
return { tableName, rowCount };
|
|
45
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a view from a file.
|
|
47
|
+
* @param filePath - The path to the file to create the view from.
|
|
48
|
+
* @param schema - The schema to create the view in.
|
|
49
|
+
* @param tableName - The name of the table to create.
|
|
50
|
+
* @param file - The file to create the view from.
|
|
51
|
+
*/
|
|
46
52
|
export async function createViewFromFile(filePath, schema, tableName, file) {
|
|
47
53
|
const duckConn = await getDuckDb();
|
|
48
|
-
// const fileName = file.name;
|
|
49
|
-
// await duckConn.db.dropFile(fileName);
|
|
50
|
-
// await duckConn.db.registerFileHandle(
|
|
51
|
-
// fileName,
|
|
52
|
-
// file,
|
|
53
|
-
// DuckDBDataProtocol.BROWSER_FILEREADER,
|
|
54
|
-
// true,
|
|
55
|
-
// );
|
|
56
|
-
// const tableName = makeTableName(fileName);
|
|
57
|
-
// await duckConn.conn.query(`
|
|
58
|
-
// CREATE OR REPLACE VIEW ${tableName} AS SELECT * FROM '${fileName}'
|
|
59
|
-
// `);
|
|
60
|
-
//const fileName = file.name;
|
|
61
54
|
await duckConn.db.dropFile(filePath);
|
|
62
55
|
if (file instanceof File) {
|
|
63
56
|
await duckConn.db.registerFileHandle(filePath, file, DuckDBDataProtocol.BROWSER_FILEREADER, true);
|
|
@@ -66,91 +59,23 @@ export async function createViewFromFile(filePath, schema, tableName, file) {
|
|
|
66
59
|
await duckConn.db.registerFileBuffer(filePath, file);
|
|
67
60
|
}
|
|
68
61
|
return createViewFromRegisteredFile(filePath, schema, tableName);
|
|
69
|
-
// const res = await duckConn.conn.query(
|
|
70
|
-
// `SELECT count(*) FROM ${inputTableName}`,
|
|
71
|
-
// );
|
|
72
|
-
// const inputRowCount = getColValAsNumber(res, 0);
|
|
73
|
-
// const tableMeta = await duckConn.conn.query(
|
|
74
|
-
// `DESCRIBE TABLE ${inputTableName}`,
|
|
75
|
-
// );
|
|
76
|
-
// const inputTableFields = Array.from(tableMeta).map((row) => ({
|
|
77
|
-
// name: String(row?.column_name),
|
|
78
|
-
// type: String(row?.column_type),
|
|
79
|
-
// }));
|
|
80
|
-
// const nextResult: DataTable = {
|
|
81
|
-
// inputFileName,
|
|
82
|
-
// tableName: inputTableName,
|
|
83
|
-
// rowCount: inputRowCount,
|
|
84
|
-
// // outputRowCount: undefined,
|
|
85
|
-
// columns: inputTableFields,
|
|
86
|
-
// };
|
|
87
|
-
// // setResult(nextResult);
|
|
88
|
-
// return nextResult;
|
|
89
62
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// await duckConn.conn.query(`DROP TABLE IF EXISTS ${inputTableName}`);
|
|
110
|
-
// const readFileQuery = inputFileName.endsWith('.parquet')
|
|
111
|
-
// ? `parquet_scan(${escapeVal(inputFileName)})`
|
|
112
|
-
// : `read_csv(${escapeVal(
|
|
113
|
-
// inputFileName,
|
|
114
|
-
// )}, SAMPLE_SIZE=-1, AUTO_DETECT=TRUE)`;
|
|
115
|
-
// await duckConn.conn.query(
|
|
116
|
-
// `CREATE TABLE ${inputTableName} AS
|
|
117
|
-
// SELECT * FROM ${readFileQuery}`,
|
|
118
|
-
// );
|
|
119
|
-
// const res = await duckConn.conn.query(
|
|
120
|
-
// `SELECT count(*) FROM ${inputTableName}`,
|
|
121
|
-
// );
|
|
122
|
-
// const inputRowCount = getColValAsNumber(res, 0);
|
|
123
|
-
// const tableMeta = await duckConn.conn.query(
|
|
124
|
-
// `DESCRIBE TABLE ${inputTableName}`,
|
|
125
|
-
// );
|
|
126
|
-
// const inputTableFields = Array.from(tableMeta).map((row) => ({
|
|
127
|
-
// name: String(row?.column_name),
|
|
128
|
-
// type: String(row?.column_type),
|
|
129
|
-
// }));
|
|
130
|
-
// const nextResult: CreateTableDropzoneResult = {
|
|
131
|
-
// inputFileName,
|
|
132
|
-
// inputTableName,
|
|
133
|
-
// inputRowCount,
|
|
134
|
-
// // outputRowCount: undefined,
|
|
135
|
-
// inputTableFields,
|
|
136
|
-
// columns: {},
|
|
137
|
-
// };
|
|
138
|
-
// // setResult(nextResult);
|
|
139
|
-
// onTableCreated(inputTableName, nextResult);
|
|
140
|
-
// } catch (e) {
|
|
141
|
-
// console.error(e);
|
|
142
|
-
// onError(e instanceof Error ? e.message : String(e));
|
|
143
|
-
// }
|
|
144
|
-
// }
|
|
145
|
-
// async function maybeDropTable(
|
|
146
|
-
// value: CreateTableDropzoneResult,
|
|
147
|
-
// duckConn: DuckDb,
|
|
148
|
-
// ) {
|
|
149
|
-
// const {inputFileName, inputTableName} = value || {};
|
|
150
|
-
// if (inputFileName) {
|
|
151
|
-
// await duckConn.db.dropFile(inputFileName);
|
|
152
|
-
// }
|
|
153
|
-
// if (inputTableName) {
|
|
154
|
-
// await duckConn.conn.query(`DROP TABLE IF EXISTS ${inputTableName};`);
|
|
155
|
-
// }
|
|
156
|
-
// }
|
|
63
|
+
/**
|
|
64
|
+
* Create a table from an Arrow table.
|
|
65
|
+
* @param tableName - The name of the table to create.
|
|
66
|
+
* @param arrowTable - The Arrow table to create the table from.
|
|
67
|
+
*/
|
|
68
|
+
export async function createTableFromArrowTable(tableName, data) {
|
|
69
|
+
const { conn } = await getDuckDb();
|
|
70
|
+
await conn.insertArrowTable(data, { name: tableName });
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create a table from an array of objects.
|
|
74
|
+
* @param tableName - The name of the table to create.
|
|
75
|
+
* @param data - The array of objects to create the table from.
|
|
76
|
+
*/
|
|
77
|
+
export async function createTableFromObjects(tableName, data) {
|
|
78
|
+
const { conn } = await getDuckDb();
|
|
79
|
+
const query = sqlFrom(data);
|
|
80
|
+
await conn.query(`CREATE OR REPLACE TABLE ${tableName} AS ${query}`);
|
|
81
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a SQL query that embeds the given data for loading.
|
|
3
|
+
* @param {*} data The dataset as an array of objects.
|
|
4
|
+
* @param {object} [options] Loading options.
|
|
5
|
+
* @param {string[]|Record<string,string>} [options.columns] The columns to include.
|
|
6
|
+
* If not specified, the keys of the first data object are used.
|
|
7
|
+
* @returns {string} SQL query string to load data.
|
|
8
|
+
*/
|
|
9
|
+
export declare function sqlFrom(data: Record<string, unknown>[], { columns, }?: {
|
|
10
|
+
columns?: string[] | Record<string, string>;
|
|
11
|
+
}): string;
|
|
12
|
+
/**
|
|
13
|
+
* Convert a value to a SQL literal.
|
|
14
|
+
* @param {*} value The value to convert.
|
|
15
|
+
* @returns {string} The SQL literal.
|
|
16
|
+
*/
|
|
17
|
+
export declare function literalToSQL(value: unknown): string;
|
|
18
|
+
//# sourceMappingURL=sql-from.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-from.d.ts","sourceRoot":"","sources":["../src/sql-from.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,EACE,OAAsC,GACvC,GAAE;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAM,UAsBtD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,UA2B1C"}
|
package/dist/sql-from.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Adapted from https://github.com/uwdata/mosaic/blob/main/packages/sql/src/load/sql-from.js
|
|
2
|
+
// BSD 3-Clause License Copyright (c) 2023, UW Interactive Data Lab
|
|
3
|
+
/**
|
|
4
|
+
* Create a SQL query that embeds the given data for loading.
|
|
5
|
+
* @param {*} data The dataset as an array of objects.
|
|
6
|
+
* @param {object} [options] Loading options.
|
|
7
|
+
* @param {string[]|Record<string,string>} [options.columns] The columns to include.
|
|
8
|
+
* If not specified, the keys of the first data object are used.
|
|
9
|
+
* @returns {string} SQL query string to load data.
|
|
10
|
+
*/
|
|
11
|
+
export function sqlFrom(data, { columns = Object.keys(data?.[0] || {}), } = {}) {
|
|
12
|
+
let keys = [];
|
|
13
|
+
let columnMap;
|
|
14
|
+
if (Array.isArray(columns)) {
|
|
15
|
+
keys = columns;
|
|
16
|
+
columnMap = keys.reduce((m, k) => ({ ...m, [k]: k }), {});
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
columnMap = columns;
|
|
20
|
+
keys = Object.keys(columns);
|
|
21
|
+
}
|
|
22
|
+
if (!keys.length) {
|
|
23
|
+
throw new Error('Can not create table from empty column set.');
|
|
24
|
+
}
|
|
25
|
+
const subq = [];
|
|
26
|
+
for (const datum of data) {
|
|
27
|
+
const sel = keys.map((k) => `${literalToSQL(datum[k])} AS "${columnMap[k]}"`);
|
|
28
|
+
subq.push(`(SELECT ${sel.join(', ')})`);
|
|
29
|
+
}
|
|
30
|
+
return subq.join(' UNION ALL ');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Convert a value to a SQL literal.
|
|
34
|
+
* @param {*} value The value to convert.
|
|
35
|
+
* @returns {string} The SQL literal.
|
|
36
|
+
*/
|
|
37
|
+
export function literalToSQL(value) {
|
|
38
|
+
switch (typeof value) {
|
|
39
|
+
case 'number':
|
|
40
|
+
return Number.isFinite(value) ? `${value}` : 'NULL';
|
|
41
|
+
case 'string':
|
|
42
|
+
return `'${value.replace(`'`, `''`)}'`;
|
|
43
|
+
case 'boolean':
|
|
44
|
+
return value ? 'TRUE' : 'FALSE';
|
|
45
|
+
default:
|
|
46
|
+
if (value == null) {
|
|
47
|
+
return 'NULL';
|
|
48
|
+
}
|
|
49
|
+
else if (value instanceof Date) {
|
|
50
|
+
const ts = +value;
|
|
51
|
+
if (Number.isNaN(ts))
|
|
52
|
+
return 'NULL';
|
|
53
|
+
const y = value.getUTCFullYear();
|
|
54
|
+
const m = value.getUTCMonth();
|
|
55
|
+
const d = value.getUTCDate();
|
|
56
|
+
return ts === Date.UTC(y, m, d)
|
|
57
|
+
? `DATE '${y}-${m + 1}-${d}'` // utc date
|
|
58
|
+
: `epoch_ms(${ts})`; // timestamp
|
|
59
|
+
}
|
|
60
|
+
else if (value instanceof RegExp) {
|
|
61
|
+
return `'${value.source}'`;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// otherwise rely on string coercion
|
|
65
|
+
return `${value}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
package/package.json
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlrooms/duckdb",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
|
-
"types": "
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"private": false,
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
9
12
|
"publishConfig": {
|
|
10
13
|
"access": "public"
|
|
11
14
|
},
|
|
12
|
-
"
|
|
13
|
-
"@duckdb/duckdb-wasm": "
|
|
14
|
-
"apache-arrow": "
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@duckdb/duckdb-wasm": "^1.29.0",
|
|
17
|
+
"apache-arrow": "^18.1.0"
|
|
15
18
|
},
|
|
16
19
|
"scripts": {
|
|
17
20
|
"dev": "tsc -w",
|
|
18
21
|
"build": "tsc",
|
|
19
22
|
"lint": "eslint .",
|
|
20
|
-
"typecheck": "tsc --noEmit"
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"typedoc": "typedoc"
|
|
21
25
|
},
|
|
22
|
-
"gitHead": "
|
|
26
|
+
"gitHead": "32da0b35fe906bdcef42274624d069cc49425a74"
|
|
23
27
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# Change Log
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
-
|
|
6
|
-
## 0.0.1-alpha.0 (2025-01-30)
|
|
7
|
-
|
|
8
|
-
**Note:** Version bump only for package @sqlrooms/duckdb
|
|
9
|
-
|
|
10
|
-
**Note:** Version bump only for package @sqlrooms/duckdb
|
package/dist/duckdb copy.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
2
|
-
import * as arrow from 'apache-arrow';
|
|
3
|
-
import { ColumnTypeCategory } from '@fsq/ui';
|
|
4
|
-
export type DuckConnection = {
|
|
5
|
-
conn: duckdb.AsyncDuckDBConnection;
|
|
6
|
-
db: duckdb.AsyncDuckDB;
|
|
7
|
-
worker: Worker;
|
|
8
|
-
terminate: () => void;
|
|
9
|
-
};
|
|
10
|
-
export declare function getDuckDb(): Promise<DuckConnection>;
|
|
11
|
-
export declare function useDuckDb(): any;
|
|
12
|
-
export declare function getDuckDbTypeCategory(columnType: string): ColumnTypeCategory | undefined;
|
|
13
|
-
export declare function getDuckTables(schema?: string): Promise<string[]>;
|
|
14
|
-
export declare function getColValAsNumber(res: arrow.Table, column?: string | number, index?: number): number;
|
|
15
|
-
export declare const escapeVal: (val: any) => string;
|
|
16
|
-
export type FileTableInfo = {
|
|
17
|
-
fileName: string;
|
|
18
|
-
tableName: string;
|
|
19
|
-
numRows?: number;
|
|
20
|
-
};
|
|
21
|
-
export declare function checkTableExists(tableName: string, schema?: string): Promise<boolean>;
|
|
22
|
-
export declare function addFileToDb({ file, schema, tableName, existingTables }: {
|
|
23
|
-
file: File;
|
|
24
|
-
schema?: string;
|
|
25
|
-
tableName?: string;
|
|
26
|
-
existingTables?: string[];
|
|
27
|
-
}): Promise<FileTableInfo>;
|
|
28
|
-
//# sourceMappingURL=duckdb%20copy.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"duckdb copy.d.ts","sourceRoot":"","sources":["../src/duckdb copy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAC;AAC9C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAKtC,OAAO,EAAC,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAgB3C,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC,qBAAqB,CAAC;IACnC,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAIF,wBAAsB,SAAS,4BAqC9B;AAED,wBAAgB,SAAS,QAWxB;AA4BD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAQxF;AAED,wBAAsB,aAAa,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAYtE;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,KAAK,CAAC,KAAK,EAChB,MAAM,GAAE,MAAM,GAAG,MAAU,EAC3B,KAAK,SAAI,GACR,MAAM,CASR;AAED,eAAO,MAAM,SAAS,QAAS,GAAG,WAEjC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,SAAS,oBAYxE;AAED,wBAAsB,WAAW,CAAC,EAChC,IAAI,EACJ,MAAe,EACf,SAAS,EACT,cAAmB,EACpB,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,GAAG,OAAO,CAAC,aAAa,CAAC,CAmBzB"}
|
package/dist/duckdb copy.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
2
|
-
import eh_worker from '@duckdb/duckdb-wasm/dist/duckdb-browser-eh.worker.js?url';
|
|
3
|
-
import mvp_worker from '@duckdb/duckdb-wasm/dist/duckdb-browser-mvp.worker.js?url';
|
|
4
|
-
import duckdb_wasm_next from '@duckdb/duckdb-wasm/dist/duckdb-eh.wasm?url';
|
|
5
|
-
import duckdb_wasm from '@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm?url';
|
|
6
|
-
import { useSuspenseQuery } from '@tanstack/react-query';
|
|
7
|
-
import { convertToUniqueColumnOrTableName } from './unique-names';
|
|
8
|
-
const MANUAL_BUNDLES = {
|
|
9
|
-
mvp: {
|
|
10
|
-
mainModule: duckdb_wasm,
|
|
11
|
-
mainWorker: mvp_worker
|
|
12
|
-
},
|
|
13
|
-
eh: {
|
|
14
|
-
mainModule: duckdb_wasm_next,
|
|
15
|
-
// mainWorker: new URL('@duckdb/duckdb-wasm/dist/duckdb-browser-eh.worker.js', import.meta.url).toString(),
|
|
16
|
-
mainWorker: eh_worker
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
let duckConnection = null;
|
|
20
|
-
export async function getDuckDb() {
|
|
21
|
-
if (duckConnection) {
|
|
22
|
-
return duckConnection;
|
|
23
|
-
}
|
|
24
|
-
// Select a bundle based on browser checks
|
|
25
|
-
const bundle = await duckdb.selectBundle(MANUAL_BUNDLES);
|
|
26
|
-
if (!bundle.mainWorker) {
|
|
27
|
-
throw new Error('No worker found');
|
|
28
|
-
}
|
|
29
|
-
console.time('DuckDB initialization');
|
|
30
|
-
const worker = new Worker(bundle.mainWorker);
|
|
31
|
-
const logger = new duckdb.ConsoleLogger();
|
|
32
|
-
const db = new duckdb.AsyncDuckDB(logger, worker);
|
|
33
|
-
await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
34
|
-
// TODO: enable OPFS once duckdb-wasm supports it
|
|
35
|
-
// await db.open({
|
|
36
|
-
// path: 'opfs://sql-workbooks.db',
|
|
37
|
-
// accessMode: duckdb.DuckDBAccessMode.READ_WRITE
|
|
38
|
-
// });
|
|
39
|
-
const conn = await db.connect(); // Connect to db
|
|
40
|
-
console.timeEnd('DuckDB initialization');
|
|
41
|
-
return (duckConnection = {
|
|
42
|
-
conn: conn,
|
|
43
|
-
db: db,
|
|
44
|
-
worker: worker,
|
|
45
|
-
terminate: () => {
|
|
46
|
-
conn.close();
|
|
47
|
-
db.terminate();
|
|
48
|
-
worker.terminate();
|
|
49
|
-
duckConnection = null;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
export function useDuckDb() {
|
|
54
|
-
const res = useSuspenseQuery({
|
|
55
|
-
queryKey: ['duckConnection'],
|
|
56
|
-
queryFn: async () => {
|
|
57
|
-
console.log('Attempting to get DuckDB connection');
|
|
58
|
-
return await getDuckDb();
|
|
59
|
-
},
|
|
60
|
-
retry: false,
|
|
61
|
-
gcTime: Infinity
|
|
62
|
-
});
|
|
63
|
-
return res.data;
|
|
64
|
-
}
|
|
65
|
-
const DUCKDB_TYPE_CATEGORIES = {
|
|
66
|
-
string: [/^varchar/, /^char/, /^text/, /^string/, /^uuid/, /^bit/],
|
|
67
|
-
number: [
|
|
68
|
-
/^tinyint/,
|
|
69
|
-
/^smallint/,
|
|
70
|
-
/^integer/,
|
|
71
|
-
/^bigint/,
|
|
72
|
-
/^hugeint/,
|
|
73
|
-
/^utinyint/,
|
|
74
|
-
/^usmallint/,
|
|
75
|
-
/^uinteger/,
|
|
76
|
-
/^ubigint/,
|
|
77
|
-
/^uhugeint/,
|
|
78
|
-
/^decimal/,
|
|
79
|
-
/^numeric/,
|
|
80
|
-
/^double/,
|
|
81
|
-
/^float/
|
|
82
|
-
],
|
|
83
|
-
boolean: [/^bool(ean)?/],
|
|
84
|
-
binary: [/^blob/, /^bytea/, /^binary/, /^varbinary/],
|
|
85
|
-
datetime: [/^date$/, /^time$/, /^timestamp$/, /^timestamptz$/, /^interval$/],
|
|
86
|
-
json: [/^json$/],
|
|
87
|
-
struct: [/^struct$/, /^list$/, /^map$/, /^array$/, /^union$/],
|
|
88
|
-
geometry: [/^geometry/]
|
|
89
|
-
};
|
|
90
|
-
export function getDuckDbTypeCategory(columnType) {
|
|
91
|
-
const type = columnType.toLowerCase();
|
|
92
|
-
for (const [category, patterns] of Object.entries(DUCKDB_TYPE_CATEGORIES)) {
|
|
93
|
-
if (patterns.some(pattern => type.match(pattern))) {
|
|
94
|
-
return category;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return undefined;
|
|
98
|
-
}
|
|
99
|
-
export async function getDuckTables(schema = 'main') {
|
|
100
|
-
const { conn } = await getDuckDb();
|
|
101
|
-
const tablesResults = await conn.query(`SELECT * FROM information_schema.tables
|
|
102
|
-
WHERE table_schema = '${schema}'
|
|
103
|
-
ORDER BY table_name`);
|
|
104
|
-
const tableNames = [];
|
|
105
|
-
for (let i = 0; i < tablesResults.numRows; i++) {
|
|
106
|
-
tableNames.push(tablesResults.getChild('table_name')?.get(i));
|
|
107
|
-
}
|
|
108
|
-
return tableNames;
|
|
109
|
-
}
|
|
110
|
-
export function getColValAsNumber(res, column = 0, index = 0) {
|
|
111
|
-
const v = (typeof column === 'number' ? res.getChildAt(column) : res.getChild(column))?.get(index);
|
|
112
|
-
if (v === undefined || v === null) {
|
|
113
|
-
return NaN;
|
|
114
|
-
}
|
|
115
|
-
// if it's an array (can be returned by duckdb as bigint)
|
|
116
|
-
return Number(v[0] ?? v);
|
|
117
|
-
}
|
|
118
|
-
export const escapeVal = (val) => {
|
|
119
|
-
return `'${String(val).replace(/'/g, "''")}'`;
|
|
120
|
-
};
|
|
121
|
-
export async function checkTableExists(tableName, schema = 'main') {
|
|
122
|
-
const { conn } = await getDuckDb();
|
|
123
|
-
return Boolean(getColValAsNumber(await conn.query(`SELECT EXISTS(
|
|
124
|
-
SELECT table_name FROM information_schema.tables
|
|
125
|
-
WHERE table_name = '${tableName}' AND table_schema = '${schema}'
|
|
126
|
-
)`)));
|
|
127
|
-
}
|
|
128
|
-
export async function addFileToDb({ file, schema = 'main', tableName, existingTables = [] }) {
|
|
129
|
-
const table = tableName ?? convertToUniqueColumnOrTableName(file.name, existingTables);
|
|
130
|
-
const { db, conn } = await getDuckDb();
|
|
131
|
-
// const {ext} = splitFilePath(file.name);
|
|
132
|
-
// const duckdbFileName = `${createId()}.${ext}`;
|
|
133
|
-
// const duckdbFileName = `${tableName}.dat`;
|
|
134
|
-
await db.registerFileHandle(file.name, file, duckdb.DuckDBDataProtocol.BROWSER_FILEREADER, true);
|
|
135
|
-
const numRows = getColValAsNumber(await conn.query(`CREATE OR REPLACE TABLE ${schema}.${table} AS SELECT * FROM ${escapeVal(file.name)}`));
|
|
136
|
-
if (!numRows) {
|
|
137
|
-
throw new Error(`No data loaded from "${file.name}"`);
|
|
138
|
-
}
|
|
139
|
-
return { tableName: table, fileName: file.name, numRows };
|
|
140
|
-
}
|
package/dist/useDuckConn.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
2
|
-
import * as arrow from 'apache-arrow';
|
|
3
|
-
import { DataTable } from './types';
|
|
4
|
-
/**
|
|
5
|
-
* @deprecated DuckConn is deprecated, use DuckDb instead
|
|
6
|
-
*/
|
|
7
|
-
export type DuckConn = DuckDb;
|
|
8
|
-
export type DuckDb = {
|
|
9
|
-
db: duckdb.AsyncDuckDB;
|
|
10
|
-
conn: duckdb.AsyncDuckDBConnection;
|
|
11
|
-
worker: Worker;
|
|
12
|
-
};
|
|
13
|
-
export declare class DuckQueryError extends Error {
|
|
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: any) => 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>;
|
|
42
|
-
//# sourceMappingURL=useDuckConn.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useDuckConn.d.ts","sourceRoot":"","sources":["../src/useDuckConn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAC;AAC9C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAC,SAAS,EAAC,MAAM,SAAS,CAAC;AAElC;;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,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;gBAChC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS;IAWlE,iBAAiB;CAIlB;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,kBAAY,CAAC;AAErC,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAyFjD;AAKD;;GAEG;AACH,eAAO,MAAM,WAAW,kBAAY,CAAC;AAErC,wBAAgB,SAAS,IAAI,MAAM,CAYlC;AAED,eAAO,MAAM,iBAAiB,SAAU,MAAM,YAKjB,CAAC;AAE9B,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,KAAK,CAAC,KAAK,EAChB,MAAM,GAAE,MAAM,GAAG,MAAU,EAC3B,KAAK,SAAI,GACR,MAAM,CASR;AAED,eAAO,MAAM,SAAS,QAAS,GAAG,WAEjC,CAAC;AAEF,eAAO,MAAM,QAAQ,OAAQ,MAAM,WAMlC,CAAC;AAEF,wBAAsB,aAAa,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAYtE;AAED,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,SAAS,GACd,OAAO,CAAC,SAAS,CAAC,CAmBpB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,SAAS,GACd,OAAO,CAAC,SAAS,EAAE,CAAC,CAOtB;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,MAAM,SAAS,GACd,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED,wBAAsB,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiClE;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhE;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3D;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAGlD"}
|
package/dist/useDuckConn.js
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
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 allBundles: duckdb.DuckDBBundles = {
|
|
52
|
-
// //mvp: {},
|
|
53
|
-
// eh: {
|
|
54
|
-
// mainModule: new URL(
|
|
55
|
-
// '/duckdb/duckdb-eh.c9a601f6b3ff6fab0213.wasm',
|
|
56
|
-
// globalThis.location.href,
|
|
57
|
-
// ).toString(),
|
|
58
|
-
// mainWorker: new URL(
|
|
59
|
-
// '/duckdb/duckdb-browser-eh.worker.e3751c0855c66ca87c52.js',
|
|
60
|
-
// globalThis.location.href,
|
|
61
|
-
// ).toString(),
|
|
62
|
-
// },
|
|
63
|
-
// };
|
|
64
|
-
const bestBundle = await duckdb.selectBundle(allBundles);
|
|
65
|
-
if (!bestBundle.mainWorker) {
|
|
66
|
-
throw new Error('No best bundle found for DuckDB worker');
|
|
67
|
-
}
|
|
68
|
-
const workerUrl = URL.createObjectURL(new Blob([`importScripts("${bestBundle.mainWorker}");`], {
|
|
69
|
-
type: 'text/javascript',
|
|
70
|
-
}));
|
|
71
|
-
// const worker = await duckdb.createWorker(bestBundle.mainWorker);
|
|
72
|
-
const worker = new Worker(workerUrl);
|
|
73
|
-
const logger = ENABLE_DUCK_LOGGING
|
|
74
|
-
? new duckdb.ConsoleLogger()
|
|
75
|
-
: SilentLogger;
|
|
76
|
-
const db = new (class extends duckdb.AsyncDuckDB {
|
|
77
|
-
onError(event) {
|
|
78
|
-
super.onError(event);
|
|
79
|
-
console.error('onError', event);
|
|
80
|
-
}
|
|
81
|
-
})(logger, worker);
|
|
82
|
-
await db.instantiate(bestBundle.mainModule, bestBundle.pthreadWorker);
|
|
83
|
-
URL.revokeObjectURL(workerUrl);
|
|
84
|
-
await db.open({
|
|
85
|
-
path: ':memory:',
|
|
86
|
-
query: {
|
|
87
|
-
// castBigIntToDouble: true
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
const conn = await db.connect();
|
|
91
|
-
// Replace conn.query to include full query in the error message
|
|
92
|
-
const connQuery = conn.query;
|
|
93
|
-
conn.query = (async (q) => {
|
|
94
|
-
const stack = new Error().stack;
|
|
95
|
-
try {
|
|
96
|
-
return await connQuery.call(conn, q);
|
|
97
|
-
}
|
|
98
|
-
catch (err) {
|
|
99
|
-
throw new DuckQueryError(err, q, stack);
|
|
100
|
-
// throw new Error(
|
|
101
|
-
// `Query failed: ${err}\n\nFull query:\n\n${q}\n\nQuery call stack:\n\n${stack}\n\n`,
|
|
102
|
-
// );
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
await conn.query(`
|
|
106
|
-
SET max_expression_depth TO 100000;
|
|
107
|
-
SET memory_limit = '10GB';
|
|
108
|
-
`);
|
|
109
|
-
duckConn = { db, conn, worker };
|
|
110
|
-
resolve(duckConn);
|
|
111
|
-
}
|
|
112
|
-
catch (err) {
|
|
113
|
-
reject(err);
|
|
114
|
-
throw err;
|
|
115
|
-
}
|
|
116
|
-
return duckConn;
|
|
117
|
-
}
|
|
118
|
-
// Cache the promise to avoid multiple initialization attempts
|
|
119
|
-
let duckPromise = null;
|
|
120
|
-
/**
|
|
121
|
-
* @deprecated useDuckConn is deprecated, use useDuckDb instead
|
|
122
|
-
*/
|
|
123
|
-
export const useDuckConn = useDuckDb;
|
|
124
|
-
export function useDuckDb() {
|
|
125
|
-
if (!duckPromise) {
|
|
126
|
-
duckPromise = getDuckDb();
|
|
127
|
-
}
|
|
128
|
-
// If we don't have a connection yet, throw the promise
|
|
129
|
-
// This will trigger Suspense
|
|
130
|
-
if (!duckConn) {
|
|
131
|
-
throw duckPromise;
|
|
132
|
-
}
|
|
133
|
-
return duckConn;
|
|
134
|
-
}
|
|
135
|
-
export const isNumericDuckType = (type) => type.indexOf('INT') >= 0 ||
|
|
136
|
-
type.indexOf('DECIMAL') >= 0 ||
|
|
137
|
-
type.indexOf('FLOAT') >= 0 ||
|
|
138
|
-
type.indexOf('REAL') >= 0 ||
|
|
139
|
-
type.indexOf('DOUBLE') >= 0;
|
|
140
|
-
export function getColValAsNumber(res, column = 0, index = 0) {
|
|
141
|
-
const v = (typeof column === 'number' ? res.getChildAt(column) : res.getChild(column))?.get(index);
|
|
142
|
-
if (v === undefined || v === null) {
|
|
143
|
-
return NaN;
|
|
144
|
-
}
|
|
145
|
-
// if it's an array (can be returned by duckdb as bigint)
|
|
146
|
-
return Number(v[0] ?? v);
|
|
147
|
-
}
|
|
148
|
-
export const escapeVal = (val) => {
|
|
149
|
-
return `'${String(val).replace(/'/g, "''")}'`;
|
|
150
|
-
};
|
|
151
|
-
export const escapeId = (id) => {
|
|
152
|
-
const str = String(id);
|
|
153
|
-
if (str.startsWith('"') && str.endsWith('"')) {
|
|
154
|
-
return str;
|
|
155
|
-
}
|
|
156
|
-
return `"${str.replace(/"/g, '""')}"`;
|
|
157
|
-
};
|
|
158
|
-
export async function getDuckTables(schema = 'main') {
|
|
159
|
-
const { conn } = await getDuckDb();
|
|
160
|
-
const tablesResults = await conn.query(`SELECT * FROM information_schema.tables
|
|
161
|
-
WHERE table_schema = '${schema}'
|
|
162
|
-
ORDER BY table_name`);
|
|
163
|
-
const tableNames = [];
|
|
164
|
-
for (let i = 0; i < tablesResults.numRows; i++) {
|
|
165
|
-
tableNames.push(tablesResults.getChild('table_name')?.get(i));
|
|
166
|
-
}
|
|
167
|
-
return tableNames;
|
|
168
|
-
}
|
|
169
|
-
export async function getDuckTableSchema(tableName, schema = 'main') {
|
|
170
|
-
const { conn } = await getDuckDb();
|
|
171
|
-
const describeResults = await conn.query(`DESCRIBE ${schema}.${tableName}`);
|
|
172
|
-
const columnNames = describeResults.getChild('column_name');
|
|
173
|
-
const columnTypes = describeResults.getChild('column_type');
|
|
174
|
-
const columns = [];
|
|
175
|
-
for (let di = 0; di < describeResults.numRows; di++) {
|
|
176
|
-
const columnName = columnNames?.get(di);
|
|
177
|
-
const columnType = columnTypes?.get(di);
|
|
178
|
-
columns.push({ name: columnName, type: columnType });
|
|
179
|
-
}
|
|
180
|
-
return {
|
|
181
|
-
tableName,
|
|
182
|
-
columns,
|
|
183
|
-
// Costly to get the row count for large tables
|
|
184
|
-
// rowCount: getColValAsNumber(
|
|
185
|
-
// await conn.query(`SELECT COUNT(*) FROM ${schema}.${tableName}`),
|
|
186
|
-
// ),
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
export async function getDuckTableSchemas(schema = 'main') {
|
|
190
|
-
const tableNames = await getDuckTables(schema);
|
|
191
|
-
const tablesInfo = [];
|
|
192
|
-
for (const tableName of tableNames) {
|
|
193
|
-
tablesInfo.push(await getDuckTableSchema(tableName, schema));
|
|
194
|
-
}
|
|
195
|
-
return tablesInfo;
|
|
196
|
-
}
|
|
197
|
-
export async function checkTableExists(tableName, schema = 'main') {
|
|
198
|
-
const { conn } = await getDuckDb();
|
|
199
|
-
const res = await conn.query(`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${schema}' AND table_name = '${tableName}'`);
|
|
200
|
-
return getColValAsNumber(res) > 0;
|
|
201
|
-
}
|
|
202
|
-
export async function dropAllTables(schema) {
|
|
203
|
-
try {
|
|
204
|
-
const { conn } = await getDuckDb();
|
|
205
|
-
if (schema && schema !== 'main') {
|
|
206
|
-
await conn.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
const res = await conn.query(`SELECT table_name, table_schema, table_type FROM information_schema.tables${schema ? ` WHERE table_schema = '${schema}'` : ''}`);
|
|
210
|
-
const schemasCol = res.getChild('table_schema');
|
|
211
|
-
const tableNamesCol = res.getChild('table_name');
|
|
212
|
-
const tableTypesCol = res.getChild('table_type');
|
|
213
|
-
for (let i = 0; i < res.numRows; i++) {
|
|
214
|
-
try {
|
|
215
|
-
const schemaName = schemasCol?.get(i);
|
|
216
|
-
const tableName = tableNamesCol?.get(i);
|
|
217
|
-
const tableType = tableTypesCol?.get(i);
|
|
218
|
-
if (tableName) {
|
|
219
|
-
const query = `DROP ${tableType === 'VIEW' ? 'VIEW' : 'TABLE'} IF EXISTS ${schemaName}.${tableName}`;
|
|
220
|
-
await conn.query(query);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
catch (err) {
|
|
224
|
-
console.error(err);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
catch (err) {
|
|
230
|
-
console.error(err);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
export async function dropTable(tableName) {
|
|
234
|
-
const { conn } = await getDuckDb();
|
|
235
|
-
await conn.query(`DROP TABLE IF EXISTS ${tableName};`);
|
|
236
|
-
}
|
|
237
|
-
export async function dropFile(fname) {
|
|
238
|
-
const { db } = await getDuckDb();
|
|
239
|
-
db.dropFile(fname);
|
|
240
|
-
}
|
|
241
|
-
export async function dropAllFiles() {
|
|
242
|
-
const { db } = await getDuckDb();
|
|
243
|
-
db.dropFiles();
|
|
244
|
-
}
|