@uwdata/mosaic-core 0.17.0 → 0.19.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/LICENSE +47 -0
- package/README.md +0 -1
- package/dist/src/Coordinator.d.ts +159 -0
- package/dist/src/Coordinator.d.ts.map +1 -0
- package/dist/src/Coordinator.js +250 -0
- package/dist/src/Coordinator.js.map +1 -0
- package/dist/src/MosaicClient.d.ts +138 -0
- package/dist/src/MosaicClient.d.ts.map +1 -0
- package/dist/src/MosaicClient.js +214 -0
- package/dist/src/MosaicClient.js.map +1 -0
- package/dist/src/Param.d.ts +56 -0
- package/dist/src/Param.d.ts.map +1 -0
- package/dist/src/Param.js +89 -0
- package/dist/src/Param.js.map +1 -0
- package/dist/src/QueryConsolidator.d.ts +11 -0
- package/dist/src/QueryConsolidator.d.ts.map +1 -0
- package/dist/src/QueryConsolidator.js +249 -0
- package/dist/src/QueryConsolidator.js.map +1 -0
- package/dist/src/QueryManager.d.ts +77 -0
- package/dist/src/QueryManager.d.ts.map +1 -0
- package/dist/src/QueryManager.js +174 -0
- package/dist/src/QueryManager.js.map +1 -0
- package/dist/src/Selection.d.ts +222 -0
- package/dist/src/Selection.d.ts.map +1 -0
- package/dist/src/Selection.js +322 -0
- package/dist/src/Selection.js.map +1 -0
- package/dist/src/SelectionClause.d.ts +222 -0
- package/dist/src/SelectionClause.d.ts.map +1 -0
- package/dist/src/SelectionClause.js +168 -0
- package/dist/src/SelectionClause.js.map +1 -0
- package/dist/src/connectors/Connector.d.ts +26 -0
- package/dist/src/connectors/Connector.d.ts.map +1 -0
- package/dist/src/connectors/Connector.js +2 -0
- package/dist/src/connectors/Connector.js.map +1 -0
- package/dist/src/connectors/rest.d.ts +24 -0
- package/dist/src/connectors/rest.d.ts.map +1 -0
- package/dist/src/connectors/rest.js +37 -0
- package/dist/src/connectors/rest.js.map +1 -0
- package/dist/src/connectors/socket.d.ts +40 -0
- package/dist/src/connectors/socket.d.ts.map +1 -0
- package/dist/src/connectors/socket.js +115 -0
- package/dist/src/connectors/socket.js.map +1 -0
- package/dist/src/connectors/wasm.d.ts +56 -0
- package/dist/src/connectors/wasm.d.ts.map +1 -0
- package/dist/src/connectors/wasm.js +116 -0
- package/dist/src/connectors/wasm.js.map +1 -0
- package/dist/src/index.d.ts +28 -0
- package/dist/src/index.d.ts.map +1 -0
- package/{src → dist/src}/index.js +8 -11
- package/dist/src/index.js.map +1 -0
- package/dist/src/make-client.d.ts +33 -0
- package/dist/src/make-client.d.ts.map +1 -0
- package/dist/src/make-client.js +52 -0
- package/dist/src/make-client.js.map +1 -0
- package/dist/src/preagg/PreAggregator.d.ts +150 -0
- package/dist/src/preagg/PreAggregator.d.ts.map +1 -0
- package/dist/src/preagg/PreAggregator.js +382 -0
- package/dist/src/preagg/PreAggregator.js.map +1 -0
- package/dist/src/preagg/preagg-columns.d.ts +16 -0
- package/dist/src/preagg/preagg-columns.d.ts.map +1 -0
- package/dist/src/preagg/preagg-columns.js +95 -0
- package/dist/src/preagg/preagg-columns.js.map +1 -0
- package/dist/src/preagg/sufficient-statistics.d.ts +14 -0
- package/dist/src/preagg/sufficient-statistics.d.ts.map +1 -0
- package/dist/src/preagg/sufficient-statistics.js +446 -0
- package/dist/src/preagg/sufficient-statistics.js.map +1 -0
- package/dist/src/types.d.ts +77 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/util/AsyncDispatch.d.ts +121 -0
- package/dist/src/util/AsyncDispatch.d.ts.map +1 -0
- package/dist/src/util/AsyncDispatch.js +188 -0
- package/dist/src/util/AsyncDispatch.js.map +1 -0
- package/dist/src/util/cache.d.ts +19 -0
- package/dist/src/util/cache.d.ts.map +1 -0
- package/dist/src/util/cache.js +66 -0
- package/dist/src/util/cache.js.map +1 -0
- package/dist/src/util/decode-ipc.d.ts +12 -0
- package/dist/src/util/decode-ipc.d.ts.map +1 -0
- package/{src → dist/src}/util/decode-ipc.js +5 -6
- package/dist/src/util/decode-ipc.js.map +1 -0
- package/dist/src/util/distinct.d.ts +3 -0
- package/dist/src/util/distinct.d.ts.map +1 -0
- package/dist/src/util/distinct.js +16 -0
- package/dist/src/util/distinct.js.map +1 -0
- package/dist/src/util/field-info.d.ts +26 -0
- package/dist/src/util/field-info.d.ts.map +1 -0
- package/dist/src/util/field-info.js +91 -0
- package/dist/src/util/field-info.js.map +1 -0
- package/dist/src/util/hash.d.ts +2 -0
- package/dist/src/util/hash.d.ts.map +1 -0
- package/dist/src/util/hash.js +26 -0
- package/dist/src/util/hash.js.map +1 -0
- package/dist/src/util/is-activatable.d.ts +8 -0
- package/dist/src/util/is-activatable.d.ts.map +1 -0
- package/dist/src/util/is-activatable.js +10 -0
- package/dist/src/util/is-activatable.js.map +1 -0
- package/dist/src/util/is-arrow-table.d.ts +9 -0
- package/dist/src/util/is-arrow-table.d.ts.map +1 -0
- package/dist/src/util/is-arrow-table.js +11 -0
- package/dist/src/util/is-arrow-table.js.map +1 -0
- package/dist/src/util/js-type.d.ts +9 -0
- package/dist/src/util/js-type.d.ts.map +1 -0
- package/dist/src/util/js-type.js +59 -0
- package/dist/src/util/js-type.js.map +1 -0
- package/dist/src/util/priority-queue.d.ts +35 -0
- package/dist/src/util/priority-queue.d.ts.map +1 -0
- package/dist/src/util/priority-queue.js +81 -0
- package/dist/src/util/priority-queue.js.map +1 -0
- package/dist/src/util/query-result.d.ts +47 -0
- package/dist/src/util/query-result.d.ts.map +1 -0
- package/dist/src/util/query-result.js +83 -0
- package/dist/src/util/query-result.js.map +1 -0
- package/dist/src/util/synchronizer.d.ts +36 -0
- package/dist/src/util/synchronizer.d.ts.map +1 -0
- package/dist/src/util/synchronizer.js +52 -0
- package/dist/src/util/synchronizer.js.map +1 -0
- package/dist/src/util/throttle.d.ts +12 -0
- package/dist/src/util/throttle.d.ts.map +1 -0
- package/dist/src/util/throttle.js +51 -0
- package/dist/src/util/throttle.js.map +1 -0
- package/dist/src/util/to-data-columns.d.ts +22 -0
- package/dist/src/util/to-data-columns.d.ts.map +1 -0
- package/dist/src/util/to-data-columns.js +51 -0
- package/dist/src/util/to-data-columns.js.map +1 -0
- package/dist/src/util/void-logger.d.ts +13 -0
- package/dist/src/util/void-logger.d.ts.map +1 -0
- package/dist/src/util/void-logger.js +13 -0
- package/dist/src/util/void-logger.js.map +1 -0
- package/package.json +17 -11
- package/src/Coordinator.ts +396 -0
- package/src/{MosaicClient.js → MosaicClient.ts} +50 -43
- package/src/{Param.js → Param.ts} +29 -28
- package/src/{QueryConsolidator.js → QueryConsolidator.ts} +85 -62
- package/src/{QueryManager.js → QueryManager.ts} +61 -54
- package/src/Selection.ts +391 -0
- package/src/SelectionClause.ts +357 -0
- package/src/connectors/Connector.ts +6 -6
- package/src/connectors/rest.ts +56 -0
- package/src/connectors/{socket.js → socket.ts} +53 -42
- package/src/connectors/{wasm.js → wasm.ts} +52 -63
- package/src/index.ts +42 -0
- package/src/make-client.ts +93 -0
- package/src/preagg/{PreAggregator.js → PreAggregator.ts} +164 -145
- package/src/preagg/{preagg-columns.js → preagg-columns.ts} +27 -24
- package/src/preagg/{sufficient-statistics.js → sufficient-statistics.ts} +160 -110
- package/src/types.ts +24 -9
- package/src/util/{AsyncDispatch.js → AsyncDispatch.ts} +62 -43
- package/src/util/{cache.js → cache.ts} +25 -15
- package/src/util/decode-ipc.ts +15 -0
- package/src/util/{distinct.js → distinct.ts} +3 -3
- package/src/util/{field-info.js → field-info.ts} +30 -31
- package/src/util/{hash.js → hash.ts} +4 -4
- package/src/util/is-activatable.ts +11 -0
- package/src/util/is-arrow-table.ts +12 -0
- package/src/util/{js-type.js → js-type.ts} +7 -5
- package/src/util/{priority-queue.js → priority-queue.ts} +32 -20
- package/src/util/{query-result.js → query-result.ts} +24 -17
- package/src/util/synchronizer.ts +56 -0
- package/src/util/throttle.ts +59 -0
- package/src/util/to-data-columns.ts +65 -0
- package/src/util/void-logger.ts +23 -0
- package/src/Coordinator.js +0 -313
- package/src/Selection.js +0 -380
- package/src/SelectionClause.js +0 -159
- package/src/connectors/rest.js +0 -38
- package/src/index-types.ts +0 -5
- package/src/make-client.js +0 -101
- package/src/util/is-activatable.js +0 -8
- package/src/util/is-arrow-table.js +0 -10
- package/src/util/selection-types.ts +0 -137
- package/src/util/synchronizer.js +0 -47
- package/src/util/throttle.js +0 -54
- package/src/util/to-data-columns.js +0 -60
- package/src/util/void-logger.js +0 -13
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -3
|
@@ -3,10 +3,10 @@ import { distinct } from './util/distinct.js';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Test if a value is a Param instance.
|
|
6
|
-
* @param
|
|
7
|
-
* @returns
|
|
6
|
+
* @param x The value to test.
|
|
7
|
+
* @returns True if the input is a Param, false otherwise.
|
|
8
8
|
*/
|
|
9
|
-
export function isParam(x) {
|
|
9
|
+
export function isParam<T>(x: unknown): x is Param<T> {
|
|
10
10
|
return x instanceof Param;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -14,61 +14,62 @@ export function isParam(x) {
|
|
|
14
14
|
* Represents a dynamic parameter that dispatches updates
|
|
15
15
|
* upon parameter changes.
|
|
16
16
|
*/
|
|
17
|
-
export class Param extends AsyncDispatch {
|
|
17
|
+
export class Param<T> extends AsyncDispatch<T> {
|
|
18
|
+
protected _value?: T;
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Create a new Param instance.
|
|
21
|
-
* @param
|
|
22
|
+
* @param value The initial value of the Param.
|
|
22
23
|
*/
|
|
23
|
-
constructor(value) {
|
|
24
|
+
constructor(value?: T) {
|
|
24
25
|
super();
|
|
25
26
|
this._value = value;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Create a new Param instance with the given initial value.
|
|
30
|
-
* @param
|
|
31
|
-
* @returns
|
|
31
|
+
* @param value The initial value of the Param.
|
|
32
|
+
* @returns The new Param instance.
|
|
32
33
|
*/
|
|
33
|
-
static value(value) {
|
|
34
|
+
static value<T>(value: T): Param<T> {
|
|
34
35
|
return new Param(value);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Create a new Param instance over an array of initial values,
|
|
39
40
|
* which may contain nested Params.
|
|
40
|
-
* @param
|
|
41
|
-
* @returns
|
|
41
|
+
* @param values The initial values of the Param.
|
|
42
|
+
* @returns The new Param instance.
|
|
42
43
|
*/
|
|
43
|
-
static array(values) {
|
|
44
|
+
static array<T>(values: (T|Param<T>)[]): Param<T[]> {
|
|
44
45
|
if (values.some(v => isParam(v))) {
|
|
45
|
-
const p = new Param();
|
|
46
|
-
const update = () => {
|
|
47
|
-
p.update(values.map(v => isParam(v) ? v.value : v));
|
|
46
|
+
const p = new Param<T[]>();
|
|
47
|
+
const update = (): void => {
|
|
48
|
+
p.update(values.map(v => isParam<T>(v) ? v.value! : v));
|
|
48
49
|
};
|
|
49
50
|
update();
|
|
50
51
|
values.forEach(v => isParam(v) ? v.addEventListener('value', update) : 0);
|
|
51
52
|
return p;
|
|
52
53
|
}
|
|
53
|
-
return new Param(values)
|
|
54
|
+
return new Param(values) as Param<T[]>;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
58
|
* The current value of the Param.
|
|
58
59
|
*/
|
|
59
|
-
get value() {
|
|
60
|
+
get value(): T | undefined {
|
|
60
61
|
return this._value;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
/**
|
|
64
65
|
* Update the Param value
|
|
65
|
-
* @param
|
|
66
|
-
* @param
|
|
67
|
-
* @param
|
|
66
|
+
* @param value The new value of the Param.
|
|
67
|
+
* @param options The update options.
|
|
68
|
+
* @param options.force A boolean flag indicating if the Param
|
|
68
69
|
* should emit a 'value' event even if the internal value is unchanged.
|
|
69
|
-
* @returns
|
|
70
|
+
* @returns This Param instance.
|
|
70
71
|
*/
|
|
71
|
-
update(value, { force } = {}) {
|
|
72
|
+
update(value: T, { force }: { force?: boolean } = {}): this {
|
|
72
73
|
const shouldEmit = distinct(this._value, value) || force;
|
|
73
74
|
if (shouldEmit) {
|
|
74
75
|
this.emit('value', value);
|
|
@@ -81,14 +82,14 @@ export class Param extends AsyncDispatch {
|
|
|
81
82
|
/**
|
|
82
83
|
* Upon value-typed updates, sets the current value to the input value
|
|
83
84
|
* immediately prior to the event value being emitted to listeners.
|
|
84
|
-
* @param
|
|
85
|
-
* @param
|
|
86
|
-
* @returns
|
|
85
|
+
* @param type The event type.
|
|
86
|
+
* @param value The input event value.
|
|
87
|
+
* @returns The input event value.
|
|
87
88
|
*/
|
|
88
|
-
willEmit(type, value) {
|
|
89
|
+
willEmit(type: string, value: T): T {
|
|
89
90
|
if (type === 'value') {
|
|
90
|
-
this._value = value;
|
|
91
|
+
this._value = value as T;
|
|
91
92
|
}
|
|
92
93
|
return value;
|
|
93
94
|
}
|
|
94
|
-
}
|
|
95
|
+
}
|
|
@@ -1,26 +1,45 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ExprNode, Query, SelectQuery } from '@uwdata/mosaic-sql';
|
|
2
|
+
import type { Table } from '@uwdata/flechette';
|
|
2
3
|
import { isAggregateExpression, isColumnRef, isDescribeQuery, isSelectQuery } from '@uwdata/mosaic-sql';
|
|
3
4
|
import { QueryResult } from './util/query-result.js';
|
|
5
|
+
import type { Cache, QueryEntry, QueryType } from './types.js';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
interface GroupEntry {
|
|
8
|
+
entry: QueryEntry;
|
|
9
|
+
priority: number;
|
|
10
|
+
index: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface QueryGroup extends Array<GroupEntry> {
|
|
14
|
+
query?: Query;
|
|
15
|
+
result?: QueryResult;
|
|
16
|
+
maps?: Array<Array<[string, string]>>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function wait(callback: () => void): unknown {
|
|
20
|
+
if (typeof requestAnimationFrame !== 'undefined') {
|
|
21
|
+
return requestAnimationFrame(callback);
|
|
22
|
+
} else if (typeof setImmediate !== 'undefined') {
|
|
23
|
+
return setImmediate(callback);
|
|
24
|
+
} else {
|
|
25
|
+
return setTimeout(callback);
|
|
26
|
+
}
|
|
11
27
|
}
|
|
12
28
|
|
|
13
29
|
/**
|
|
14
30
|
* Create a consolidator to combine structurally compatible queries.
|
|
15
|
-
* @param
|
|
16
|
-
* @param
|
|
31
|
+
* @param enqueue Query manager enqueue method
|
|
32
|
+
* @param cache Client-side query cache (sql -> data)
|
|
17
33
|
* @returns A consolidator object
|
|
18
34
|
*/
|
|
19
|
-
export function consolidator(
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
export function consolidator(
|
|
36
|
+
enqueue: (entry: QueryEntry, priority?: number) => void,
|
|
37
|
+
cache: Cache
|
|
38
|
+
) {
|
|
39
|
+
let pending: GroupEntry[] = [];
|
|
40
|
+
let id: unknown = 0;
|
|
22
41
|
|
|
23
|
-
function run() {
|
|
42
|
+
function run(): void {
|
|
24
43
|
// group queries into bundles that can be consolidated
|
|
25
44
|
const groups = entryGroups(pending, cache);
|
|
26
45
|
pending = [];
|
|
@@ -34,37 +53,38 @@ export function consolidator(enqueue, cache) {
|
|
|
34
53
|
}
|
|
35
54
|
|
|
36
55
|
return {
|
|
37
|
-
add(entry, priority) {
|
|
56
|
+
add(entry: QueryEntry, priority: number): void {
|
|
38
57
|
if (entry.request.type === 'arrow') {
|
|
39
58
|
// wait one frame, gather an ordered list of queries
|
|
40
59
|
// only Apache Arrow is supported, so we can project efficiently
|
|
41
|
-
id
|
|
60
|
+
id ||= wait(() => run());
|
|
42
61
|
pending.push({ entry, priority, index: pending.length });
|
|
43
62
|
} else {
|
|
44
63
|
enqueue(entry, priority);
|
|
45
64
|
}
|
|
46
65
|
}
|
|
47
|
-
}
|
|
66
|
+
};
|
|
48
67
|
}
|
|
49
68
|
|
|
50
69
|
/**
|
|
51
70
|
* Segment query requests into consolidation-compatible groups.
|
|
52
|
-
* @param
|
|
71
|
+
* @param entries Query request entries ({ request, result } objects)
|
|
72
|
+
* @param cache Client-side query cache
|
|
53
73
|
* @returns An array of grouped entry arrays
|
|
54
74
|
*/
|
|
55
|
-
function entryGroups(entries, cache) {
|
|
56
|
-
const groups = [];
|
|
57
|
-
const groupMap = new Map;
|
|
75
|
+
function entryGroups(entries: GroupEntry[], cache: Cache): QueryGroup[] {
|
|
76
|
+
const groups: QueryGroup[] = [];
|
|
77
|
+
const groupMap = new Map<string, QueryGroup>();
|
|
58
78
|
|
|
59
79
|
for (const query of entries) {
|
|
60
80
|
const { entry: { request } } = query;
|
|
61
81
|
const key = consolidationKey(request.query, cache);
|
|
62
82
|
if (!groupMap.has(key)) {
|
|
63
|
-
const list = [];
|
|
83
|
+
const list: QueryGroup = [];
|
|
64
84
|
groups.push(list);
|
|
65
85
|
groupMap.set(key, list);
|
|
66
86
|
}
|
|
67
|
-
groupMap.get(key)
|
|
87
|
+
groupMap.get(key)!.push(query);
|
|
68
88
|
}
|
|
69
89
|
|
|
70
90
|
return groups;
|
|
@@ -75,19 +95,19 @@ function entryGroups(entries, cache) {
|
|
|
75
95
|
* Queries with matching keys are conosolidation-compatible.
|
|
76
96
|
* If a query is found in the cache, it is exempted from consolidation,
|
|
77
97
|
* which is indicated by returning the precise query SQL as the key.
|
|
78
|
-
* @param
|
|
79
|
-
* @param
|
|
98
|
+
* @param query The input query.
|
|
99
|
+
* @param cache The query cache (sql -> data).
|
|
80
100
|
* @returns a key string
|
|
81
101
|
*/
|
|
82
|
-
function consolidationKey(query, cache) {
|
|
102
|
+
function consolidationKey(query: QueryType, cache: Cache): string {
|
|
83
103
|
const sql = `${query}`;
|
|
84
104
|
if (isSelectQuery(query) && !cache.get(sql)) {
|
|
85
105
|
if (
|
|
86
|
-
query.
|
|
87
|
-
query.
|
|
106
|
+
query._where.length || query._qualify.length || query._having.length ||
|
|
107
|
+
query._orderby.length || query._distinct
|
|
88
108
|
) {
|
|
89
|
-
//
|
|
90
|
-
// that
|
|
109
|
+
// bail if query includes clauses that may refer to *derived* columns
|
|
110
|
+
// that we can't resolve. also do not consolidate distinct queries
|
|
91
111
|
return sql;
|
|
92
112
|
}
|
|
93
113
|
|
|
@@ -99,11 +119,11 @@ function consolidationKey(query, cache) {
|
|
|
99
119
|
// we resolve these against the true grouping expressions
|
|
100
120
|
const groupby = query._groupby;
|
|
101
121
|
if (groupby.length) {
|
|
102
|
-
const map = {}; // expression map (alias -> expr)
|
|
122
|
+
const map: Record<string, ExprNode> = {}; // expression map (alias -> expr)
|
|
103
123
|
query._select.forEach(({ alias, expr }) => map[alias] = expr);
|
|
104
124
|
q.setGroupby(groupby.map(e => (isColumnRef(e) && map[e.column]) || e));
|
|
105
125
|
}
|
|
106
|
-
else if (query._select.some(e => isAggregateExpression(e.expr))) {
|
|
126
|
+
else if (query._select.some(e => isAggregateExpression(e.expr!))) {
|
|
107
127
|
// if query is an ungrouped aggregate, add an explicit groupby to
|
|
108
128
|
// prevent improper consolidation with non-aggregate queries
|
|
109
129
|
q.setGroupby('ALL');
|
|
@@ -119,10 +139,13 @@ function consolidationKey(query, cache) {
|
|
|
119
139
|
|
|
120
140
|
/**
|
|
121
141
|
* Issue queries, consolidating where possible.
|
|
122
|
-
* @param
|
|
123
|
-
* @param
|
|
142
|
+
* @param group Array of bundled query entries
|
|
143
|
+
* @param enqueue Add entry to query queue
|
|
124
144
|
*/
|
|
125
|
-
function consolidate(
|
|
145
|
+
function consolidate(
|
|
146
|
+
group: QueryGroup,
|
|
147
|
+
enqueue: (entry: QueryEntry, priority?: number) => void
|
|
148
|
+
): void {
|
|
126
149
|
if (shouldConsolidate(group)) {
|
|
127
150
|
// issue a single consolidated query
|
|
128
151
|
enqueue({
|
|
@@ -143,11 +166,11 @@ function consolidate(group, enqueue) {
|
|
|
143
166
|
|
|
144
167
|
/**
|
|
145
168
|
* Check if a group contains multiple distinct queries.
|
|
146
|
-
* @param
|
|
169
|
+
* @param group Array of bundled query entries
|
|
147
170
|
* @returns false if group contains a single (possibly repeated) query,
|
|
148
171
|
* otherwise true
|
|
149
172
|
*/
|
|
150
|
-
function shouldConsolidate(group) {
|
|
173
|
+
function shouldConsolidate(group: QueryGroup): boolean {
|
|
151
174
|
if (group.length > 1) {
|
|
152
175
|
const sql = `${group[0].entry.request.query}`;
|
|
153
176
|
for (let i = 1; i < group.length; ++i) {
|
|
@@ -161,37 +184,39 @@ function shouldConsolidate(group) {
|
|
|
161
184
|
|
|
162
185
|
/**
|
|
163
186
|
* Create a consolidated query for a group.
|
|
164
|
-
* @param
|
|
187
|
+
* @param group Array of bundled query entries
|
|
165
188
|
* @returns A consolidated Query instance
|
|
166
189
|
*/
|
|
167
|
-
function consolidatedQuery(group) {
|
|
168
|
-
const maps = group.maps = [];
|
|
169
|
-
const fields = new Map;
|
|
190
|
+
function consolidatedQuery(group: QueryGroup): Query {
|
|
191
|
+
const maps: Array<Array<[string, string]>> = group.maps = [];
|
|
192
|
+
const fields = new Map<string, [string, ExprNode]>();
|
|
170
193
|
|
|
171
194
|
// gather select fields
|
|
172
195
|
for (const item of group) {
|
|
173
|
-
const
|
|
174
|
-
const fieldMap = [];
|
|
196
|
+
const query = item.entry.request.query as SelectQuery;
|
|
197
|
+
const fieldMap: Array<[string, string]> = [];
|
|
175
198
|
maps.push(fieldMap);
|
|
176
199
|
for (const { alias, expr } of query._select) {
|
|
177
200
|
const e = `${expr}`;
|
|
178
201
|
if (!fields.has(e)) {
|
|
179
202
|
fields.set(e, [`col${fields.size}`, expr]);
|
|
180
203
|
}
|
|
181
|
-
const [name] = fields.get(e)
|
|
204
|
+
const [name] = fields.get(e)!;
|
|
182
205
|
fieldMap.push([name, alias]);
|
|
183
206
|
}
|
|
184
207
|
}
|
|
185
208
|
|
|
186
209
|
// use a cloned query as a starting point
|
|
187
|
-
const query = group[0].entry.request.query.clone();
|
|
210
|
+
const query = (group[0].entry.request.query as SelectQuery).clone();
|
|
188
211
|
|
|
189
212
|
// update group by statement as needed
|
|
190
213
|
const groupby = query._groupby;
|
|
191
214
|
if (groupby.length) {
|
|
192
|
-
const map = {};
|
|
215
|
+
const map: Record<string, string> = {};
|
|
193
216
|
group.maps[0].forEach(([name, as]) => map[as] = name);
|
|
194
|
-
query.setGroupby(
|
|
217
|
+
query.setGroupby(
|
|
218
|
+
groupby.map((e: ExprNode) => (isColumnRef(e) && map[e.column]) || e)
|
|
219
|
+
);
|
|
195
220
|
}
|
|
196
221
|
|
|
197
222
|
// update select statement and return
|
|
@@ -200,10 +225,10 @@ function consolidatedQuery(group) {
|
|
|
200
225
|
|
|
201
226
|
/**
|
|
202
227
|
* Process query results, dispatch results to original requests
|
|
203
|
-
* @param
|
|
204
|
-
* @param
|
|
228
|
+
* @param group Array of query requests
|
|
229
|
+
* @param cache Client-side query cache (sql -> data)
|
|
205
230
|
*/
|
|
206
|
-
async function processResults(group, cache) {
|
|
231
|
+
async function processResults(group: QueryGroup, cache: Cache): Promise<void> {
|
|
207
232
|
const { maps, query, result } = group;
|
|
208
233
|
|
|
209
234
|
// exit early if no consolidation performed
|
|
@@ -211,9 +236,9 @@ async function processResults(group, cache) {
|
|
|
211
236
|
if (!maps) return;
|
|
212
237
|
|
|
213
238
|
// await consolidated query result, pass errors if needed
|
|
214
|
-
let data;
|
|
239
|
+
let data: Table;
|
|
215
240
|
try {
|
|
216
|
-
data = await result;
|
|
241
|
+
data = await result as Table;
|
|
217
242
|
} catch (err) {
|
|
218
243
|
// pass error to consolidated queries
|
|
219
244
|
for (const { entry } of group) {
|
|
@@ -224,7 +249,7 @@ async function processResults(group, cache) {
|
|
|
224
249
|
|
|
225
250
|
// extract result for each query in the consolidation group
|
|
226
251
|
// update cache and pass extract to original issuer
|
|
227
|
-
const describe = isDescribeQuery(query);
|
|
252
|
+
const describe = isDescribeQuery(query!);
|
|
228
253
|
group.forEach(({ entry }, index) => {
|
|
229
254
|
const { request, result } = entry;
|
|
230
255
|
const map = maps[index];
|
|
@@ -240,29 +265,27 @@ async function processResults(group, cache) {
|
|
|
240
265
|
|
|
241
266
|
/**
|
|
242
267
|
* Project a consolidated result to a client result
|
|
243
|
-
* @param
|
|
244
|
-
*
|
|
245
|
-
* @param {[string, string][]} map Column name map as [source, target] pairs
|
|
268
|
+
* @param data Consolidated query result, as an Arrow Table
|
|
269
|
+
* @param map Column name map as [source, target] pairs
|
|
246
270
|
* @returns the projected Apache Arrow table
|
|
247
271
|
*/
|
|
248
|
-
function projectResult(data, map) {
|
|
272
|
+
function projectResult(data: Table, map: Array<[string, string]>): Table {
|
|
249
273
|
return data.select(map.map(x => x[0]), map.map(x => x[1]));
|
|
250
274
|
}
|
|
251
275
|
|
|
252
276
|
/**
|
|
253
277
|
* Filter a consolidated describe query result to a client result
|
|
254
|
-
* @param
|
|
255
|
-
*
|
|
256
|
-
* @param {[string, string][]} map Column name map as [source, target] pairs
|
|
278
|
+
* @param data Consolidated query result, as an Arrow Table
|
|
279
|
+
* @param map Column name map as [source, target] pairs
|
|
257
280
|
* @returns the filtered table data
|
|
258
281
|
*/
|
|
259
|
-
function filterResult(data, map) {
|
|
282
|
+
function filterResult(data: Table, map: Array<[string, string]>): unknown[] {
|
|
260
283
|
const lookup = new Map(map);
|
|
261
|
-
const result = [];
|
|
284
|
+
const result: unknown[] = [];
|
|
262
285
|
for (const d of data) {
|
|
263
286
|
if (lookup.has(d.column_name)) {
|
|
264
287
|
result.push({ ...d, column_name: lookup.get(d.column_name) })
|
|
265
288
|
}
|
|
266
289
|
}
|
|
267
290
|
return result;
|
|
268
|
-
}
|
|
291
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { Connector } from './connectors/Connector.js';
|
|
2
|
+
import type { Cache, Logger, QueryEntry, QueryRequest } from './types.js';
|
|
3
3
|
import { consolidator } from './QueryConsolidator.js';
|
|
4
4
|
import { lruCache, voidCache } from './util/cache.js';
|
|
5
5
|
import { PriorityQueue } from './util/priority-queue.js';
|
|
@@ -9,38 +9,38 @@ import { voidLogger } from './util/void-logger.js';
|
|
|
9
9
|
export const Priority = Object.freeze({ High: 0, Normal: 1, Low: 2 });
|
|
10
10
|
|
|
11
11
|
export class QueryManager {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
private queue: PriorityQueue<QueryEntry>;
|
|
13
|
+
private db: Connector | null;
|
|
14
|
+
private clientCache: Cache | null;
|
|
15
|
+
private _logger: Logger;
|
|
16
|
+
private _logQueries: boolean;
|
|
17
|
+
private _consolidate: ReturnType<typeof consolidator> | null;
|
|
18
|
+
/** Requests pending with the query manager. */
|
|
19
|
+
public pendingResults: QueryResult[];
|
|
20
|
+
private maxConcurrentRequests: number;
|
|
21
|
+
private pendingExec: boolean;
|
|
22
|
+
|
|
23
|
+
constructor(maxConcurrentRequests: number = 32) {
|
|
16
24
|
this.queue = new PriorityQueue(3);
|
|
17
|
-
/** @type {Connector} */
|
|
18
25
|
this.db = null;
|
|
19
|
-
/** @type {Cache} */
|
|
20
26
|
this.clientCache = null;
|
|
21
|
-
/** @type {Logger} */
|
|
22
27
|
this._logger = voidLogger();
|
|
23
|
-
/** @type {boolean} */
|
|
24
28
|
this._logQueries = false;
|
|
25
|
-
/** @type {ReturnType<typeof consolidator> | null} */
|
|
26
29
|
this._consolidate = null;
|
|
27
|
-
/**
|
|
28
|
-
* Requests pending with the query manager.
|
|
29
|
-
* @type {QueryResult[]}
|
|
30
|
-
*/
|
|
31
30
|
this.pendingResults = [];
|
|
32
|
-
/** @type {number} */
|
|
33
31
|
this.maxConcurrentRequests = maxConcurrentRequests;
|
|
34
|
-
/** @type {boolean} */
|
|
35
32
|
this.pendingExec = false;
|
|
36
33
|
}
|
|
37
34
|
|
|
38
|
-
next() {
|
|
35
|
+
next(): void {
|
|
39
36
|
if (this.queue.isEmpty() || this.pendingResults.length > this.maxConcurrentRequests || this.pendingExec) {
|
|
40
37
|
return;
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
const
|
|
40
|
+
const entry = this.queue.next();
|
|
41
|
+
if (!entry) return;
|
|
42
|
+
|
|
43
|
+
const { request, result } = entry;
|
|
44
44
|
|
|
45
45
|
this.pendingResults.push(result);
|
|
46
46
|
if (request.type === 'exec') this.pendingExec = true;
|
|
@@ -48,7 +48,7 @@ export class QueryManager {
|
|
|
48
48
|
this.submit(request, result).finally(() => {
|
|
49
49
|
// return from the queue all requests that are ready
|
|
50
50
|
while (this.pendingResults.length && this.pendingResults[0].state !== QueryState.pending) {
|
|
51
|
-
const result = this.pendingResults.shift()
|
|
51
|
+
const result = this.pendingResults.shift()!;
|
|
52
52
|
if (result.state === QueryState.ready) {
|
|
53
53
|
result.fulfill();
|
|
54
54
|
} else if (result.state === QueryState.done) {
|
|
@@ -62,29 +62,27 @@ export class QueryManager {
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Add an entry to the query queue with a priority.
|
|
65
|
-
* @param
|
|
66
|
-
* @param
|
|
67
|
-
* @param {QueryResult} [entry.result] The query result.
|
|
68
|
-
* @param {number} priority The query priority, defaults to `Priority.Normal`.
|
|
65
|
+
* @param entry The entry to add.
|
|
66
|
+
* @param priority The query priority, defaults to `Priority.Normal`.
|
|
69
67
|
*/
|
|
70
|
-
enqueue(entry, priority = Priority.Normal) {
|
|
68
|
+
enqueue(entry: QueryEntry, priority: number = Priority.Normal): void {
|
|
71
69
|
this.queue.insert(entry, priority);
|
|
72
70
|
this.next();
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
/**
|
|
76
74
|
* Submit the query to the connector.
|
|
77
|
-
* @param
|
|
78
|
-
* @param
|
|
75
|
+
* @param request The request.
|
|
76
|
+
* @param result The query result.
|
|
79
77
|
*/
|
|
80
|
-
async submit(request, result) {
|
|
78
|
+
async submit(request: QueryRequest, result: QueryResult): Promise<void> {
|
|
81
79
|
try {
|
|
82
80
|
const { query, type, cache = false, options } = request;
|
|
83
81
|
const sql = query ? `${query}` : null;
|
|
84
82
|
|
|
85
83
|
// check query cache
|
|
86
84
|
if (cache) {
|
|
87
|
-
const cached = this.clientCache
|
|
85
|
+
const cached = this.clientCache!.get(sql!);
|
|
88
86
|
if (cached) {
|
|
89
87
|
const data = await cached;
|
|
90
88
|
this._logger.debug('Cache');
|
|
@@ -99,12 +97,13 @@ export class QueryManager {
|
|
|
99
97
|
this._logger.debug('Query', { type, sql, ...options });
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
// @ts-expect-error type may be exec | json | arrow
|
|
101
|
+
const promise = this.db!.query({ type, sql: sql!, ...options });
|
|
102
|
+
if (cache) this.clientCache!.set(sql!, promise);
|
|
104
103
|
|
|
105
104
|
const data = await promise;
|
|
106
105
|
|
|
107
|
-
if (cache) this.clientCache
|
|
106
|
+
if (cache) this.clientCache!.set(sql!, data);
|
|
108
107
|
|
|
109
108
|
this._logger.debug(`Request: ${(performance.now() - t0).toFixed(1)}`);
|
|
110
109
|
result.ready(type === 'exec' ? null : data);
|
|
@@ -115,10 +114,12 @@ export class QueryManager {
|
|
|
115
114
|
|
|
116
115
|
/**
|
|
117
116
|
* Get or set the current query cache.
|
|
118
|
-
* @param
|
|
119
|
-
* @returns
|
|
117
|
+
* @param value Cache value to set
|
|
118
|
+
* @returns Current cache
|
|
120
119
|
*/
|
|
121
|
-
cache(
|
|
120
|
+
cache(): Cache | null;
|
|
121
|
+
cache(value: Cache | boolean): Cache;
|
|
122
|
+
cache(value?: Cache | boolean): Cache | null {
|
|
122
123
|
return value !== undefined
|
|
123
124
|
? (this.clientCache = value === true ? lruCache() : (value || voidCache()))
|
|
124
125
|
: this.clientCache;
|
|
@@ -126,38 +127,44 @@ export class QueryManager {
|
|
|
126
127
|
|
|
127
128
|
/**
|
|
128
129
|
* Get or set the current logger.
|
|
129
|
-
* @param
|
|
130
|
-
* @returns
|
|
130
|
+
* @param value Logger to set
|
|
131
|
+
* @returns Current logger
|
|
131
132
|
*/
|
|
132
|
-
logger(
|
|
133
|
+
logger(): Logger;
|
|
134
|
+
logger(value: Logger): Logger;
|
|
135
|
+
logger(value?: Logger): Logger {
|
|
133
136
|
return value ? (this._logger = value) : this._logger;
|
|
134
137
|
}
|
|
135
138
|
|
|
136
139
|
/**
|
|
137
140
|
* Get or set if queries should be logged.
|
|
138
|
-
* @param
|
|
139
|
-
* @returns
|
|
141
|
+
* @param value Whether to log queries
|
|
142
|
+
* @returns Current logging state
|
|
140
143
|
*/
|
|
141
|
-
logQueries(
|
|
144
|
+
logQueries(): boolean;
|
|
145
|
+
logQueries(value: boolean): boolean;
|
|
146
|
+
logQueries(value?: boolean): boolean {
|
|
142
147
|
return value !== undefined ? this._logQueries = !!value : this._logQueries;
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
/**
|
|
146
151
|
* Get or set the database connector.
|
|
147
|
-
* @param
|
|
148
|
-
* @returns
|
|
152
|
+
* @param connector Connector to set
|
|
153
|
+
* @returns Current connector
|
|
149
154
|
*/
|
|
150
|
-
connector(
|
|
155
|
+
connector(): Connector | null;
|
|
156
|
+
connector(connector: Connector): Connector;
|
|
157
|
+
connector(connector?: Connector): Connector | null {
|
|
151
158
|
return connector ? (this.db = connector) : this.db;
|
|
152
159
|
}
|
|
153
160
|
|
|
154
161
|
/**
|
|
155
162
|
* Indicate if query consolidation should be performed.
|
|
156
|
-
* @param
|
|
163
|
+
* @param flag Whether to enable consolidation
|
|
157
164
|
*/
|
|
158
|
-
consolidate(flag) {
|
|
165
|
+
consolidate(flag: boolean): void {
|
|
159
166
|
if (flag && !this._consolidate) {
|
|
160
|
-
this._consolidate = consolidator(this.enqueue.bind(this), this.clientCache);
|
|
167
|
+
this._consolidate = consolidator(this.enqueue.bind(this), this.clientCache!);
|
|
161
168
|
} else if (!flag && this._consolidate) {
|
|
162
169
|
this._consolidate = null;
|
|
163
170
|
}
|
|
@@ -165,11 +172,11 @@ export class QueryManager {
|
|
|
165
172
|
|
|
166
173
|
/**
|
|
167
174
|
* Request a query result.
|
|
168
|
-
* @param
|
|
169
|
-
* @param
|
|
170
|
-
* @returns
|
|
175
|
+
* @param request The request.
|
|
176
|
+
* @param priority The query priority, defaults to `Priority.Normal`.
|
|
177
|
+
* @returns A query result promise.
|
|
171
178
|
*/
|
|
172
|
-
request(request, priority = Priority.Normal) {
|
|
179
|
+
request(request: QueryRequest, priority: number = Priority.Normal): QueryResult {
|
|
173
180
|
const result = new QueryResult();
|
|
174
181
|
const entry = { request, result };
|
|
175
182
|
if (this._consolidate) {
|
|
@@ -180,7 +187,7 @@ export class QueryManager {
|
|
|
180
187
|
return result;
|
|
181
188
|
}
|
|
182
189
|
|
|
183
|
-
cancel(requests) {
|
|
190
|
+
cancel(requests: QueryResult[]): void {
|
|
184
191
|
const set = new Set(requests);
|
|
185
192
|
if (set.size) {
|
|
186
193
|
this.queue.remove(({ result }) => {
|
|
@@ -199,7 +206,7 @@ export class QueryManager {
|
|
|
199
206
|
}
|
|
200
207
|
}
|
|
201
208
|
|
|
202
|
-
clear() {
|
|
209
|
+
clear(): void {
|
|
203
210
|
this.queue.remove(({ result }) => {
|
|
204
211
|
result.reject('Cleared');
|
|
205
212
|
return true;
|
|
@@ -210,4 +217,4 @@ export class QueryManager {
|
|
|
210
217
|
}
|
|
211
218
|
this.pendingResults = [];
|
|
212
219
|
}
|
|
213
|
-
}
|
|
220
|
+
}
|