@uwdata/mosaic-core 0.14.1 → 0.16.1

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.
Files changed (40) hide show
  1. package/dist/types/Coordinator.d.ts +37 -42
  2. package/dist/types/MosaicClient.d.ts +76 -44
  3. package/dist/types/QueryManager.d.ts +42 -17
  4. package/dist/types/Selection.d.ts +1 -1
  5. package/dist/types/SelectionClause.d.ts +1 -1
  6. package/dist/types/connectors/Connector.d.ts +25 -0
  7. package/dist/types/connectors/rest.d.ts +13 -17
  8. package/dist/types/connectors/socket.d.ts +98 -16
  9. package/dist/types/connectors/wasm.d.ts +133 -14
  10. package/dist/types/index-types.d.ts +1 -0
  11. package/dist/types/make-client.d.ts +51 -21
  12. package/dist/types/preagg/PreAggregator.d.ts +18 -16
  13. package/dist/types/preagg/preagg-columns.d.ts +2 -2
  14. package/dist/types/preagg/sufficient-statistics.d.ts +2 -2
  15. package/dist/types/types.d.ts +21 -0
  16. package/dist/types/util/cache.d.ts +14 -10
  17. package/dist/types/util/decode-ipc.d.ts +9 -4
  18. package/dist/types/util/void-logger.d.ts +3 -0
  19. package/package.json +4 -4
  20. package/src/Coordinator.js +36 -44
  21. package/src/MosaicClient.js +110 -46
  22. package/src/QueryConsolidator.js +2 -1
  23. package/src/QueryManager.js +31 -0
  24. package/src/Selection.js +4 -2
  25. package/src/SelectionClause.js +5 -2
  26. package/src/connectors/Connector.ts +30 -0
  27. package/src/connectors/rest.js +14 -12
  28. package/src/connectors/socket.js +112 -77
  29. package/src/connectors/wasm.js +118 -55
  30. package/src/index-types.ts +1 -0
  31. package/src/make-client.js +68 -31
  32. package/src/preagg/PreAggregator.js +20 -17
  33. package/src/preagg/preagg-columns.js +5 -2
  34. package/src/preagg/sufficient-statistics.js +2 -1
  35. package/src/types.ts +23 -0
  36. package/src/util/cache.js +20 -5
  37. package/src/util/decode-ipc.js +9 -5
  38. package/src/util/field-info.js +2 -1
  39. package/src/util/js-type.js +3 -1
  40. package/src/util/void-logger.js +4 -1
@@ -1,29 +1,49 @@
1
- import { Coordinator } from './Coordinator.js';
2
- import { Selection } from './Selection.js';
1
+ /** @import { Query } from '@uwdata/mosaic-sql' */
2
+ /** @import { Coordinator } from './Coordinator.js' */
3
+ /** @import { Selection } from './Selection.js' */
3
4
  import { throttle } from './util/throttle.js';
4
5
 
5
6
  /**
6
- * Base class for Mosaic clients.
7
+ * A Mosaic client is a data consumer that indicates its data needs to a
8
+ * Mosaic coordinator via the query method. The coordinator is responsible
9
+ * for issuing queries and returning results to the client.
10
+ *
11
+ * The client life-cycle consists of connection to a coordinator,
12
+ * initialization (potentially involving queries for data schema and summary
13
+ * statistic information), and then interactive queries that may be driven by
14
+ * an associated selection. When no longer needed, a client should be
15
+ * disconnected from the coordinator.
16
+ *
17
+ * When enabled, a client will initialize and respond to query update requests.
18
+ * If disabled, the client will delay initialization and not respond to queries
19
+ * until enabled again. Disabling a client can improve system performance when
20
+ * associated interface elements are offscreen or disabled.
7
21
  */
