@perspective-dev/client 4.1.1 → 4.2.0
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/cdn/perspective.js +2 -2
- package/dist/cdn/perspective.js.map +4 -4
- package/dist/esm/perspective.browser.d.ts +4 -0
- package/dist/esm/perspective.inline.js +2 -2
- package/dist/esm/perspective.inline.js.map +4 -4
- package/dist/esm/perspective.js +2 -2
- package/dist/esm/perspective.js.map +4 -4
- package/dist/esm/perspective.node.d.ts +9 -0
- package/dist/esm/perspective.node.js +1629 -1291
- package/dist/esm/perspective.node.js.map +3 -3
- package/dist/esm/virtual_server.d.ts +1 -8
- package/dist/esm/virtual_servers/clickhouse.js +2 -0
- package/dist/esm/virtual_servers/clickhouse.js.map +7 -0
- package/dist/esm/virtual_servers/duckdb.d.ts +21 -7
- package/dist/esm/virtual_servers/duckdb.js +1 -11
- package/dist/esm/virtual_servers/duckdb.js.map +3 -3
- package/dist/wasm/perspective-js.d.ts +723 -647
- package/dist/wasm/perspective-js.js +2115 -1841
- package/dist/wasm/perspective-js.wasm +0 -0
- package/dist/wasm/perspective-js.wasm.d.ts +61 -49
- package/package.json +2 -1
- package/src/rust/generic_sql_model.rs +189 -0
- package/src/rust/lib.rs +2 -2
- package/src/rust/utils/console_logger.rs +4 -4
- package/src/rust/virtual_server.rs +50 -46
- package/src/ts/perspective.browser.ts +15 -2
- package/src/ts/perspective.node.ts +21 -1
- package/src/ts/virtual_server.ts +4 -14
- package/src/ts/virtual_servers/clickhouse.ts +362 -0
- package/src/ts/virtual_servers/duckdb.ts +114 -292
|
@@ -13,13 +13,23 @@
|
|
|
13
13
|
export type * from "../../dist/wasm/perspective-js.d.ts";
|
|
14
14
|
import type * as psp from "../../dist/wasm/perspective-js.d.ts";
|
|
15
15
|
export type * from "./virtual_server.ts";
|
|
16
|
-
|
|
17
16
|
import * as psp_virtual from "./virtual_server.ts";
|
|
18
|
-
|
|
19
17
|
import * as wasm_module from "../../dist/wasm/perspective-js.js";
|
|
20
18
|
import * as api from "./wasm/browser.ts";
|
|
21
19
|
import { load_wasm_stage_0 } from "./wasm/decompress.ts";
|
|
22
20
|
|
|
21
|
+
export {
|
|
22
|
+
GenericSQLVirtualServerModel,
|
|
23
|
+
VirtualDataSlice,
|
|
24
|
+
VirtualServer,
|
|
25
|
+
} from "../../dist/wasm/perspective-js.js";
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
GenericSQLVirtualServerModel,
|
|
29
|
+
VirtualDataSlice,
|
|
30
|
+
VirtualServer,
|
|
31
|
+
} from "../../dist/wasm/perspective-js.js";
|
|
32
|
+
|
|
23
33
|
let GLOBAL_SERVER_WASM: Promise<ArrayBuffer | WebAssembly.Module>;
|
|
24
34
|
|
|
25
35
|
export async function createMessageHandler(
|
|
@@ -144,4 +154,7 @@ export default {
|
|
|
144
154
|
init_client,
|
|
145
155
|
init_server,
|
|
146
156
|
createMessageHandler,
|
|
157
|
+
GenericSQLVirtualServerModel,
|
|
158
|
+
VirtualDataSlice,
|
|
159
|
+
VirtualServer,
|
|
147
160
|
};
|
|
@@ -29,9 +29,20 @@ import * as engine from "./wasm/engine.ts";
|
|
|
29
29
|
import { compile_perspective } from "./wasm/emscripten_api.ts";
|
|
30
30
|
import * as psp_websocket from "./websocket.ts";
|
|
31
31
|
import * as api from "./wasm/browser.ts";
|
|
32
|
-
|
|
33
32
|
import * as virtual_server from "./virtual_server.ts";
|
|
34
33
|
|
|
34
|
+
export {
|
|
35
|
+
GenericSQLVirtualServerModel,
|
|
36
|
+
VirtualDataSlice,
|
|
37
|
+
VirtualServer,
|
|
38
|
+
} from "../../dist/wasm/perspective-js.js";
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
GenericSQLVirtualServerModel,
|
|
42
|
+
VirtualDataSlice,
|
|
43
|
+
VirtualServer,
|
|
44
|
+
} from "../../dist/wasm/perspective-js.js";
|
|
45
|
+
|
|
35
46
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
36
47
|
|
|
37
48
|
const { resolve } = createRequire(import.meta.url);
|
|
@@ -337,6 +348,12 @@ export function createMessageHandler(
|
|
|
337
348
|
return virtual_server.createMessageHandler(perspective_client, handler);
|
|
338
349
|
}
|
|
339
350
|
|
|
351
|
+
/**
|
|
352
|
+
* The initialized WASM module. Use this when you need to pass the module
|
|
353
|
+
* to components that require it, such as `DuckDBHandler`.
|
|
354
|
+
*/
|
|
355
|
+
export { perspective_client as wasmModule };
|
|
356
|
+
|
|
340
357
|
export default {
|
|
341
358
|
table,
|
|
342
359
|
websocket,
|
|
@@ -347,4 +364,7 @@ export default {
|
|
|
347
364
|
on_error,
|
|
348
365
|
system_info,
|
|
349
366
|
WebSocketServer,
|
|
367
|
+
GenericSQLVirtualServerModel,
|
|
368
|
+
VirtualDataSlice,
|
|
369
|
+
VirtualServer,
|
|
350
370
|
};
|
package/src/ts/virtual_server.ts
CHANGED
|
@@ -55,8 +55,9 @@ export interface VirtualServerHandler {
|
|
|
55
55
|
viewGetData(
|
|
56
56
|
viewId: string,
|
|
57
57
|
config: ViewConfig,
|
|
58
|
+
schema: Record<string, ColumnType>,
|
|
58
59
|
viewport: ViewWindow,
|
|
59
|
-
dataSlice: perspective.
|
|
60
|
+
dataSlice: perspective.VirtualDataSlice,
|
|
60
61
|
): void | Promise<void>;
|
|
61
62
|
viewSchema?(
|
|
62
63
|
viewId: string,
|
|
@@ -78,11 +79,11 @@ export function createMessageHandler(
|
|
|
78
79
|
mod: typeof perspective,
|
|
79
80
|
handler: VirtualServerHandler,
|
|
80
81
|
) {
|
|
81
|
-
let virtualServer: perspective.
|
|
82
|
+
let virtualServer: perspective.VirtualServer;
|
|
82
83
|
async function postMessage(port: MessagePort, msg: MessageEvent) {
|
|
83
84
|
if (msg.data.cmd === "init") {
|
|
84
85
|
try {
|
|
85
|
-
virtualServer = new mod.
|
|
86
|
+
virtualServer = new mod.VirtualServer(handler);
|
|
86
87
|
if (msg.data.id !== undefined) {
|
|
87
88
|
port.postMessage({ id: msg.data.id });
|
|
88
89
|
} else {
|
|
@@ -113,14 +114,3 @@ export function createMessageHandler(
|
|
|
113
114
|
|
|
114
115
|
return channel.port2;
|
|
115
116
|
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Re-export the WASM VirtualServer and VirtualDataSlice classes with better names.
|
|
119
|
-
*
|
|
120
|
-
* VirtualServer: Handles Perspective protocol messages using your custom handler
|
|
121
|
-
* VirtualDataSlice: Used to fill data in viewGetData callbacks
|
|
122
|
-
*/
|
|
123
|
-
export {
|
|
124
|
-
JsVirtualServer as VirtualServer,
|
|
125
|
-
JsVirtualDataSlice as VirtualDataSlice,
|
|
126
|
-
} from "../../dist/wasm/perspective-js.js";
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* An implementation of a Perspective Virtual Server for DuckDB.
|
|
15
|
+
*
|
|
16
|
+
* This import is optional, and so must be imported manually from either
|
|
17
|
+
* `@perspective-dev/client/dist/esm/virtual_servers/duckdb.js` or
|
|
18
|
+
* `@perspective-dev/client/src/ts/virtual_servers/duckdb.ts`, it is not
|
|
19
|
+
* exported from the package root `@perspective-dev/client`
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type * as perspective from "@perspective-dev/client";
|
|
25
|
+
import type { ColumnType } from "@perspective-dev/client/dist/esm/ts-rs/ColumnType.d.ts";
|
|
26
|
+
import type { ViewConfig } from "@perspective-dev/client/dist/esm/ts-rs/ViewConfig.d.ts";
|
|
27
|
+
import type { ViewWindow } from "@perspective-dev/client/dist/esm/ts-rs/ViewWindow.d.ts";
|
|
28
|
+
import type * as clickhouse from "@clickhouse/client-web";
|
|
29
|
+
|
|
30
|
+
const NUMBER_AGGS = [
|
|
31
|
+
"sum",
|
|
32
|
+
"count",
|
|
33
|
+
"any_value",
|
|
34
|
+
"arbitrary",
|
|
35
|
+
"array_agg",
|
|
36
|
+
"avg",
|
|
37
|
+
"bit_and",
|
|
38
|
+
"bit_or",
|
|
39
|
+
"bit_xor",
|
|
40
|
+
"bitstring_agg",
|
|
41
|
+
"bool_and",
|
|
42
|
+
"bool_or",
|
|
43
|
+
"countif",
|
|
44
|
+
"favg",
|
|
45
|
+
"fsum",
|
|
46
|
+
"geomean",
|
|
47
|
+
"kahan_sum",
|
|
48
|
+
"last",
|
|
49
|
+
"max",
|
|
50
|
+
"min",
|
|
51
|
+
"product",
|
|
52
|
+
"string_agg",
|
|
53
|
+
"sumkahan",
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const STRING_AGGS = [
|
|
57
|
+
"count",
|
|
58
|
+
"any_value",
|
|
59
|
+
"arbitrary",
|
|
60
|
+
"first",
|
|
61
|
+
"countif",
|
|
62
|
+
"last",
|
|
63
|
+
"string_agg",
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const FILTER_OPS = [
|
|
67
|
+
"==",
|
|
68
|
+
"!=",
|
|
69
|
+
"LIKE",
|
|
70
|
+
"IS DISTINCT FROM",
|
|
71
|
+
"IS NOT DISTINCT FROM",
|
|
72
|
+
">=",
|
|
73
|
+
"<=",
|
|
74
|
+
">",
|
|
75
|
+
"<",
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
function duckdbTypeToPsp(name: string): ColumnType {
|
|
79
|
+
if (name.startsWith("Nullable")) {
|
|
80
|
+
name = name.match(/Nullable\((.+?)\)/)![1];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (name.startsWith("Array")) {
|
|
84
|
+
return "string";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (name === "Int64" || name === "UInt64" || name === "Float64") {
|
|
88
|
+
return "float";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (name === "String") {
|
|
92
|
+
return "string";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (name === "DateTime") {
|
|
96
|
+
return "datetime";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (name === "Date") {
|
|
100
|
+
return "date";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
throw new Error(`Unknown type '${name}'`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function convertDecimalToNumber(value: any, dtypeString: string) {
|
|
107
|
+
if (!(value instanceof Uint32Array || value instanceof Int32Array)) {
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let bigIntValue = BigInt(0);
|
|
112
|
+
for (let i = 0; i < value.length; i++) {
|
|
113
|
+
bigIntValue |= BigInt(value[i]) << BigInt(i * 32);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const scaleMatch = dtypeString.match(/Decimal\[\d+e(\d+)\]/);
|
|
117
|
+
if (scaleMatch) {
|
|
118
|
+
const scale = parseInt(scaleMatch[1]);
|
|
119
|
+
return Number(bigIntValue) / Math.pow(10, scale);
|
|
120
|
+
} else {
|
|
121
|
+
return Number(bigIntValue);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
class Lock {
|
|
126
|
+
lockPromise: Promise<void>;
|
|
127
|
+
constructor() {
|
|
128
|
+
this.lockPromise = Promise.resolve();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
acquire() {
|
|
132
|
+
let releaseLock: (value: void) => void;
|
|
133
|
+
const newLockPromise: Promise<void> = new Promise((resolve) => {
|
|
134
|
+
releaseLock = resolve;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const acquirePromise = this.lockPromise.then(() => releaseLock);
|
|
138
|
+
this.lockPromise = newLockPromise;
|
|
139
|
+
return acquirePromise;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const LOCK = new Lock();
|
|
144
|
+
|
|
145
|
+
async function runQuery(
|
|
146
|
+
db: clickhouse.ClickHouseClient,
|
|
147
|
+
query: string,
|
|
148
|
+
options: { columns?: true; execute?: boolean },
|
|
149
|
+
): Promise<{
|
|
150
|
+
rows: any[];
|
|
151
|
+
columns: string[];
|
|
152
|
+
dtypes: string[];
|
|
153
|
+
}>;
|
|
154
|
+
|
|
155
|
+
async function runQuery(
|
|
156
|
+
db: clickhouse.ClickHouseClient,
|
|
157
|
+
query: string,
|
|
158
|
+
options?: { columns?: false; execute?: boolean },
|
|
159
|
+
): Promise<any[]>;
|
|
160
|
+
|
|
161
|
+
async function runQuery(
|
|
162
|
+
db: clickhouse.ClickHouseClient,
|
|
163
|
+
query: string,
|
|
164
|
+
options: { columns?: boolean; execute?: boolean } = {},
|
|
165
|
+
) {
|
|
166
|
+
query = query.replace(/\s+/g, " ").trim();
|
|
167
|
+
const release = await LOCK.acquire();
|
|
168
|
+
try {
|
|
169
|
+
const result = await db.query({ query });
|
|
170
|
+
if (!options.execute) {
|
|
171
|
+
const { data, meta } =
|
|
172
|
+
(await result.json()) as clickhouse.ResponseJSON<unknown>;
|
|
173
|
+
|
|
174
|
+
if (options.columns) {
|
|
175
|
+
return {
|
|
176
|
+
rows: data,
|
|
177
|
+
columns: meta!.map((f) => f.name),
|
|
178
|
+
dtypes: meta!.map((f) => f.type),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return data;
|
|
183
|
+
}
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error("Query error:", error);
|
|
186
|
+
console.error("Query:", query);
|
|
187
|
+
throw error;
|
|
188
|
+
} finally {
|
|
189
|
+
release();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* An implementation of Perspective's Virtual Server for `@duckdb/duckdb-wasm`.
|
|
195
|
+
*/
|
|
196
|
+
export class ClickhouseHandler implements perspective.VirtualServerHandler {
|
|
197
|
+
private db: clickhouse.ClickHouseClient;
|
|
198
|
+
private sqlBuilder: perspective.GenericSQLVirtualServerModel;
|
|
199
|
+
constructor(db: clickhouse.ClickHouseClient, mod?: typeof perspective) {
|
|
200
|
+
if (!mod) {
|
|
201
|
+
if (customElements) {
|
|
202
|
+
const viewer_class: any =
|
|
203
|
+
customElements.get("perspective-viewer");
|
|
204
|
+
if (viewer_class) {
|
|
205
|
+
mod = viewer_class.__wasm_module__;
|
|
206
|
+
} else {
|
|
207
|
+
throw new Error("Missing perspective-client.wasm");
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.db = db;
|
|
214
|
+
this.sqlBuilder = new mod!.GenericSQLVirtualServerModel({
|
|
215
|
+
create_entity: "VIEW",
|
|
216
|
+
grouping_fn: "GROUPING",
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
getFeatures() {
|
|
221
|
+
return {
|
|
222
|
+
group_by: true,
|
|
223
|
+
split_by: false,
|
|
224
|
+
sort: true,
|
|
225
|
+
expressions: true,
|
|
226
|
+
filter_ops: {
|
|
227
|
+
integer: FILTER_OPS,
|
|
228
|
+
float: FILTER_OPS,
|
|
229
|
+
string: FILTER_OPS,
|
|
230
|
+
boolean: FILTER_OPS,
|
|
231
|
+
date: FILTER_OPS,
|
|
232
|
+
datetime: FILTER_OPS,
|
|
233
|
+
},
|
|
234
|
+
aggregates: {
|
|
235
|
+
integer: NUMBER_AGGS,
|
|
236
|
+
float: NUMBER_AGGS,
|
|
237
|
+
string: STRING_AGGS,
|
|
238
|
+
boolean: STRING_AGGS,
|
|
239
|
+
date: STRING_AGGS,
|
|
240
|
+
datetime: STRING_AGGS,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async getHostedTables() {
|
|
246
|
+
const query = "SHOW TABLES";
|
|
247
|
+
const results = await runQuery(this.db, query);
|
|
248
|
+
return results.map((row) => {
|
|
249
|
+
return `${row.name}`;
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async tableSchema(tableId: string, config?: ViewConfig) {
|
|
254
|
+
const query = this.sqlBuilder.tableSchema(tableId);
|
|
255
|
+
const results = await runQuery(this.db, query);
|
|
256
|
+
const schema = {} as Record<string, ColumnType>;
|
|
257
|
+
for (const result of results) {
|
|
258
|
+
const colName = result.name;
|
|
259
|
+
if (!colName.startsWith("__")) {
|
|
260
|
+
schema[colName] = duckdbTypeToPsp(result.type) as ColumnType;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return schema;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async viewColumnSize(viewId: string, config: ViewConfig) {
|
|
268
|
+
const query = `SELECT COUNT() FROM system.columns WHERE table = '${viewId}'`;
|
|
269
|
+
const results = await runQuery(this.db, query);
|
|
270
|
+
const gs = config.group_by?.length || 0;
|
|
271
|
+
const count = Number(results[0]["COUNT()"]);
|
|
272
|
+
console.log(count);
|
|
273
|
+
return (
|
|
274
|
+
count -
|
|
275
|
+
(gs === 0 ? 0 : gs + (config.split_by?.length === 0 ? 1 : 0))
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async tableSize(tableId: string) {
|
|
280
|
+
const query = this.sqlBuilder.tableSize(tableId);
|
|
281
|
+
const results = await runQuery(this.db, query);
|
|
282
|
+
return Number(results[0]["COUNT()"]);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async tableMakeView(tableId: string, viewId: string, config: ViewConfig) {
|
|
286
|
+
const query = this.sqlBuilder.tableMakeView(tableId, viewId, config);
|
|
287
|
+
await runQuery(this.db, query, { execute: true });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async tableValidateExpression(tableId: string, expression: string) {
|
|
291
|
+
const query = this.sqlBuilder.tableValidateExpression(
|
|
292
|
+
tableId,
|
|
293
|
+
expression,
|
|
294
|
+
);
|
|
295
|
+
const results = await runQuery(this.db, query);
|
|
296
|
+
return duckdbTypeToPsp(results[0]["type"]) as ColumnType;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async viewDelete(viewId: string) {
|
|
300
|
+
const query = this.sqlBuilder.viewDelete(viewId);
|
|
301
|
+
await runQuery(this.db, query, { execute: true });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async viewGetData(
|
|
305
|
+
viewId: string,
|
|
306
|
+
config: ViewConfig,
|
|
307
|
+
schema: Record<string, ColumnType>,
|
|
308
|
+
viewport: ViewWindow,
|
|
309
|
+
dataSlice: perspective.VirtualDataSlice,
|
|
310
|
+
) {
|
|
311
|
+
const is_group_by = config.group_by?.length > 0;
|
|
312
|
+
const is_split_by = config.split_by?.length > 0;
|
|
313
|
+
const query = this.sqlBuilder.viewGetData(
|
|
314
|
+
viewId,
|
|
315
|
+
config,
|
|
316
|
+
viewport,
|
|
317
|
+
schema,
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const { rows, columns, dtypes } = await runQuery(this.db, query, {
|
|
321
|
+
columns: true,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
for (let cidx = 0; cidx < columns.length; cidx++) {
|
|
325
|
+
if (cidx === 0 && is_group_by && !is_split_by) {
|
|
326
|
+
// This is the grouping_id column, skip it
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let col = columns[cidx];
|
|
331
|
+
if (is_split_by && !col.startsWith("__ROW_PATH_")) {
|
|
332
|
+
col = col.replaceAll("_", "|");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const dtype = duckdbTypeToPsp(dtypes[cidx]) as ColumnType;
|
|
336
|
+
|
|
337
|
+
const isDecimal = dtypes[cidx].startsWith("Decimal");
|
|
338
|
+
for (let ridx = 0; ridx < rows.length; ridx++) {
|
|
339
|
+
const row = rows[ridx];
|
|
340
|
+
const grouping_id = row["__GROUPING_ID__"];
|
|
341
|
+
let value = row[columns[cidx]];
|
|
342
|
+
if (isDecimal) {
|
|
343
|
+
value = convertDecimalToNumber(value, dtypes[cidx]);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (typeof value === "bigint") {
|
|
347
|
+
value = Number(value);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (dtype === "datetime" && typeof value === "string") {
|
|
351
|
+
value = +new Date(value);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (dtype === "string" && typeof value !== "string") {
|
|
355
|
+
value = `${value}`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
dataSlice.setCol(dtype, col, ridx, value, grouping_id);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|