-
Notifications
You must be signed in to change notification settings - Fork 10
/
useDuckConn.ts
115 lines (103 loc) · 3 KB
/
useDuckConn.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import * as duckdb from "@duckdb/duckdb-wasm";
import useSWR from "swr";
import { Table } from "apache-arrow";
export type DuckConn = {
db: duckdb.AsyncDuckDB;
conn: duckdb.AsyncDuckDBConnection;
worker: Worker;
};
const ENABLE_DUCK_LOGGING = false;
const SilentLogger = {
log: () => {
/* do nothing */
},
};
// TODO: shut DB down at some point
let duckConn: DuckConn;
let initialize: Promise<DuckConn>;
export async function getDuckConn(): Promise<DuckConn> {
if (duckConn) {
return duckConn;
} else if (initialize) {
// The initialization has already been started, wait for it to finish
return initialize;
}
let resolve: (value: DuckConn) => void;
let reject: (reason?: any) => void;
initialize = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
try {
console.time("DB instantiation");
const allBundles = duckdb.getJsDelivrBundles();
const bestBundle = await duckdb.selectBundle(allBundles);
if (bestBundle.mainWorker) {
const worker = await duckdb.createWorker(bestBundle.mainWorker);
const logger = ENABLE_DUCK_LOGGING
? new duckdb.ConsoleLogger()
: SilentLogger;
const db = new duckdb.AsyncDuckDB(logger, worker);
await db.instantiate(bestBundle.mainModule, bestBundle.pthreadWorker);
await db.open({
path: ":memory:",
query: {
castBigIntToDouble: true,
},
});
const conn = await db.connect();
// Replace conn.query to include full query in the error message
const connQuery = conn.query;
const runQuery = async (q: string): Promise<Table<any>> => {
const stack = new Error().stack;
// try {
return await connQuery.call(conn, q);
// } catch (err) {
// const er = new Error(
// `DB query failed: ${err}\n\nFull query:\n\n${q}\n\nQuery call stack:\n\n${stack}\n\n`
// );
// throw er;
// }
};
conn.query = runQuery;
duckConn = { db, conn, worker };
resolve!(duckConn);
} else {
throw new Error("No best bundle found for DuckDB worker");
}
console.timeEnd("DB instantiation");
} catch (err) {
reject!(err);
throw err;
}
return duckConn;
}
export function useDuckConn() {
const res = useSWR<DuckConn>(
"duckConn",
async () => {
if (!duckConn) {
await getDuckConn();
}
return duckConn;
},
{ suspense: true }
);
return res.data!;
}
export const isNumericDuckType = (type: string) =>
type.indexOf("INT") >= 0 ||
type.indexOf("DECIMAL") >= 0 ||
type.indexOf("FLOAT") >= 0 ||
type.indexOf("REAL") >= 0 ||
type.indexOf("DOUBLE") >= 0;
export function getColValAsNumber(res: Table, column: string | number): number {
const v = (
typeof column === "number" ? res.getChildAt(column) : res.getChild(column)
)?.get(0);
if (v === undefined || v === null) {
return NaN;
}
// if it's an array (can be returned by duckdb as bigint)
return v[0] ?? v;
}