8
22
  export class MosaicClient {
9
23
  /**
10
- * Constructor.
11
- * @param {*} filterSelection An optional selection to interactively filter
12
- * this client's data. If provided, a coordinator will re-query and update
13
- * the client when the selection updates.
24
+ * Create a new client instance.
25
+ * @param {Selection} [filterSelection] An optional selection to
26
+ * interactively filter this client's data. If provided, a coordinator
27
+ * will re-query and update the client when the selection updates.
14
28
  */
15
29
  constructor(filterSelection) {
16
- /** @type {Selection} */
30
+ /** @type {Selection | undefined} */
17
31
  this._filterBy = filterSelection;
18
32
  this._requestUpdate = throttle(() => this.requestQuery(), true);
19
- /** @type {Coordinator} */
33
+ /** @type {Coordinator | null} */
20
34
  this._coordinator = null;
21
35
  /** @type {Promise<any>} */
22
36
  this._pending = Promise.resolve();
37
+ /** @type {boolean} */
38
+ this._enabled = true;
39
+ /** @type {boolean} */
40
+ this._initialized = false;
41
+ /** @type {Query | boolean | null} */
42
+ this._request = null;
23
43
  }
24
44
 
25
45
  /**
26
- * Return this client's connected coordinator.
46
+ * @returns {Coordinator | null} this client's connected coordinator.
27
47
  */
28
48
  get coordinator() {
29
49
  return this._coordinator;
@@ -36,6 +56,33 @@ export class MosaicClient {
36
56
  this._coordinator = coordinator;
37
57
  }
38
58
 
59
+ /**
60
+ * Return this client's enabled state.
61
+ */
62
+ get enabled() {
63
+ return this._enabled;
64
+ }
65
+
66
+ /**
67
+ * Set this client's enabled state;
68
+ */
69
+ set enabled(state) {
70
+ state = !!state; // ensure boolean
71
+ if (this._enabled !== state) {
72
+ this._enabled = state;
73
+ if (state) {
74
+ if (!this._initialized) {
75
+ // initialization includes a query request
76
+ this.initialize();
77
+ } else if (this._request) {
78
+ // request query now if requested while disabled
79
+ this.requestQuery(this._request === true ? undefined : this._request);
80
+ }
81
+ this._request = null;
82
+ }
83
+ }
84
+ }
85
+
39
86
  /**
40
87
  * Return a Promise that resolves once the client has updated.
41
88
  */
@@ -44,7 +91,7 @@ export class MosaicClient {
44
91
  }
45
92
 
46
93
  /**
47
- * Return this client's filter selection.
94
+ * @returns {Selection | undefined} this client's filter selection.
48
95
  */
49
96
  get filterBy() {
50
97
  return this._filterBy;
@@ -52,8 +99,8 @@ export class MosaicClient {
52
99
 
53
100
  /**
54
101
  * Return a boolean indicating if the client query can be sped up with
55
- * materialized views of pre-aggregated data. Should return true if changes to
56
- * the filterBy selection does not change the groupby domain of the client
102
+ * materialized views of pre-aggregated data. Should return true if changes
103
+ * to the filterBy selection do not change the groupby domain of the client
57
104
  * query.
58
105
  */
59
106
  get filterStable() {
@@ -61,25 +108,9 @@ export class MosaicClient {
61
108
  }
62
109
 
63
110
  /**
64
- * Return an array of fields queried by this client.
65
- * @returns {import('./types.js').FieldInfoRequest[] | null}
66
- * The fields to retrieve info for.
67
- */
68
- fields() {
69
- return null;
70
- }
71
-
72
- /**
73
- * Called by the coordinator to set the field info for this client.
74
- * @param {import('./types.js').FieldInfo[]} info The field info result.
75
- * @returns {this}
76
- */
77
- fieldInfo(info) { // eslint-disable-line no-unused-vars
78
- return this;
79
- }
80
-
81
- /**
82
- * Prepare the client before the query() method is called.
111
+ * Prepare the client before the `query()` method is called. Subclasses
112
+ * should override this method as needed, potentially issuing one or more
113
+ * queries to gather data or metadata needed prior to `query` calls.
83
114
  */
84
115
  async prepare() {
85
116
  }
@@ -122,34 +153,67 @@ export class MosaicClient {
122
153
 
123
154
  /**
124
155
  * Request the coordinator to execute a query for this client.
125
- * If an explicit query is not provided, the client query method will
126
- * be called, filtered by the current filterBy selection. This method
127
- * has no effect if the client is not registered with a coordinator.
156
+ * If an explicit query is not provided, the client `query` method will
157
+ * be called, filtered by the current `filterBy` selection. This method has
158
+ * no effect if the client is not connected to a coordinator. If the client
159
+ * is connected by currently disabled, the request will be serviced if the
160
+ * client is later enabled.
161
+ * @param {Query} [query] The query to request. If unspecified, the query
162
+ * will be determind by the client's `query` method and the current
163
+ * `filterBy` selection state.
128
164
  * @returns {Promise}
129
165
  */
130
166
  requestQuery(query) {
131
- const q = query || this.query(this.filterBy?.predicate(this));
132
- return this._coordinator?.requestQuery(this, q);
167
+ if (this._enabled) {
168
+ const q = query || this.query(this.filterBy?.predicate(this));
169
+ return this._coordinator?.requestQuery(this, q);
170
+ } else {
171
+ this._request = query ?? true;
172
+ return null;
173
+ }
133
174
  }
134
175
 
135
176
  /**
136
177
  * Request that the coordinator perform a throttled update of this client
137
- * using the default query. Unlike requestQuery, for which every call will
138
- * result in an executed query, multiple calls to requestUpdate may be
139
- * consolidated into a single update.
178
+ * using the default query. Unlike requestQuery, for which every call results
179
+ * in an executed query, multiple calls to requestUpdate may be consolidated
180
+ * into a single update. This method has no effect if the client is not
181
+ * connected to a coordinator. If the client is connected but currently
182
+ * disabled, the request will be serviced if the client is later enabled.
140
183
  */
141
184
  requestUpdate() {
142
- this._requestUpdate();
185
+ if (this._enabled) {
186
+ this._requestUpdate();
187
+ } else {
188
+ this.requestQuery();
189
+ }
143
190
  }
144
191
 
145
192
  /**
146
- * Reset this client, initiating new field info, call the prepare method,
147
- * and query requests. This method has no effect if the client is not
148
- * registered with a coordinator.
149
- * @returns {Promise}
193
+ * Reset this client, calling the prepare method and query requests. This
194
+ * method has no effect if the client is not registered with a coordinator.
150
195
  */
151
196
  initialize() {
152
- return this._coordinator?.initializeClient(this);
197
+ if (!this._enabled) {
198
+ // clear flag so we initialize when enabled again
199
+ this._initialized = false;
200
+ } else if (this._coordinator) {
201
+ // if connected, let's initialize
202
+ this._initialized = true;
203
+ this._pending = this.prepare().then(() => this.requestQuery());
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Remove this client: disconnect from the coordinator and free up any
209
+ * resource use. This method has no effect if the client is not connected
210
+ * to a coordinator.
211
+ *
212
+ * If overriding this method in a client subclass, be sure to also
213
+ * disconnect from the coordinator.
214
+ */
215
+ destroy() {
216
+ this.coordinator?.disconnect(this);
153
217
  }
154
218
 
155
219
  /**
@@ -1,4 +1,5 @@
1
- import { DescribeQuery, isAggregateExpression, isColumnRef, isDescribeQuery, isSelectQuery, Query } from '@uwdata/mosaic-sql';
1
+ /** @import { DescribeQuery, Query } from '@uwdata/mosaic-sql' */
2
+ import { isAggregateExpression, isColumnRef, isDescribeQuery, isSelectQuery } from '@uwdata/mosaic-sql';
2
3
  import { QueryResult } from './util/query-result.js';
3
4
 
4
5
  function wait(callback) {
@@ -1,3 +1,5 @@
1
+ /** @import { Connector } from './connectors/Connector.js' */
2
+ /** @import { Cache, Logger } from './types.js' */
1
3
  import { consolidator } from './QueryConsolidator.js';
2
4
  import { lruCache, voidCache } from './util/cache.js';
3
5
  import { PriorityQueue } from './util/priority-queue.js';
@@ -12,10 +14,15 @@ export class QueryManager {
12
14
  ) {
13
15
  /** @type {PriorityQueue} */
14
16
  this.queue = new PriorityQueue(3);
17
+ /** @type {Connector} */
15
18
  this.db = null;
19
+ /** @type {Cache} */
16
20
  this.clientCache = null;
21
+ /** @type {Logger} */
17
22
  this._logger = voidLogger();
23
+ /** @type {boolean} */
18
24
  this._logQueries = false;
25
+ /** @type {ReturnType<typeof consolidator> | null} */
19
26
  this._consolidate = null;
20
27
  /**
21
28
  * Requests pending with the query manager.
@@ -106,24 +113,48 @@ export class QueryManager {
106
113
  }
107
114
  }
108
115
 
116
+ /**
117
+ * Get or set the current query cache.
118
+ * @param {Cache | boolean} [value]
119
+ * @returns {Cache}
120
+ */
109
121
  cache(value) {
110
122
  return value !== undefined
111
123
  ? (this.clientCache = value === true ? lruCache() : (value || voidCache()))
112
124
  : this.clientCache;
113
125
  }
114
126
 
127
+ /**
128
+ * Get or set the current logger.
129
+ * @param {Logger} [value]
130
+ * @returns {Logger}
131
+ */
115
132
  logger(value) {
116
133
  return value ? (this._logger = value) : this._logger;
117
134
  }
118
135
 
136
+ /**
137
+ * Get or set if queries should be logged.
138
+ * @param {boolean} [value]
139
+ * @returns {boolean}
140
+ */
119
141
  logQueries(value) {
120
142
  return value !== undefined ? this._logQueries = !!value : this._logQueries;
121
143
  }
122
144
 
145
+ /**
146
+ * Get or set the database connector.
147
+ * @param {Connector} [connector]
148
+ * @returns {Connector}
149
+ */
123
150
  connector(connector) {
124
151
  return connector ? (this.db = connector) : this.db;
125
152
  }
126
153
 
154
+ /**
155
+ * Indicate if query consolidation should be performed.
156
+ * @param {boolean} flag
157
+ */
127
158
  consolidate(flag) {
128
159
  if (flag && !this._consolidate) {
129
160
  this._consolidate = consolidator(this.enqueue.bind(this), this.clientCache);
package/src/Selection.js CHANGED
@@ -1,7 +1,9 @@
1
- /** @import {SelectionClause} from './util/selection-types.js' */
1
+ /**
2
+ * @import { MosaicClient } from './MosaicClient.js'
3
+ * @import { SelectionClause } from './util/selection-types.js'
4
+ */
2
5
  import { literal, or } from '@uwdata/mosaic-sql';
3
6
  import { Param } from './Param.js';
4
- import { MosaicClient } from './MosaicClient.js';
5
7
 
6
8
  /**
7
9
  * Test if a value is a Selection instance.
@@ -1,5 +1,8 @@
1
- import { ExprNode, and, contains, isBetween, isIn, isNotDistinct, literal, or, prefix, regexp_matches, suffix } from '@uwdata/mosaic-sql';
2
- import { MosaicClient } from './MosaicClient.js';
1
+ /**
2
+ * @import { ExprNode } from '@uwdata/mosaic-sql'
3
+ * @import { MosaicClient } from './MosaicClient.js'
4
+ */
5
+ import { and, contains, isBetween, isIn, isNotDistinct, literal, or, prefix, regexp_matches, suffix } from '@uwdata/mosaic-sql';
3
6
 
4
7
  /**
5
8
  * @typedef {import('./util/selection-types.js').SelectionClause} SelectionClause
@@ -0,0 +1,30 @@
1
+ import type { Table } from '@uwdata/flechette';
2
+
3
+ export interface QueryRequest {
4
+ /** The query type. */
5
+ type?: string;
6
+ /** A SQL query string. */
7
+ sql: string;
8
+ }
9
+
10
+ export interface ArrowQueryRequest extends QueryRequest {
11
+ /** The query type. */
12
+ type?: 'arrow';
13
+ }
14
+
15
+ export interface ExecQueryRequest extends QueryRequest {
16
+ /** The query type. */
17
+ type: 'exec';
18
+ }
19
+
20
+ export interface JSONQueryRequest extends QueryRequest {
21
+ /** The query type. */
22
+ type: 'json';
23
+ }
24
+
25
+ export interface Connector {
26
+ /** Issue a query and return the result. */
27
+ query(query: ArrowQueryRequest): Promise<Table>;
28
+ query(query: ExecQueryRequest): Promise<void>;
29
+ query(query: JSONQueryRequest): Promise<Record<string, any>[]>;
30
+ }
@@ -1,16 +1,19 @@
1
+ /** @import { ExtractionOptions } from '@uwdata/flechette' */
2
+ /** @import { Connector } from './Connector.js' */
1
3
  import { decodeIPC } from '../util/decode-ipc.js';
2
4
 
3
- export function restConnector(uri = 'http://localhost:3000/') {
5
+ /**
6
+ * Connect to a DuckDB server over an HTTP REST interface.
7
+ * @param {object} [options] Connector options.
8
+ * @param {string} [options.uri] The URI for the DuckDB REST server.
9
+ * @param {ExtractionOptions} [options.ipc] Arrow IPC extraction options.
10
+ * @returns {Connector} A connector instance.
11
+ */
12
+ export function restConnector({
13
+ uri = 'http://localhost:3000/',
14
+ ipc = undefined,
15
+ } = {}) {
4
16
  return {
5
- /**
6
- * Query the DuckDB server.
7
- * @param {object} query
8
- * @param {'exec' | 'arrow' | 'json' | 'create-bundle' | 'load-bundle'} [query.type] The query type.
9
- * @param {string} [query.sql] A SQL query string.
10
- * @param {string[]} [query.queries] The queries used to create a bundle.
11
- * @param {string} [query.name] The name of a bundle to create or load.
12
- * @returns the query result
13
- */
14
17
  async query(query) {
15
18
  const req = fetch(uri, {
16
19
  method: 'POST',
@@ -21,7 +24,6 @@ export function restConnector(uri = 'http://localhost:3000/') {
21
24
  body: JSON.stringify(query)
22
25
  });
23
26
 
24
-
25
27
  const res = await req;
26
28
 
27
29
  if (!res.ok) {
@@ -29,7 +31,7 @@ export function restConnector(uri = 'http://localhost:3000/') {
29
31
  }
30
32
 
31
33
  return query.type === 'exec' ? req
32
- : query.type === 'arrow' ? decodeIPC(await res.arrayBuffer())
34
+ : query.type === 'arrow' ? decodeIPC(await res.arrayBuffer(), ipc)
33
35
  : res.json();
34
36
  }
35
37
  };
@@ -1,100 +1,135 @@
1
+ /** @import { ExtractionOptions, Table } from '@uwdata/flechette' */
2
+ /** @import { ArrowQueryRequest, Connector, ExecQueryRequest, JSONQueryRequest } from './Connector.js' */
1
3
  import { decodeIPC } from '../util/decode-ipc.js';
2
4
 
3
- export function socketConnector(uri = 'ws://localhost:3000/') {
4
- const queue = [];
5
- let connected = false;
6
- let request = null;
7
- let ws;
5
+ /**
6
+ * Connect to a DuckDB server over a WebSocket interface.
7
+ * @param {object} [options] Connector options.
8
+ * @param {string} [options.uri] The URI for the DuckDB REST server.
9
+ * @param {ExtractionOptions} [options.ipc] Arrow IPC extraction options.
10
+ * @returns {SocketConnector} A connector instance.
11
+ */
12
+ export function socketConnector(options) {
13
+ return new SocketConnector(options);
14
+ }
8
15
 
9
- const events = {
10
- open() {
11
- connected = true;
12
- next();
13
- },
16
+ /**
17
+ * DuckDB socket connector.
18
+ * @implements {Connector}
19
+ */
20
+ export class SocketConnector {
21
+ /**
22
+ * @param {object} [options] Connector options.
23
+ * @param {string} [options.uri] The URI for the DuckDB REST server.
24
+ * @param {ExtractionOptions} [options.ipc] Arrow IPC extraction options.
25
+ */
26
+ constructor({
27
+ uri = 'ws://localhost:3000/',
28
+ ipc = undefined,
29
+ } = {}) {
30
+ this._uri = uri;
31
+ this._queue = [];
32
+ this._connected = false;
33
+ this._request = null;
34
+ this._ws = null;
14
35
 
15
- close() {
16
- connected = false;
17
- request = null;
18
- ws = null;
19
- while (queue.length) {
20
- queue.shift().reject('Socket closed');
21
- }
22
- },
36
+ const c = this;
37
+ this._events = {
38
+ open() {
39
+ c._connected = true;
40
+ c.next();
41
+ },
23
42
 
24
- error(event) {
25
- if (request) {
26
- const { reject } = request;
27
- request = null;
28
- next();
29
- reject(event);
30
- } else {
31
- console.error('WebSocket error: ', event);
32
- }
33
- },
43
+ close() {
44
+ c._connected = false;
45
+ c._request = null;
46
+ c._ws = null;
47
+ while (c._queue.length) {
48
+ c._queue.shift().reject('Socket closed');
49
+ }
50
+ },
34
51
 
35
- message({ data }) {
36
- if (request) {
37
- const { query, resolve, reject } = request;
52
+ error(event) {
53
+ if (c._request) {
54
+ const { reject } = c._request;
55
+ c._request = null;
56
+ c.next();
57
+ reject(event);
58
+ } else {
59
+ console.error('WebSocket error: ', event);
60
+ }
61
+ },
38
62
 
39
- // clear state, start next request
40
- request = null;
41
- next();
63
+ message({ data }) {
64
+ if (c._request) {
65
+ const { query, resolve, reject } = c._request;
42
66
 
43
- // process result
44
- if (typeof data === 'string') {
45
- const json = JSON.parse(data);
46
- json.error ? reject(json.error) : resolve(json);
47
- } else if (query.type === 'exec') {
48
- resolve();
49
- } else if (query.type === 'arrow') {
50
- resolve(decodeIPC(data));
67
+ // clear state, start next request
68
+ c._request = null;
69
+ c.next();
70
+
71
+ // process result
72
+ if (typeof data === 'string') {
73
+ const json = JSON.parse(data);
74
+ json.error ? reject(json.error) : resolve(json);
75
+ } else if (query.type === 'exec') {
76
+ resolve();
77
+ } else if (query.type === 'arrow') {
78
+ resolve(decodeIPC(data, ipc));
79
+ } else {
80
+ throw new Error(`Unexpected socket data: ${data}`);
81
+ }
51
82
  } else {
52
- throw new Error(`Unexpected socket data: ${data}`);
83
+ console.log('WebSocket message: ', data);
53
84
  }
54
- } else {
55
- console.log('WebSocket message: ', data);
56
85
  }
57
86
  }
58
87
  }
59
88
 
60
- function init() {
61
- ws = new WebSocket(uri);
62
- ws.binaryType = 'arraybuffer';
63
- for (const type in events) {
64
- ws.addEventListener(type, events[type]);
89
+ get connected() {
90
+ return this._connected;
91
+ }
92
+
93
+ init() {
94
+ this._ws = new WebSocket(this._uri);
95
+ this._ws.binaryType = 'arraybuffer';
96
+ for (const type in this._events) {
97
+ this._ws.addEventListener(type, this._events[type]);
65
98
  }
66
99
  }
67
100
 
68
- function enqueue(query, resolve, reject) {
69
- if (ws == null) init();
70
- queue.push({ query, resolve, reject });
71
- if (connected && !request) next();
101
+ enqueue(query, resolve, reject) {
102
+ if (this._ws == null) this.init();
103
+ this._queue.push({ query, resolve, reject });
104
+ if (this._connected && !this._request) this.next();
72
105
  }
73
106
 
74
- function next() {
75
- if (queue.length) {
76
- request = queue.shift();
77
- ws.send(JSON.stringify(request.query));
107
+ next() {
108
+ if (this._queue.length) {
109
+ this._request = this._queue.shift();
110
+ this._ws.send(JSON.stringify(this._request.query));
78
111
  }
79
112
  }
80
113
 
81
- return {
82
- get connected() {
83
- return connected;
84
- },
85
- /**
86
- * Query the DuckDB server.
87
- * @param {object} query
88
- * @param {'exec' | 'arrow' | 'json' | 'create-bundle' | 'load-bundle'} [query.type] The query type.
89
- * @param {string} [query.sql] A SQL query string.
90
- * @param {string[]} [query.queries] The queries used to create a bundle.
91
- * @param {string} [query.name] The name of a bundle to create or load.
92
- * @returns the query result
93
- */
94
- query(query) {
95
- return new Promise(
96
- (resolve, reject) => enqueue(query, resolve, reject)
97
- );
98
- }
99
- };
114
+ /**
115
+ * @overload
116
+ * @param {ArrowQueryRequest} query
117
+ * @returns {Promise<Table>}
118
+ *
119
+ * @overload
120
+ * @param {ExecQueryRequest} query
121
+ * @returns {Promise<void>}
122
+ *
123
+ * @overload
124
+ * @param {JSONQueryRequest} query
125
+ * @returns {Promise<Record<string, any>[]>}
126
+ *
127
+ * @param {ArrowQueryRequest | ExecQueryRequest | JSONQueryRequest} query
128
+ * @returns {Promise<Table | void | Record<string, any>[]>}}
129
+ */
130
+ query(query) {
131
+ return new Promise(
132
+ (resolve, reject) => this.enqueue(query, resolve, reject)
133
+ );
134
+ }
100
135
  }