@malloydata/db-duckdb 0.0.130-dev240311202005 → 0.0.130
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_common.d.ts +2 -0
- package/dist/duckdb_common.js +1 -0
- package/dist/duckdb_connection.d.ts +0 -2
- package/dist/duckdb_connection.js +0 -1
- package/dist/duckdb_wasm_connection.d.ts +23 -2
- package/dist/duckdb_wasm_connection.js +40 -12
- package/dist/duckdb_wasm_connection_browser.d.ts +10 -1
- package/dist/duckdb_wasm_connection_browser.js +78 -1
- package/dist/duckdb_wasm_connection_node.d.ts +1 -1
- package/dist/duckdb_wasm_connection_node.js +3 -3
- package/package.json +5 -4
package/dist/duckdb_common.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export interface DuckDBQueryOptions {
|
|
|
4
4
|
}
|
|
5
5
|
export declare abstract class DuckDBCommon implements TestableConnection, PersistSQLResults, StreamingConnection {
|
|
6
6
|
protected queryOptions?: QueryOptionsReader | undefined;
|
|
7
|
+
protected isMotherDuck: boolean;
|
|
8
|
+
protected motherDuckToken: string | undefined;
|
|
7
9
|
private readonly dialect;
|
|
8
10
|
static DEFAULT_QUERY_OPTIONS: DuckDBQueryOptions;
|
|
9
11
|
private schemaCache;
|
package/dist/duckdb_common.js
CHANGED
|
@@ -17,8 +17,6 @@ export declare class DuckDBConnection extends DuckDBCommon {
|
|
|
17
17
|
private additionalExtensions;
|
|
18
18
|
private databasePath;
|
|
19
19
|
private workingDirectory;
|
|
20
|
-
private isMotherDuck;
|
|
21
|
-
private motherDuckToken;
|
|
22
20
|
private readOnly;
|
|
23
21
|
connecting: Promise<void>;
|
|
24
22
|
protected connection: Connection | null;
|
|
@@ -35,7 +35,6 @@ class DuckDBConnection extends duckdb_common_1.DuckDBCommon {
|
|
|
35
35
|
this.additionalExtensions = [];
|
|
36
36
|
this.databasePath = ':memory:';
|
|
37
37
|
this.workingDirectory = '.';
|
|
38
|
-
this.isMotherDuck = false;
|
|
39
38
|
this.readOnly = false;
|
|
40
39
|
this.connection = null;
|
|
41
40
|
if (typeof arg === 'string') {
|
|
@@ -1,13 +1,34 @@
|
|
|
1
|
-
import * as duckdb from '@
|
|
1
|
+
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
2
2
|
import { FetchSchemaOptions, QueryDataRow, QueryOptionsReader, RunSQLOptions, StructDef, SQLBlock, ConnectionConfig } from '@malloydata/malloy';
|
|
3
|
+
import { StructRow } from 'apache-arrow';
|
|
3
4
|
import { DuckDBCommon } from './duckdb_common';
|
|
5
|
+
/**
|
|
6
|
+
* Arrow's toJSON() doesn't really do what I'd expect, since
|
|
7
|
+
* it still includes Arrow objects like DecimalBigNums and Vectors,
|
|
8
|
+
* so we need this fairly gross function to unwrap those.
|
|
9
|
+
*
|
|
10
|
+
* @param value Element from an Arrow StructRow.
|
|
11
|
+
* @return Vanilla Javascript value
|
|
12
|
+
*/
|
|
13
|
+
export declare const unwrapArrow: (value: unknown) => any;
|
|
14
|
+
/**
|
|
15
|
+
* Process a single Arrow result row into a Malloy QueryDataRow
|
|
16
|
+
* Unfortunately simply calling JSONParse(JSON.stringify(row)) even
|
|
17
|
+
* winds up converting DecimalBigNums to strings instead of numbers.
|
|
18
|
+
* For some reason a custom replacer only sees DecimalBigNums as
|
|
19
|
+
* strings, as well.
|
|
20
|
+
*/
|
|
21
|
+
export declare const unwrapRow: (row: StructRow) => QueryDataRow;
|
|
4
22
|
type RemoteFileCallback = (tableName: string) => Promise<Uint8Array | undefined>;
|
|
5
23
|
export interface DuckDBWasmOptions extends ConnectionConfig {
|
|
24
|
+
additionalExtensions?: string[];
|
|
6
25
|
databasePath?: string;
|
|
26
|
+
motherDuckToken: string | undefined;
|
|
7
27
|
workingDirectory?: string;
|
|
8
28
|
}
|
|
9
29
|
export declare abstract class DuckDBWASMConnection extends DuckDBCommon {
|
|
10
30
|
readonly arg: string | DuckDBWasmOptions;
|
|
31
|
+
private additionalExtensions;
|
|
11
32
|
readonly name: string;
|
|
12
33
|
private databasePath;
|
|
13
34
|
protected workingDirectory: string;
|
|
@@ -20,7 +41,7 @@ export declare abstract class DuckDBWASMConnection extends DuckDBCommon {
|
|
|
20
41
|
private remoteFileStatus;
|
|
21
42
|
constructor(options: DuckDBWasmOptions, queryOptions?: QueryOptionsReader);
|
|
22
43
|
constructor(name: string, databasePath?: string | null, workingDirectory?: string, queryOptions?: QueryOptionsReader);
|
|
23
|
-
|
|
44
|
+
protected init(): Promise<void>;
|
|
24
45
|
abstract getBundles(): duckdb.DuckDBBundles;
|
|
25
46
|
get connection(): duckdb.AsyncDuckDBConnection | null;
|
|
26
47
|
get database(): duckdb.AsyncDuckDB | null;
|
|
@@ -48,13 +48,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
48
48
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
49
49
|
};
|
|
50
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
-
exports.DuckDBWASMConnection = void 0;
|
|
52
|
-
const duckdb = __importStar(require("@
|
|
51
|
+
exports.DuckDBWASMConnection = exports.unwrapRow = exports.unwrapArrow = void 0;
|
|
52
|
+
const duckdb = __importStar(require("@duckdb/duckdb-wasm"));
|
|
53
53
|
const web_worker_1 = __importDefault(require("web-worker"));
|
|
54
54
|
const apache_arrow_1 = require("apache-arrow");
|
|
55
55
|
const duckdb_common_1 = require("./duckdb_common");
|
|
56
56
|
const TABLE_MATCH = /FROM\s*('([^']*)'|"([^"]*)")/gi;
|
|
57
57
|
const TABLE_FUNCTION_MATCH = /FROM\s+[a-z0-9_]+\(('([^']*)'|"([^"]*)")/gi;
|
|
58
|
+
const FILE_EXTS = ['.csv', '.tsv', '.parquet'];
|
|
58
59
|
/**
|
|
59
60
|
* Arrow's toJSON() doesn't really do what I'd expect, since
|
|
60
61
|
* it still includes Arrow objects like DecimalBigNums and Vectors,
|
|
@@ -69,7 +70,7 @@ const unwrapArrow = (value) => {
|
|
|
69
70
|
return value;
|
|
70
71
|
}
|
|
71
72
|
else if (value instanceof apache_arrow_1.Vector) {
|
|
72
|
-
return [...value].map(unwrapArrow);
|
|
73
|
+
return [...value].map(exports.unwrapArrow);
|
|
73
74
|
}
|
|
74
75
|
else if (value instanceof Date) {
|
|
75
76
|
return value;
|
|
@@ -88,14 +89,22 @@ const unwrapArrow = (value) => {
|
|
|
88
89
|
return Number(obj[Symbol.toPrimitive]());
|
|
89
90
|
}
|
|
90
91
|
else if (Array.isArray(value)) {
|
|
91
|
-
return value.map(unwrapArrow);
|
|
92
|
+
return value.map(exports.unwrapArrow);
|
|
93
|
+
}
|
|
94
|
+
else if (obj['microseconds'] && obj['timezone'] === null) {
|
|
95
|
+
// Convert epoch µs to ms
|
|
96
|
+
return Number(obj['microseconds']) / 1000;
|
|
97
|
+
}
|
|
98
|
+
else if (obj['days']) {
|
|
99
|
+
// Convert epoch day to Date
|
|
100
|
+
return new Date(obj['days'] * 8.64e7);
|
|
92
101
|
}
|
|
93
102
|
else {
|
|
94
103
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
104
|
const result = {};
|
|
96
105
|
for (const key in obj) {
|
|
97
106
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
98
|
-
result[key] = unwrapArrow(obj[key]);
|
|
107
|
+
result[key] = (0, exports.unwrapArrow)(obj[key]);
|
|
99
108
|
}
|
|
100
109
|
}
|
|
101
110
|
return result;
|
|
@@ -103,6 +112,7 @@ const unwrapArrow = (value) => {
|
|
|
103
112
|
}
|
|
104
113
|
return value;
|
|
105
114
|
};
|
|
115
|
+
exports.unwrapArrow = unwrapArrow;
|
|
106
116
|
/**
|
|
107
117
|
* Process a single Arrow result row into a Malloy QueryDataRow
|
|
108
118
|
* Unfortunately simply calling JSONParse(JSON.stringify(row)) even
|
|
@@ -111,19 +121,22 @@ const unwrapArrow = (value) => {
|
|
|
111
121
|
* strings, as well.
|
|
112
122
|
*/
|
|
113
123
|
const unwrapRow = (row) => {
|
|
114
|
-
return unwrapArrow(row.toJSON());
|
|
124
|
+
return (0, exports.unwrapArrow)(row.toJSON());
|
|
115
125
|
};
|
|
126
|
+
exports.unwrapRow = unwrapRow;
|
|
116
127
|
/**
|
|
117
128
|
* Process a duckedb Table into an array of Malloy QueryDataRows
|
|
118
129
|
*/
|
|
119
130
|
const unwrapTable = (table) => {
|
|
120
|
-
return table.toArray().map(unwrapRow);
|
|
131
|
+
return table.toArray().map(exports.unwrapRow);
|
|
121
132
|
};
|
|
122
133
|
const isNode = () => typeof navigator === 'undefined';
|
|
123
134
|
class DuckDBWASMConnection extends duckdb_common_1.DuckDBCommon {
|
|
124
135
|
constructor(arg, arg2, workingDirectory, queryOptions) {
|
|
136
|
+
var _a, _b;
|
|
125
137
|
super();
|
|
126
138
|
this.arg = arg;
|
|
139
|
+
this.additionalExtensions = [];
|
|
127
140
|
this.databasePath = null;
|
|
128
141
|
this.workingDirectory = '/';
|
|
129
142
|
this._connection = null;
|
|
@@ -154,7 +167,17 @@ class DuckDBWASMConnection extends duckdb_common_1.DuckDBCommon {
|
|
|
154
167
|
if (typeof arg.workingDirectory === 'string') {
|
|
155
168
|
this.workingDirectory = arg.workingDirectory;
|
|
156
169
|
}
|
|
170
|
+
if (typeof arg.motherDuckToken === 'string') {
|
|
171
|
+
this.motherDuckToken = arg.motherDuckToken;
|
|
172
|
+
}
|
|
173
|
+
if (Array.isArray(arg.additionalExtensions)) {
|
|
174
|
+
this.additionalExtensions = arg.additionalExtensions;
|
|
175
|
+
}
|
|
157
176
|
}
|
|
177
|
+
this.isMotherDuck =
|
|
178
|
+
((_a = this.databasePath) === null || _a === void 0 ? void 0 : _a.startsWith('md:')) ||
|
|
179
|
+
((_b = this.databasePath) === null || _b === void 0 ? void 0 : _b.startsWith('motherduck:')) ||
|
|
180
|
+
false;
|
|
158
181
|
this.connecting = this.init();
|
|
159
182
|
}
|
|
160
183
|
async init() {
|
|
@@ -204,10 +227,10 @@ class DuckDBWASMConnection extends duckdb_common_1.DuckDBCommon {
|
|
|
204
227
|
if (this.workingDirectory) {
|
|
205
228
|
await this.runDuckDBQuery(`SET FILE_SEARCH_PATH='${this.workingDirectory}'`);
|
|
206
229
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
230
|
+
const extensions = ['json', 'icu', ...this.additionalExtensions];
|
|
231
|
+
for (const ext of extensions) {
|
|
232
|
+
await this.loadExtension(ext);
|
|
233
|
+
}
|
|
211
234
|
const setupCmds = ["SET TimeZone='UTC'"];
|
|
212
235
|
for (const cmd of setupCmds) {
|
|
213
236
|
try {
|
|
@@ -279,7 +302,7 @@ class DuckDBWASMConnection extends duckdb_common_1.DuckDBCommon {
|
|
|
279
302
|
(abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted)) {
|
|
280
303
|
break;
|
|
281
304
|
}
|
|
282
|
-
yield unwrapRow(row);
|
|
305
|
+
yield (0, exports.unwrapRow)(row);
|
|
283
306
|
index++;
|
|
284
307
|
}
|
|
285
308
|
}
|
|
@@ -299,6 +322,11 @@ class DuckDBWASMConnection extends duckdb_common_1.DuckDBCommon {
|
|
|
299
322
|
};
|
|
300
323
|
await this.setup();
|
|
301
324
|
for (const tablePath of tables) {
|
|
325
|
+
if (this.isMotherDuck &&
|
|
326
|
+
!tables.includes('/') &&
|
|
327
|
+
!FILE_EXTS.some(ext => tablePath.endsWith(ext))) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
302
330
|
// http and s3 urls are handled by duckdb-wasm
|
|
303
331
|
if (tablePath.match(/^https?:\/\//)) {
|
|
304
332
|
continue;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import * as duckdb from '@
|
|
1
|
+
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
2
2
|
import { DuckDBWASMConnection as DuckDBWASMConnectionBase } from './duckdb_wasm_connection';
|
|
3
|
+
import { MDConnection } from '@motherduck/wasm-client';
|
|
4
|
+
import { QueryDataRow } from '@malloydata/malloy';
|
|
3
5
|
export declare class DuckDBWASMConnection extends DuckDBWASMConnectionBase {
|
|
6
|
+
protected _mdConnection: MDConnection | null;
|
|
4
7
|
getBundles(): duckdb.DuckDBBundles;
|
|
8
|
+
init(): Promise<void>;
|
|
9
|
+
setup(): Promise<void>;
|
|
10
|
+
protected runDuckDBQuery(sql: string, abortSignal?: AbortSignal): Promise<{
|
|
11
|
+
rows: QueryDataRow[];
|
|
12
|
+
totalRows: number;
|
|
13
|
+
}>;
|
|
5
14
|
createHash(sqlCommand: string): Promise<string>;
|
|
6
15
|
}
|
|
@@ -46,12 +46,89 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
46
46
|
};
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
48
|
exports.DuckDBWASMConnection = void 0;
|
|
49
|
-
const duckdb = __importStar(require("@
|
|
49
|
+
const duckdb = __importStar(require("@duckdb/duckdb-wasm"));
|
|
50
50
|
const duckdb_wasm_connection_1 = require("./duckdb_wasm_connection");
|
|
51
|
+
const wasm_client_1 = require("@motherduck/wasm-client");
|
|
51
52
|
class DuckDBWASMConnection extends duckdb_wasm_connection_1.DuckDBWASMConnection {
|
|
53
|
+
constructor() {
|
|
54
|
+
super(...arguments);
|
|
55
|
+
this._mdConnection = null;
|
|
56
|
+
}
|
|
52
57
|
getBundles() {
|
|
53
58
|
return duckdb.getJsDelivrBundles();
|
|
54
59
|
}
|
|
60
|
+
async init() {
|
|
61
|
+
if (this.isMotherDuck) {
|
|
62
|
+
if (!this.motherDuckToken) {
|
|
63
|
+
throw new Error('Please set your MotherDuck token');
|
|
64
|
+
}
|
|
65
|
+
const mdConnection = wasm_client_1.MDConnection.create({
|
|
66
|
+
mdToken: this.motherDuckToken,
|
|
67
|
+
});
|
|
68
|
+
await mdConnection.isInitialized();
|
|
69
|
+
this._mdConnection = mdConnection;
|
|
70
|
+
console.info('MotherDuck initialized');
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
await super.init();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async setup() {
|
|
77
|
+
if (this.isMotherDuck) {
|
|
78
|
+
const doSetup = async () => {
|
|
79
|
+
const setupCmds = ["SET TimeZone='UTC'"];
|
|
80
|
+
for (const cmd of setupCmds) {
|
|
81
|
+
try {
|
|
82
|
+
await this.runDuckDBQuery(cmd);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
// eslint-disable-next-line no-console
|
|
86
|
+
console.error(`duckdb setup ${cmd} => ${error}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
await this.connecting;
|
|
91
|
+
if (!this.isSetup) {
|
|
92
|
+
this.isSetup = doSetup();
|
|
93
|
+
}
|
|
94
|
+
await this.isSetup;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
await super.setup();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async runDuckDBQuery(sql, abortSignal) {
|
|
101
|
+
if (this.isMotherDuck) {
|
|
102
|
+
if (this._mdConnection) {
|
|
103
|
+
const connection = this._mdConnection;
|
|
104
|
+
let queryId = undefined;
|
|
105
|
+
const cancel = () => {
|
|
106
|
+
if (queryId) {
|
|
107
|
+
connection.cancelQuery(queryId, 'Cancelled');
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.addEventListener('abort', cancel);
|
|
111
|
+
queryId = connection.enqueueQuery(sql);
|
|
112
|
+
if (queryId) {
|
|
113
|
+
const result = await connection.evaluateQueuedQuery(queryId);
|
|
114
|
+
if (result === null || result === void 0 ? void 0 : result.data) {
|
|
115
|
+
const rows = (0, duckdb_wasm_connection_1.unwrapArrow)(result.data.toRows());
|
|
116
|
+
const totalRows = result.data.rowCount;
|
|
117
|
+
return {
|
|
118
|
+
rows,
|
|
119
|
+
totalRows,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
throw new Error('No data');
|
|
123
|
+
}
|
|
124
|
+
throw new Error('Failed to enqueue query');
|
|
125
|
+
}
|
|
126
|
+
throw new Error('MotherDuck not initialized');
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
return super.runDuckDBQuery(sql, abortSignal);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
55
132
|
async createHash(sqlCommand) {
|
|
56
133
|
const msgUint8 = new TextEncoder().encode(sqlCommand);
|
|
57
134
|
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DuckDBBundles } from '@
|
|
1
|
+
import { DuckDBBundles } from '@duckdb/duckdb-wasm';
|
|
2
2
|
import { DuckDBWASMConnection as DuckDBWASMConnectionBase } from './duckdb_wasm_connection';
|
|
3
3
|
export declare class DuckDBWASMConnection extends DuckDBWASMConnectionBase {
|
|
4
4
|
getBundles(): DuckDBBundles;
|
|
@@ -30,13 +30,13 @@ const crypto_1 = __importDefault(require("crypto"));
|
|
|
30
30
|
const duckdb_wasm_connection_1 = require("./duckdb_wasm_connection");
|
|
31
31
|
class DuckDBWASMConnection extends duckdb_wasm_connection_1.DuckDBWASMConnection {
|
|
32
32
|
getBundles() {
|
|
33
|
-
const resolvePath = require.resolve('@
|
|
33
|
+
const resolvePath = require.resolve('@duckdb/duckdb-wasm');
|
|
34
34
|
if (!resolvePath) {
|
|
35
|
-
throw new Error('Unable to resolve @
|
|
35
|
+
throw new Error('Unable to resolve @duckdb/duckdb-wasm path');
|
|
36
36
|
}
|
|
37
37
|
const distMatch = resolvePath.match(/^.*\/dist\//);
|
|
38
38
|
if (!distMatch) {
|
|
39
|
-
throw new Error('Unable to resolve @
|
|
39
|
+
throw new Error('Unable to resolve @duckdb/duckdb-wasm dist path');
|
|
40
40
|
}
|
|
41
41
|
const dist = distMatch[0];
|
|
42
42
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/db-duckdb",
|
|
3
|
-
"version": "0.0.130
|
|
3
|
+
"version": "0.0.130",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -40,9 +40,10 @@
|
|
|
40
40
|
"prepublishOnly": "npm run build"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@
|
|
44
|
-
"@malloydata/malloy": "
|
|
45
|
-
"
|
|
43
|
+
"@duckdb/duckdb-wasm": "1.28.1-dev106.0",
|
|
44
|
+
"@malloydata/malloy": "0.0.130",
|
|
45
|
+
"@motherduck/wasm-client": "^0.4.0",
|
|
46
|
+
"apache-arrow": "^14.0.0",
|
|
46
47
|
"duckdb": "0.9.2",
|
|
47
48
|
"web-worker": "^1.2.0"
|
|
48
49
|
}
|