@meshagent/meshagent 0.38.4 → 0.39.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.
- package/CHANGELOG.md +14 -2
- package/dist/browser/datasets-client.d.ts +415 -0
- package/dist/{node/database-client.js → browser/datasets-client.js} +480 -234
- package/dist/browser/entrypoint.js +9 -17
- package/dist/browser/index.d.ts +1 -2
- package/dist/browser/index.js +1 -2
- package/dist/browser/meshagent-client.d.ts +179 -3
- package/dist/browser/meshagent-client.js +333 -25
- package/dist/browser/participant-token.d.ts +5 -5
- package/dist/browser/participant-token.js +14 -13
- package/dist/browser/room-client.d.ts +5 -3
- package/dist/browser/room-client.js +70 -4
- package/dist/esm/datasets-client.d.ts +415 -0
- package/dist/esm/{database-client.js → datasets-client.js} +473 -227
- package/dist/esm/entrypoint.js +6 -12
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.js +1 -2
- package/dist/esm/meshagent-client.d.ts +179 -3
- package/dist/esm/meshagent-client.js +333 -25
- package/dist/esm/participant-token.d.ts +5 -5
- package/dist/esm/participant-token.js +12 -11
- package/dist/esm/room-client.d.ts +5 -3
- package/dist/esm/room-client.js +70 -4
- package/dist/node/datasets-client.d.ts +415 -0
- package/dist/{browser/database-client.js → node/datasets-client.js} +480 -234
- package/dist/node/entrypoint.js +6 -12
- package/dist/node/index.d.ts +1 -2
- package/dist/node/index.js +1 -2
- package/dist/node/meshagent-client.d.ts +179 -3
- package/dist/node/meshagent-client.js +333 -25
- package/dist/node/participant-token.d.ts +5 -5
- package/dist/node/participant-token.js +14 -13
- package/dist/node/room-client.d.ts +5 -3
- package/dist/node/room-client.js +70 -4
- package/package.json +3 -2
- package/dist/browser/data-types.d.ts +0 -67
- package/dist/browser/data-types.js +0 -192
- package/dist/browser/database-client.d.ts +0 -281
- package/dist/esm/data-types.d.ts +0 -67
- package/dist/esm/data-types.js +0 -179
- package/dist/esm/database-client.d.ts +0 -281
- package/dist/node/data-types.d.ts +0 -67
- package/dist/node/data-types.js +0 -192
- package/dist/node/database-client.d.ts +0 -281
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Schema, Table, tableFromIPC, tableToIPC } from "apache-arrow";
|
|
2
2
|
import { RoomServerException } from "./room-server-client";
|
|
3
|
-
import { ControlContent, ErrorContent, JsonContent } from "./response";
|
|
4
|
-
|
|
3
|
+
import { BinaryContent, ControlContent, EmptyContent, ErrorContent, JsonContent } from "./response";
|
|
4
|
+
const ARROW_IPC_STREAM_MIME_TYPE = "application/vnd.apache.arrow.stream";
|
|
5
|
+
export class DatasetValueEncoder {
|
|
5
6
|
}
|
|
6
|
-
export class
|
|
7
|
+
export class DatasetExpression extends DatasetValueEncoder {
|
|
7
8
|
constructor(expression) {
|
|
8
9
|
super();
|
|
9
10
|
const normalized = expression.trim();
|
|
10
11
|
if (normalized === "") {
|
|
11
|
-
throw new TypeError("
|
|
12
|
+
throw new TypeError("dataset expression must not be empty");
|
|
12
13
|
}
|
|
13
14
|
this.expression = normalized;
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
+
encodeDatasetValue() {
|
|
16
17
|
return {
|
|
17
18
|
expression: this.expression,
|
|
18
19
|
};
|
|
@@ -21,17 +22,17 @@ export class DatabaseExpression extends DatabaseValueEncoder {
|
|
|
21
22
|
return this.expression;
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
|
-
export class
|
|
25
|
+
export class DatasetDate extends DatasetValueEncoder {
|
|
25
26
|
constructor(value) {
|
|
26
27
|
super();
|
|
27
28
|
const normalized = value.trim();
|
|
28
29
|
const parsed = new Date(`${normalized}T00:00:00Z`);
|
|
29
30
|
if (!ISO_DATE_REGEX.test(normalized) || Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== normalized) {
|
|
30
|
-
throw new TypeError("invalid
|
|
31
|
+
throw new TypeError("invalid dataset date format");
|
|
31
32
|
}
|
|
32
33
|
this.value = normalized;
|
|
33
34
|
}
|
|
34
|
-
|
|
35
|
+
encodeDatasetValue() {
|
|
35
36
|
return {
|
|
36
37
|
date: this.value,
|
|
37
38
|
};
|
|
@@ -40,12 +41,12 @@ export class DatabaseDate extends DatabaseValueEncoder {
|
|
|
40
41
|
return this.value;
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
export class
|
|
44
|
+
export class DatasetStruct extends DatasetValueEncoder {
|
|
44
45
|
constructor(fields) {
|
|
45
46
|
super();
|
|
46
47
|
this.fields = Object.fromEntries(Object.entries(fields).map(([key, value]) => {
|
|
47
48
|
if (typeof key !== "string") {
|
|
48
|
-
throw new TypeError("
|
|
49
|
+
throw new TypeError("dataset struct keys must be strings");
|
|
49
50
|
}
|
|
50
51
|
return [key, value];
|
|
51
52
|
}));
|
|
@@ -53,21 +54,21 @@ export class DatabaseStruct extends DatabaseValueEncoder {
|
|
|
53
54
|
toJson() {
|
|
54
55
|
return Object.fromEntries(Object.entries(this.fields).map(([key, value]) => [key, encodeRecordValue(value)]));
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
+
encodeDatasetValue() {
|
|
57
58
|
return {
|
|
58
59
|
struct: this.toJson(),
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
|
-
export class
|
|
63
|
+
export class DatasetJson extends DatasetValueEncoder {
|
|
63
64
|
constructor(value) {
|
|
64
65
|
super();
|
|
65
|
-
this.value =
|
|
66
|
+
this.value = normalizeDatasetJsonValue(value);
|
|
66
67
|
}
|
|
67
68
|
toJson() {
|
|
68
69
|
return this.value;
|
|
69
70
|
}
|
|
70
|
-
|
|
71
|
+
encodeDatasetValue() {
|
|
71
72
|
return {
|
|
72
73
|
json: this.value,
|
|
73
74
|
};
|
|
@@ -90,7 +91,7 @@ function formatUuidHex(value) {
|
|
|
90
91
|
`${value.substring(16, 20)}-` +
|
|
91
92
|
`${value.substring(20)}`);
|
|
92
93
|
}
|
|
93
|
-
export class
|
|
94
|
+
export class DatasetUuid {
|
|
94
95
|
constructor(value) {
|
|
95
96
|
this.value = formatUuidHex(normalizeUuidHex(value));
|
|
96
97
|
}
|
|
@@ -132,7 +133,7 @@ function isPlainRecord(value) {
|
|
|
132
133
|
return isRecord(value) && (Object.getPrototypeOf(value) === Object.prototype
|
|
133
134
|
|| Object.getPrototypeOf(value) === null);
|
|
134
135
|
}
|
|
135
|
-
function
|
|
136
|
+
function normalizeDatasetJsonValue(value) {
|
|
136
137
|
if (value === null ||
|
|
137
138
|
typeof value === "boolean" ||
|
|
138
139
|
typeof value === "number" ||
|
|
@@ -140,12 +141,12 @@ function normalizeDatabaseJsonValue(value) {
|
|
|
140
141
|
return value;
|
|
141
142
|
}
|
|
142
143
|
if (Array.isArray(value)) {
|
|
143
|
-
return value.map((item) =>
|
|
144
|
+
return value.map((item) => normalizeDatasetJsonValue(item));
|
|
144
145
|
}
|
|
145
146
|
if (isPlainRecord(value)) {
|
|
146
|
-
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key,
|
|
147
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, normalizeDatasetJsonValue(item)]));
|
|
147
148
|
}
|
|
148
|
-
throw new TypeError("
|
|
149
|
+
throw new TypeError("dataset json values must be valid JSON");
|
|
149
150
|
}
|
|
150
151
|
function metadataEntries(metadata) {
|
|
151
152
|
if (metadata == null) {
|
|
@@ -156,98 +157,11 @@ function metadataEntries(metadata) {
|
|
|
156
157
|
value: typeof value === "string" ? value : JSON.stringify(value),
|
|
157
158
|
}));
|
|
158
159
|
}
|
|
159
|
-
function toolkitDataTypeJson(dataType) {
|
|
160
|
-
const json = dataType.toJson();
|
|
161
|
-
const payload = {
|
|
162
|
-
type: json.type,
|
|
163
|
-
nullable: json.nullable ?? null,
|
|
164
|
-
metadata: metadataEntries(json.metadata),
|
|
165
|
-
};
|
|
166
|
-
if (json.type === "vector" || json.type === "list") {
|
|
167
|
-
payload.element_type = toolkitDataTypeJson(DataType.fromJson(json.element_type));
|
|
168
|
-
}
|
|
169
|
-
else if (json.type === "struct") {
|
|
170
|
-
const fields = json.fields;
|
|
171
|
-
if (!Array.isArray(fields)) {
|
|
172
|
-
throw new RoomServerException("unexpected return type from database.inspect");
|
|
173
|
-
}
|
|
174
|
-
payload.fields = fields.map((field) => {
|
|
175
|
-
if (!isRecord(field) || typeof field.name !== "string" || !isRecord(field.data_type)) {
|
|
176
|
-
throw new RoomServerException("unexpected return type from database.inspect");
|
|
177
|
-
}
|
|
178
|
-
return {
|
|
179
|
-
name: field.name,
|
|
180
|
-
data_type: toolkitDataTypeJson(DataType.fromJson(field.data_type)),
|
|
181
|
-
};
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
if (json.type === "vector") {
|
|
185
|
-
payload.size = json.size;
|
|
186
|
-
}
|
|
187
|
-
return payload;
|
|
188
|
-
}
|
|
189
|
-
function schemaEntries(schema) {
|
|
190
|
-
if (schema == null) {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
return Object.entries(schema).map(([name, dataType]) => ({
|
|
194
|
-
name,
|
|
195
|
-
data_type: toolkitDataTypeJson(dataType),
|
|
196
|
-
}));
|
|
197
|
-
}
|
|
198
|
-
function publicDataTypeJson(value) {
|
|
199
|
-
if (!isRecord(value)) {
|
|
200
|
-
throw new RoomServerException("unexpected return type from database.inspect");
|
|
201
|
-
}
|
|
202
|
-
const type = value.type;
|
|
203
|
-
if (typeof type !== "string") {
|
|
204
|
-
throw new RoomServerException("unexpected return type from database.inspect");
|
|
205
|
-
}
|
|
206
|
-
const metadataList = value.metadata;
|
|
207
|
-
let metadata;
|
|
208
|
-
if (metadataList != null) {
|
|
209
|
-
if (!Array.isArray(metadataList)) {
|
|
210
|
-
throw new RoomServerException("unexpected return type from database.inspect");
|
|
211
|
-
}
|
|
212
|
-
metadata = {};
|
|
213
|
-
for (const entry of metadataList) {
|
|
214
|
-
if (!isRecord(entry) || typeof entry.key !== "string" || typeof entry.value !== "string") {
|
|
215
|
-
throw new RoomServerException("unexpected return type from database.inspect");
|
|
216
|
-
}
|
|
217
|
-
metadata[entry.key] = entry.value;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
const payload = {
|
|
221
|
-
type,
|
|
222
|
-
nullable: value.nullable,
|
|
223
|
-
metadata,
|
|
224
|
-
};
|
|
225
|
-
if (type === "vector") {
|
|
226
|
-
payload.size = value.size;
|
|
227
|
-
payload.element_type = publicDataTypeJson(value.element_type);
|
|
228
|
-
}
|
|
229
|
-
else if (type === "list") {
|
|
230
|
-
payload.element_type = publicDataTypeJson(value.element_type);
|
|
231
|
-
}
|
|
232
|
-
else if (type === "struct") {
|
|
233
|
-
const rawFields = value.fields;
|
|
234
|
-
if (!Array.isArray(rawFields)) {
|
|
235
|
-
throw new RoomServerException("unexpected return type from database.inspect");
|
|
236
|
-
}
|
|
237
|
-
payload.fields = Object.fromEntries(rawFields.map((field) => {
|
|
238
|
-
if (!isRecord(field) || typeof field.name !== "string") {
|
|
239
|
-
throw new RoomServerException("unexpected return type from database.inspect");
|
|
240
|
-
}
|
|
241
|
-
return [field.name, publicDataTypeJson(field.data_type)];
|
|
242
|
-
}));
|
|
243
|
-
}
|
|
244
|
-
return payload;
|
|
245
|
-
}
|
|
246
160
|
function encodeRecordValue(value) {
|
|
247
|
-
if (value instanceof
|
|
248
|
-
return value.
|
|
161
|
+
if (value instanceof DatasetValueEncoder) {
|
|
162
|
+
return value.encodeDatasetValue();
|
|
249
163
|
}
|
|
250
|
-
if (value instanceof
|
|
164
|
+
if (value instanceof DatasetUuid) {
|
|
251
165
|
return {
|
|
252
166
|
uuid: value.toString(),
|
|
253
167
|
};
|
|
@@ -268,90 +182,90 @@ function encodeRecordValue(value) {
|
|
|
268
182
|
};
|
|
269
183
|
}
|
|
270
184
|
if (isRecord(value)) {
|
|
271
|
-
throw new RoomServerException("
|
|
185
|
+
throw new RoomServerException("dataset object values must use DatasetStruct or DatasetJson");
|
|
272
186
|
}
|
|
273
187
|
return value;
|
|
274
188
|
}
|
|
275
|
-
function
|
|
276
|
-
if (value instanceof
|
|
189
|
+
function datasetSqlLiteral(value) {
|
|
190
|
+
if (value instanceof DatasetUuid) {
|
|
277
191
|
return `X'${normalizeUuidHex(value.toString())}'`;
|
|
278
192
|
}
|
|
279
|
-
if (value instanceof
|
|
193
|
+
if (value instanceof DatasetDate) {
|
|
280
194
|
return JSON.stringify(value.toString());
|
|
281
195
|
}
|
|
282
196
|
if (value instanceof Date) {
|
|
283
197
|
return JSON.stringify(value.toISOString().replace("+00:00", "Z"));
|
|
284
198
|
}
|
|
285
|
-
if (value instanceof
|
|
199
|
+
if (value instanceof DatasetJson) {
|
|
286
200
|
return JSON.stringify(JSON.stringify(value.toJson()));
|
|
287
201
|
}
|
|
288
|
-
if (value instanceof
|
|
289
|
-
const fields = Object.entries(value.fields).map(([key, fieldValue]) => (`${JSON.stringify(key)}, ${
|
|
202
|
+
if (value instanceof DatasetStruct) {
|
|
203
|
+
const fields = Object.entries(value.fields).map(([key, fieldValue]) => (`${JSON.stringify(key)}, ${datasetSqlLiteral(fieldValue)}`));
|
|
290
204
|
return `named_struct(${fields.join(", ")})`;
|
|
291
205
|
}
|
|
292
206
|
return JSON.stringify(encodeRecordValue(value));
|
|
293
207
|
}
|
|
294
208
|
function decodeRecordValue(value) {
|
|
295
209
|
if (Array.isArray(value)) {
|
|
296
|
-
throw new RoomServerException("
|
|
210
|
+
throw new RoomServerException("dataset list values must use a {'list': [...]} wrapper");
|
|
297
211
|
}
|
|
298
212
|
if (!isRecord(value)) {
|
|
299
213
|
return value;
|
|
300
214
|
}
|
|
301
215
|
const entries = Object.entries(value);
|
|
302
216
|
if (entries.length !== 1) {
|
|
303
|
-
throw new RoomServerException("
|
|
217
|
+
throw new RoomServerException("dataset object values must use a single-key type wrapper");
|
|
304
218
|
}
|
|
305
219
|
const [wrapper, payload] = entries[0];
|
|
306
220
|
switch (wrapper) {
|
|
307
221
|
case "binary":
|
|
308
222
|
if (typeof payload !== "string") {
|
|
309
|
-
throw new RoomServerException("
|
|
223
|
+
throw new RoomServerException("dataset binary values must be base64 strings");
|
|
310
224
|
}
|
|
311
225
|
return base64ToBytes(payload);
|
|
312
226
|
case "uuid":
|
|
313
227
|
if (typeof payload !== "string") {
|
|
314
|
-
throw new RoomServerException("
|
|
228
|
+
throw new RoomServerException("dataset uuid values must be strings");
|
|
315
229
|
}
|
|
316
|
-
return new
|
|
230
|
+
return new DatasetUuid(payload);
|
|
317
231
|
case "expression":
|
|
318
232
|
if (typeof payload !== "string") {
|
|
319
|
-
throw new RoomServerException("
|
|
233
|
+
throw new RoomServerException("dataset expression values must be strings");
|
|
320
234
|
}
|
|
321
|
-
return new
|
|
235
|
+
return new DatasetExpression(payload);
|
|
322
236
|
case "date":
|
|
323
237
|
if (typeof payload !== "string") {
|
|
324
|
-
throw new RoomServerException("
|
|
238
|
+
throw new RoomServerException("dataset date values must be strings");
|
|
325
239
|
}
|
|
326
|
-
return new
|
|
240
|
+
return new DatasetDate(payload);
|
|
327
241
|
case "timestamp":
|
|
328
242
|
if (typeof payload !== "string") {
|
|
329
|
-
throw new RoomServerException("
|
|
243
|
+
throw new RoomServerException("dataset timestamp values must be strings");
|
|
330
244
|
}
|
|
331
245
|
{
|
|
332
246
|
const parsed = new Date(payload);
|
|
333
247
|
if (Number.isNaN(parsed.getTime())) {
|
|
334
|
-
throw new RoomServerException("
|
|
248
|
+
throw new RoomServerException("dataset timestamp value is not valid");
|
|
335
249
|
}
|
|
336
250
|
return parsed;
|
|
337
251
|
}
|
|
338
252
|
case "list":
|
|
339
253
|
if (!Array.isArray(payload)) {
|
|
340
|
-
throw new RoomServerException("
|
|
254
|
+
throw new RoomServerException("dataset list values must be arrays");
|
|
341
255
|
}
|
|
342
256
|
return payload.map((item) => decodeRecordValue(item));
|
|
343
257
|
case "struct":
|
|
344
258
|
if (!isRecord(payload)) {
|
|
345
|
-
throw new RoomServerException("
|
|
259
|
+
throw new RoomServerException("dataset struct values must be objects");
|
|
346
260
|
}
|
|
347
|
-
return new
|
|
261
|
+
return new DatasetStruct(Object.fromEntries(Object.entries(payload).map(([key, item]) => [key, decodeRecordValue(item)])));
|
|
348
262
|
case "json":
|
|
349
|
-
return new
|
|
263
|
+
return new DatasetJson(normalizeDatasetJsonValue(payload));
|
|
350
264
|
default:
|
|
351
|
-
throw new RoomServerException(`unsupported
|
|
265
|
+
throw new RoomServerException(`unsupported dataset value wrapper '${wrapper}'`);
|
|
352
266
|
}
|
|
353
267
|
}
|
|
354
|
-
function
|
|
268
|
+
function encodeDatasetRecord(record) {
|
|
355
269
|
return Object.fromEntries(Object.entries(record).map(([key, value]) => [key, encodeRecordValue(value)]));
|
|
356
270
|
}
|
|
357
271
|
function rowsChunk(records) {
|
|
@@ -367,21 +281,21 @@ function rowsChunk(records) {
|
|
|
367
281
|
}
|
|
368
282
|
function recordsFromRowsChunk(payload, operation) {
|
|
369
283
|
if (!isRecord(payload) || payload.kind !== "rows" || !Array.isArray(payload.rows)) {
|
|
370
|
-
throw new RoomServerException(`unexpected return type from
|
|
284
|
+
throw new RoomServerException(`unexpected return type from datasets.${operation}`);
|
|
371
285
|
}
|
|
372
286
|
return payload.rows.map((row) => {
|
|
373
287
|
if (!isRecord(row) || !Array.isArray(row.columns)) {
|
|
374
|
-
throw new RoomServerException(`unexpected return type from
|
|
288
|
+
throw new RoomServerException(`unexpected return type from datasets.${operation}`);
|
|
375
289
|
}
|
|
376
290
|
return Object.fromEntries(row.columns.map((column) => {
|
|
377
291
|
if (!isRecord(column) || typeof column.name !== "string") {
|
|
378
|
-
throw new RoomServerException(`unexpected return type from
|
|
292
|
+
throw new RoomServerException(`unexpected return type from datasets.${operation}`);
|
|
379
293
|
}
|
|
380
294
|
try {
|
|
381
295
|
return [column.name, decodeRecordValue(column.value)];
|
|
382
296
|
}
|
|
383
297
|
catch {
|
|
384
|
-
throw new RoomServerException(`unexpected return type from
|
|
298
|
+
throw new RoomServerException(`unexpected return type from datasets.${operation}`);
|
|
385
299
|
}
|
|
386
300
|
}));
|
|
387
301
|
});
|
|
@@ -410,7 +324,7 @@ async function* toAsyncIterable(chunks) {
|
|
|
410
324
|
function buildWhereClause(where) {
|
|
411
325
|
if (where != null && typeof where === "object" && !Array.isArray(where)) {
|
|
412
326
|
return Object.entries(where)
|
|
413
|
-
.map(([key, value]) => `${key} = ${
|
|
327
|
+
.map(([key, value]) => `${key} = ${datasetSqlLiteral(value)}`)
|
|
414
328
|
.join(" AND ");
|
|
415
329
|
}
|
|
416
330
|
if (typeof where === "string") {
|
|
@@ -423,36 +337,64 @@ function normalizeTableRefs(tables) {
|
|
|
423
337
|
}
|
|
424
338
|
function tableIndexFromJson(value) {
|
|
425
339
|
if (!isRecord(value) || typeof value.name !== "string" || typeof value.type !== "string" || !Array.isArray(value.columns)) {
|
|
426
|
-
throw new RoomServerException("unexpected return type from
|
|
340
|
+
throw new RoomServerException("unexpected return type from datasets.list_indexes");
|
|
427
341
|
}
|
|
428
342
|
return {
|
|
429
343
|
name: value.name,
|
|
430
344
|
type: value.type,
|
|
431
345
|
columns: value.columns.map((column) => {
|
|
432
346
|
if (typeof column !== "string") {
|
|
433
|
-
throw new RoomServerException("unexpected return type from
|
|
347
|
+
throw new RoomServerException("unexpected return type from datasets.list_indexes");
|
|
434
348
|
}
|
|
435
349
|
return column;
|
|
436
350
|
}),
|
|
351
|
+
fields: Array.isArray(value.fields) ? value.fields.map((field) => Number(field)) : [],
|
|
352
|
+
typeUrl: typeof value.type_url === "string" ? value.type_url : null,
|
|
353
|
+
numRowsIndexed: typeof value.num_rows_indexed === "number" ? value.num_rows_indexed : null,
|
|
354
|
+
numSegments: typeof value.num_segments === "number" ? value.num_segments : null,
|
|
355
|
+
totalSizeBytes: typeof value.total_size_bytes === "number" ? value.total_size_bytes : null,
|
|
356
|
+
details: typeof value.details_json === "string" ? JSON.parse(value.details_json) : isRecord(value.details) ? value.details : {},
|
|
357
|
+
statistics: typeof value.statistics_json === "string" ? JSON.parse(value.statistics_json) : isRecord(value.statistics) ? value.statistics : {},
|
|
437
358
|
};
|
|
438
359
|
}
|
|
360
|
+
function optimizeResultFromJson(value) {
|
|
361
|
+
if (!isRecord(value)) {
|
|
362
|
+
throw new RoomServerException("unexpected return type from datasets.optimize");
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
compaction: typeof value.compaction_json === "string" ? JSON.parse(value.compaction_json) : isRecord(value.compaction) ? value.compaction : null,
|
|
366
|
+
optimizedIndices: value.optimized_indices === true,
|
|
367
|
+
cleanup: typeof value.cleanup_json === "string" ? JSON.parse(value.cleanup_json) : isRecord(value.cleanup) ? value.cleanup : null,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function tableStatsFromJson(value) {
|
|
371
|
+
if (!isRecord(value)) {
|
|
372
|
+
throw new RoomServerException("unexpected return type from datasets.stats");
|
|
373
|
+
}
|
|
374
|
+
const dataset = typeof value.dataset_json === "string" ? JSON.parse(value.dataset_json) : value.dataset;
|
|
375
|
+
const data = typeof value.data_json === "string" ? JSON.parse(value.data_json) : value.data;
|
|
376
|
+
if (!isRecord(dataset) || !isRecord(data)) {
|
|
377
|
+
throw new RoomServerException("unexpected return type from datasets.stats");
|
|
378
|
+
}
|
|
379
|
+
return { dataset, data };
|
|
380
|
+
}
|
|
439
381
|
function tableVersionFromJson(value) {
|
|
440
382
|
if (!isRecord(value) || typeof value.metadata_json !== "string" || typeof value.timestamp !== "string" || typeof value.version !== "number") {
|
|
441
|
-
throw new RoomServerException("unexpected return type from
|
|
383
|
+
throw new RoomServerException("unexpected return type from datasets.list_versions");
|
|
442
384
|
}
|
|
443
385
|
const timestamp = new Date(value.timestamp);
|
|
444
386
|
if (Number.isNaN(timestamp.getTime())) {
|
|
445
|
-
throw new RoomServerException("unexpected return type from
|
|
387
|
+
throw new RoomServerException("unexpected return type from datasets.list_versions");
|
|
446
388
|
}
|
|
447
389
|
let metadata;
|
|
448
390
|
try {
|
|
449
391
|
metadata = JSON.parse(value.metadata_json);
|
|
450
392
|
}
|
|
451
393
|
catch (_) {
|
|
452
|
-
throw new RoomServerException("unexpected return type from
|
|
394
|
+
throw new RoomServerException("unexpected return type from datasets.list_versions");
|
|
453
395
|
}
|
|
454
396
|
if (!isRecord(metadata)) {
|
|
455
|
-
throw new RoomServerException("unexpected return type from
|
|
397
|
+
throw new RoomServerException("unexpected return type from datasets.list_versions");
|
|
456
398
|
}
|
|
457
399
|
return {
|
|
458
400
|
version: value.version,
|
|
@@ -462,27 +404,27 @@ function tableVersionFromJson(value) {
|
|
|
462
404
|
}
|
|
463
405
|
function tableBranchFromJson(value) {
|
|
464
406
|
if (!isRecord(value) || typeof value.name !== "string") {
|
|
465
|
-
throw new RoomServerException("unexpected return type from
|
|
407
|
+
throw new RoomServerException("unexpected return type from datasets.list_branches");
|
|
466
408
|
}
|
|
467
409
|
if (value.parent_branch != null && typeof value.parent_branch !== "string") {
|
|
468
|
-
throw new RoomServerException("unexpected return type from
|
|
410
|
+
throw new RoomServerException("unexpected return type from datasets.list_branches");
|
|
469
411
|
}
|
|
470
412
|
if (value.parent_version != null
|
|
471
413
|
&& (typeof value.parent_version !== "number" || !Number.isInteger(value.parent_version))) {
|
|
472
|
-
throw new RoomServerException("unexpected return type from
|
|
414
|
+
throw new RoomServerException("unexpected return type from datasets.list_branches");
|
|
473
415
|
}
|
|
474
416
|
if (value.manifest_size != null
|
|
475
417
|
&& (typeof value.manifest_size !== "number" || !Number.isInteger(value.manifest_size))) {
|
|
476
|
-
throw new RoomServerException("unexpected return type from
|
|
418
|
+
throw new RoomServerException("unexpected return type from datasets.list_branches");
|
|
477
419
|
}
|
|
478
420
|
let createdAt = null;
|
|
479
421
|
if (value.created_at != null) {
|
|
480
422
|
if (typeof value.created_at !== "string") {
|
|
481
|
-
throw new RoomServerException("unexpected return type from
|
|
423
|
+
throw new RoomServerException("unexpected return type from datasets.list_branches");
|
|
482
424
|
}
|
|
483
425
|
createdAt = new Date(value.created_at);
|
|
484
426
|
if (Number.isNaN(createdAt.getTime())) {
|
|
485
|
-
throw new RoomServerException("unexpected return type from
|
|
427
|
+
throw new RoomServerException("unexpected return type from datasets.list_branches");
|
|
486
428
|
}
|
|
487
429
|
}
|
|
488
430
|
return {
|
|
@@ -493,7 +435,7 @@ function tableBranchFromJson(value) {
|
|
|
493
435
|
manifestSize: value.manifest_size ?? null,
|
|
494
436
|
};
|
|
495
437
|
}
|
|
496
|
-
class
|
|
438
|
+
class DatasetWriteInputStream {
|
|
497
439
|
constructor(start, chunks) {
|
|
498
440
|
this.start = start;
|
|
499
441
|
this.pulls = [];
|
|
@@ -553,7 +495,98 @@ class DatabaseWriteInputStream {
|
|
|
553
495
|
}
|
|
554
496
|
}
|
|
555
497
|
}
|
|
556
|
-
|
|
498
|
+
async function* toAsyncArrowIterable(chunks) {
|
|
499
|
+
if (Symbol.asyncIterator in chunks) {
|
|
500
|
+
for await (const chunk of chunks) {
|
|
501
|
+
yield chunk;
|
|
502
|
+
}
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
for (const chunk of chunks) {
|
|
506
|
+
yield chunk;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function schemaToIPC(schema) {
|
|
510
|
+
return tableToIPC(new Table(schema, []), "stream");
|
|
511
|
+
}
|
|
512
|
+
function tableFromIPCBytes(data) {
|
|
513
|
+
const table = tableFromIPC(data);
|
|
514
|
+
if (table instanceof Promise) {
|
|
515
|
+
throw new RoomServerException("unexpected async Arrow IPC result");
|
|
516
|
+
}
|
|
517
|
+
return table;
|
|
518
|
+
}
|
|
519
|
+
function schemaFromIPCBytes(data) {
|
|
520
|
+
return tableFromIPCBytes(data).schema;
|
|
521
|
+
}
|
|
522
|
+
class DatasetArrowWriteInputStream {
|
|
523
|
+
constructor(start, chunks, schema) {
|
|
524
|
+
this.start = start;
|
|
525
|
+
this.schema = schema;
|
|
526
|
+
this.pulls = [];
|
|
527
|
+
this.closed = false;
|
|
528
|
+
this.source = toAsyncArrowIterable(chunks)[Symbol.asyncIterator]();
|
|
529
|
+
}
|
|
530
|
+
requestNext() {
|
|
531
|
+
if (this.closed) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const waiter = this.pulls.shift();
|
|
535
|
+
if (waiter) {
|
|
536
|
+
waiter();
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
this.pulls.push(() => undefined);
|
|
540
|
+
}
|
|
541
|
+
close() {
|
|
542
|
+
if (this.closed) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
this.closed = true;
|
|
546
|
+
while (this.pulls.length > 0) {
|
|
547
|
+
const waiter = this.pulls.shift();
|
|
548
|
+
waiter?.();
|
|
549
|
+
}
|
|
550
|
+
void this.source.return?.();
|
|
551
|
+
}
|
|
552
|
+
async waitForPull() {
|
|
553
|
+
if (this.closed) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const waiter = this.pulls.shift();
|
|
557
|
+
if (waiter) {
|
|
558
|
+
waiter();
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
await new Promise((resolve) => {
|
|
562
|
+
this.pulls.push(resolve);
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
async *stream() {
|
|
566
|
+
yield new BinaryContent({
|
|
567
|
+
data: this.schema == null ? new Uint8Array() : schemaToIPC(this.schema),
|
|
568
|
+
headers: this.start,
|
|
569
|
+
});
|
|
570
|
+
while (!this.closed) {
|
|
571
|
+
await this.waitForPull();
|
|
572
|
+
if (this.closed) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
const nextChunk = await this.source.next();
|
|
576
|
+
if (nextChunk.done) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (nextChunk.value.numRows === 0) {
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
yield new BinaryContent({
|
|
583
|
+
data: tableToIPC(nextChunk.value, "stream"),
|
|
584
|
+
headers: { kind: "data", content_type: ARROW_IPC_STREAM_MIME_TYPE },
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
class DatasetReadInputStream {
|
|
557
590
|
constructor(start) {
|
|
558
591
|
this.start = start;
|
|
559
592
|
this.pulls = [];
|
|
@@ -604,15 +637,66 @@ class DatabaseReadInputStream {
|
|
|
604
637
|
}
|
|
605
638
|
}
|
|
606
639
|
}
|
|
607
|
-
|
|
640
|
+
class DatasetArrowReadInputStream {
|
|
641
|
+
constructor(start) {
|
|
642
|
+
this.start = start;
|
|
643
|
+
this.pulls = [];
|
|
644
|
+
this.closed = false;
|
|
645
|
+
}
|
|
646
|
+
requestNext() {
|
|
647
|
+
if (this.closed) {
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const waiter = this.pulls.shift();
|
|
651
|
+
if (waiter) {
|
|
652
|
+
waiter();
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
this.pulls.push(() => undefined);
|
|
656
|
+
}
|
|
657
|
+
close() {
|
|
658
|
+
if (this.closed) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
this.closed = true;
|
|
662
|
+
while (this.pulls.length > 0) {
|
|
663
|
+
const waiter = this.pulls.shift();
|
|
664
|
+
waiter?.();
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
async waitForPull() {
|
|
668
|
+
if (this.closed) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
const waiter = this.pulls.shift();
|
|
672
|
+
if (waiter) {
|
|
673
|
+
waiter();
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
await new Promise((resolve) => {
|
|
677
|
+
this.pulls.push(resolve);
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
async *stream() {
|
|
681
|
+
yield new BinaryContent({ data: new Uint8Array(), headers: this.start });
|
|
682
|
+
while (!this.closed) {
|
|
683
|
+
await this.waitForPull();
|
|
684
|
+
if (this.closed) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
yield new BinaryContent({ data: new Uint8Array(), headers: { kind: "pull" } });
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
export class DatasetsClient {
|
|
608
692
|
constructor({ room }) {
|
|
609
693
|
this.room = room;
|
|
610
694
|
}
|
|
611
695
|
_unexpectedResponseError(operation) {
|
|
612
|
-
return new RoomServerException(`unexpected return type from
|
|
696
|
+
return new RoomServerException(`unexpected return type from datasets.${operation}`);
|
|
613
697
|
}
|
|
614
698
|
async invoke(operation, input) {
|
|
615
|
-
const response = await this.room.invoke({ toolkit: "
|
|
699
|
+
const response = await this.room.invoke({ toolkit: "dataset", tool: operation, input });
|
|
616
700
|
if (response instanceof JsonContent) {
|
|
617
701
|
return response;
|
|
618
702
|
}
|
|
@@ -621,8 +705,11 @@ export class DatabaseClient {
|
|
|
621
705
|
}
|
|
622
706
|
return null;
|
|
623
707
|
}
|
|
708
|
+
async invokeContent(operation, input) {
|
|
709
|
+
return await this.room.invoke({ toolkit: "dataset", tool: operation, input });
|
|
710
|
+
}
|
|
624
711
|
async invokeStream(operation, input) {
|
|
625
|
-
return await this.room.invokeStream({ toolkit: "
|
|
712
|
+
return await this.room.invokeStream({ toolkit: "dataset", tool: operation, input });
|
|
626
713
|
}
|
|
627
714
|
async drainWriteStream(operation, input) {
|
|
628
715
|
const response = await this.invokeStream(operation, input.stream());
|
|
@@ -637,6 +724,13 @@ export class DatabaseClient {
|
|
|
637
724
|
}
|
|
638
725
|
throw this._unexpectedResponseError(operation);
|
|
639
726
|
}
|
|
727
|
+
if (chunk instanceof BinaryContent) {
|
|
728
|
+
if (chunk.headers.kind !== "pull") {
|
|
729
|
+
throw this._unexpectedResponseError(operation);
|
|
730
|
+
}
|
|
731
|
+
input.requestNext();
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
640
734
|
if (!(chunk instanceof JsonContent) || chunk.json.kind !== "pull") {
|
|
641
735
|
throw this._unexpectedResponseError(operation);
|
|
642
736
|
}
|
|
@@ -648,7 +742,7 @@ export class DatabaseClient {
|
|
|
648
742
|
}
|
|
649
743
|
}
|
|
650
744
|
async *streamRows(operation, start) {
|
|
651
|
-
const input = new
|
|
745
|
+
const input = new DatasetReadInputStream(start);
|
|
652
746
|
const response = await this.invokeStream(operation, input.stream());
|
|
653
747
|
input.requestNext();
|
|
654
748
|
try {
|
|
@@ -673,6 +767,32 @@ export class DatabaseClient {
|
|
|
673
767
|
input.close();
|
|
674
768
|
}
|
|
675
769
|
}
|
|
770
|
+
async *streamArrow(operation, start) {
|
|
771
|
+
const input = new DatasetArrowReadInputStream(start);
|
|
772
|
+
const response = await this.invokeStream(operation, input.stream());
|
|
773
|
+
input.requestNext();
|
|
774
|
+
try {
|
|
775
|
+
for await (const chunk of response) {
|
|
776
|
+
if (chunk instanceof ErrorContent) {
|
|
777
|
+
throw new RoomServerException(chunk.text, chunk.code);
|
|
778
|
+
}
|
|
779
|
+
if (chunk instanceof ControlContent) {
|
|
780
|
+
if (chunk.method === "close") {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
throw this._unexpectedResponseError(operation);
|
|
784
|
+
}
|
|
785
|
+
if (!(chunk instanceof BinaryContent) || chunk.headers.kind !== "data") {
|
|
786
|
+
throw this._unexpectedResponseError(operation);
|
|
787
|
+
}
|
|
788
|
+
yield tableFromIPCBytes(chunk.data);
|
|
789
|
+
input.requestNext();
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
finally {
|
|
793
|
+
input.close();
|
|
794
|
+
}
|
|
795
|
+
}
|
|
676
796
|
async listTables({ namespace, branch } = {}) {
|
|
677
797
|
const response = await this.invoke("list_tables", {
|
|
678
798
|
namespace: namespace ?? null,
|
|
@@ -684,22 +804,21 @@ export class DatabaseClient {
|
|
|
684
804
|
return Array.isArray(response.json.tables) ? response.json.tables : [];
|
|
685
805
|
}
|
|
686
806
|
async createTable({ name, data, schema, mode = "create", namespace, branch, metadata, }) {
|
|
687
|
-
const input = new
|
|
807
|
+
const input = new DatasetArrowWriteInputStream({
|
|
688
808
|
kind: "start",
|
|
689
809
|
name,
|
|
690
|
-
fields: schemaEntries(schema),
|
|
691
810
|
mode,
|
|
692
811
|
namespace: namespace ?? null,
|
|
693
812
|
branch: branch ?? null,
|
|
694
813
|
metadata: metadataEntries(metadata),
|
|
695
|
-
}, data ?? []);
|
|
814
|
+
}, data ?? [], schema);
|
|
696
815
|
await this.drainWriteStream("create_table", input);
|
|
697
816
|
}
|
|
698
817
|
async createTableWithSchema({ name, schema, data, mode = "create", namespace, branch, metadata }) {
|
|
699
818
|
return this.createTable({
|
|
700
819
|
name,
|
|
701
820
|
schema,
|
|
702
|
-
data: data == null ? undefined :
|
|
821
|
+
data: data == null ? undefined : data instanceof Table ? [data] : data,
|
|
703
822
|
mode,
|
|
704
823
|
namespace,
|
|
705
824
|
branch,
|
|
@@ -709,7 +828,7 @@ export class DatabaseClient {
|
|
|
709
828
|
async createTableFromData({ name, data, mode = "create", namespace, branch, metadata }) {
|
|
710
829
|
return this.createTable({
|
|
711
830
|
name,
|
|
712
|
-
data: data == null ? undefined :
|
|
831
|
+
data: data == null ? undefined : data instanceof Table ? [data] : data,
|
|
713
832
|
mode,
|
|
714
833
|
namespace,
|
|
715
834
|
branch,
|
|
@@ -719,9 +838,20 @@ export class DatabaseClient {
|
|
|
719
838
|
async createTableFromDataStream({ name, chunks, schema, mode = "create", namespace, branch, metadata }) {
|
|
720
839
|
return this.createTable({ name, data: chunks, schema, mode, namespace, branch, metadata });
|
|
721
840
|
}
|
|
841
|
+
async createTableFromJsonData({ name, data, mode = "create", namespace, branch, metadata }) {
|
|
842
|
+
const input = new DatasetWriteInputStream({
|
|
843
|
+
kind: "start",
|
|
844
|
+
name,
|
|
845
|
+
mode,
|
|
846
|
+
namespace: namespace ?? null,
|
|
847
|
+
branch: branch ?? null,
|
|
848
|
+
metadata: metadataEntries(metadata),
|
|
849
|
+
}, data == null ? [] : rowChunkList(data));
|
|
850
|
+
await this.drainWriteStream("create_table", input);
|
|
851
|
+
}
|
|
722
852
|
async dropTable({ name, ignoreMissing = false, namespace, branch }) {
|
|
723
853
|
await this.room.invoke({
|
|
724
|
-
toolkit: "
|
|
854
|
+
toolkit: "dataset",
|
|
725
855
|
tool: "drop_table",
|
|
726
856
|
input: {
|
|
727
857
|
name,
|
|
@@ -733,20 +863,30 @@ export class DatabaseClient {
|
|
|
733
863
|
}
|
|
734
864
|
async dropIndex({ table, name, namespace, branch }) {
|
|
735
865
|
await this.room.invoke({
|
|
736
|
-
toolkit: "
|
|
866
|
+
toolkit: "dataset",
|
|
737
867
|
tool: "drop_index",
|
|
738
868
|
input: { table, name, namespace: namespace ?? null, branch: branch ?? null },
|
|
739
869
|
});
|
|
740
870
|
}
|
|
741
871
|
async addColumns({ table, newColumns, namespace, branch }) {
|
|
872
|
+
if (newColumns instanceof Schema) {
|
|
873
|
+
await this.invokeContent("add_columns", new BinaryContent({
|
|
874
|
+
data: schemaToIPC(newColumns),
|
|
875
|
+
headers: {
|
|
876
|
+
table,
|
|
877
|
+
namespace: namespace ?? null,
|
|
878
|
+
branch: branch ?? null,
|
|
879
|
+
content_type: ARROW_IPC_STREAM_MIME_TYPE,
|
|
880
|
+
},
|
|
881
|
+
}));
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
742
884
|
await this.room.invoke({
|
|
743
|
-
toolkit: "
|
|
885
|
+
toolkit: "dataset",
|
|
744
886
|
tool: "add_columns",
|
|
745
887
|
input: {
|
|
746
888
|
table,
|
|
747
|
-
columns: Object.entries(newColumns).map(([name, value]) => (value
|
|
748
|
-
? { name, value_sql: null, data_type: toolkitDataTypeJson(value) }
|
|
749
|
-
: { name, value_sql: value, data_type: null })),
|
|
889
|
+
columns: Object.entries(newColumns).map(([name, value]) => ({ name, value_sql: value })),
|
|
750
890
|
namespace: namespace ?? null,
|
|
751
891
|
branch: branch ?? null,
|
|
752
892
|
},
|
|
@@ -754,16 +894,16 @@ export class DatabaseClient {
|
|
|
754
894
|
}
|
|
755
895
|
async dropColumns({ table, columns, namespace, branch }) {
|
|
756
896
|
await this.room.invoke({
|
|
757
|
-
toolkit: "
|
|
897
|
+
toolkit: "dataset",
|
|
758
898
|
tool: "drop_columns",
|
|
759
899
|
input: { table, columns, namespace: namespace ?? null, branch: branch ?? null },
|
|
760
900
|
});
|
|
761
901
|
}
|
|
762
902
|
async insert({ table, records, namespace, branch }) {
|
|
763
|
-
await this.insertStream({ table, chunks:
|
|
903
|
+
await this.insertStream({ table, chunks: [records], namespace, branch });
|
|
764
904
|
}
|
|
765
905
|
async insertStream({ table, chunks, namespace, branch }) {
|
|
766
|
-
const input = new
|
|
906
|
+
const input = new DatasetArrowWriteInputStream({
|
|
767
907
|
kind: "start",
|
|
768
908
|
table,
|
|
769
909
|
namespace: namespace ?? null,
|
|
@@ -773,7 +913,7 @@ export class DatabaseClient {
|
|
|
773
913
|
}
|
|
774
914
|
async update({ table, where, values, namespace, branch }) {
|
|
775
915
|
await this.room.invoke({
|
|
776
|
-
toolkit: "
|
|
916
|
+
toolkit: "dataset",
|
|
777
917
|
tool: "update",
|
|
778
918
|
input: {
|
|
779
919
|
table,
|
|
@@ -786,16 +926,16 @@ export class DatabaseClient {
|
|
|
786
926
|
}
|
|
787
927
|
async delete({ table, where, namespace, branch }) {
|
|
788
928
|
await this.room.invoke({
|
|
789
|
-
toolkit: "
|
|
929
|
+
toolkit: "dataset",
|
|
790
930
|
tool: "delete",
|
|
791
931
|
input: { table, where, namespace: namespace ?? null, branch: branch ?? null },
|
|
792
932
|
});
|
|
793
933
|
}
|
|
794
934
|
async merge({ table, on, records, namespace, branch }) {
|
|
795
|
-
await this.mergeStream({ table, on, chunks:
|
|
935
|
+
await this.mergeStream({ table, on, chunks: [records], namespace, branch });
|
|
796
936
|
}
|
|
797
937
|
async mergeStream({ table, on, chunks, namespace, branch }) {
|
|
798
|
-
const input = new
|
|
938
|
+
const input = new DatasetArrowWriteInputStream({
|
|
799
939
|
kind: "start",
|
|
800
940
|
table,
|
|
801
941
|
on,
|
|
@@ -804,23 +944,125 @@ export class DatabaseClient {
|
|
|
804
944
|
}, chunks);
|
|
805
945
|
await this.drainWriteStream("merge", input);
|
|
806
946
|
}
|
|
807
|
-
async sql({ query, tables, params }) {
|
|
808
|
-
const
|
|
809
|
-
for await (const chunk of this.sqlStream({ query, tables, params })) {
|
|
810
|
-
|
|
947
|
+
async sql({ query, tables, params, namespace, branch }) {
|
|
948
|
+
const results = [];
|
|
949
|
+
for await (const chunk of this.sqlStream({ query, tables, params, namespace, branch })) {
|
|
950
|
+
results.push(chunk);
|
|
951
|
+
}
|
|
952
|
+
return results;
|
|
953
|
+
}
|
|
954
|
+
async openSqlQuery({ query, tables, params, namespace, branch }) {
|
|
955
|
+
const response = await this.invokeContent("open_sql_query", new BinaryContent({
|
|
956
|
+
data: params == null ? new Uint8Array() : tableToIPC(params, "stream"),
|
|
957
|
+
headers: {
|
|
958
|
+
query,
|
|
959
|
+
tables: normalizeTableRefs(tables ?? []),
|
|
960
|
+
namespace: namespace ?? null,
|
|
961
|
+
branch: branch ?? null,
|
|
962
|
+
},
|
|
963
|
+
}));
|
|
964
|
+
if (!(response instanceof BinaryContent)) {
|
|
965
|
+
throw this._unexpectedResponseError("open_sql_query");
|
|
966
|
+
}
|
|
967
|
+
const queryId = response.headers.query_id;
|
|
968
|
+
if (typeof queryId !== "string" || queryId === "") {
|
|
969
|
+
throw this._unexpectedResponseError("open_sql_query");
|
|
970
|
+
}
|
|
971
|
+
return {
|
|
972
|
+
schema: schemaFromIPCBytes(response.data),
|
|
973
|
+
kind: "query",
|
|
974
|
+
queryId,
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
async executeSql({ query, tables, params, namespace, branch }) {
|
|
978
|
+
const response = await this.invokeContent("execute_sql", new BinaryContent({
|
|
979
|
+
data: params == null ? new Uint8Array() : tableToIPC(params, "stream"),
|
|
980
|
+
headers: {
|
|
981
|
+
query,
|
|
982
|
+
tables: normalizeTableRefs(tables ?? []),
|
|
983
|
+
namespace: namespace ?? null,
|
|
984
|
+
branch: branch ?? null,
|
|
985
|
+
},
|
|
986
|
+
}));
|
|
987
|
+
if (response instanceof BinaryContent) {
|
|
988
|
+
if (response.headers.kind !== "query") {
|
|
989
|
+
throw this._unexpectedResponseError("execute_sql");
|
|
990
|
+
}
|
|
991
|
+
const queryId = response.headers.query_id;
|
|
992
|
+
if (typeof queryId !== "string" || queryId === "") {
|
|
993
|
+
throw this._unexpectedResponseError("execute_sql");
|
|
994
|
+
}
|
|
995
|
+
return {
|
|
996
|
+
kind: "query",
|
|
997
|
+
schema: schemaFromIPCBytes(response.data),
|
|
998
|
+
queryId,
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
if (response instanceof JsonContent) {
|
|
1002
|
+
if (response.json.kind !== "statement"
|
|
1003
|
+
|| typeof response.json.rows_affected !== "number"
|
|
1004
|
+
|| !Number.isInteger(response.json.rows_affected)) {
|
|
1005
|
+
throw this._unexpectedResponseError("execute_sql");
|
|
1006
|
+
}
|
|
1007
|
+
return {
|
|
1008
|
+
kind: "statement",
|
|
1009
|
+
rowsAffected: response.json.rows_affected,
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
throw this._unexpectedResponseError("execute_sql");
|
|
1013
|
+
}
|
|
1014
|
+
async *sqlStream({ query, tables, params, namespace, branch }) {
|
|
1015
|
+
const result = await this.executeSql({ query, tables, params, namespace, branch });
|
|
1016
|
+
if (result.kind === "statement") {
|
|
1017
|
+
throw new RoomServerException(`SQL statement did not return rows; rows_affected=${result.rowsAffected}`);
|
|
1018
|
+
}
|
|
1019
|
+
const opened = result;
|
|
1020
|
+
try {
|
|
1021
|
+
yield* this.readSqlQuery({ queryId: opened.queryId });
|
|
1022
|
+
}
|
|
1023
|
+
finally {
|
|
1024
|
+
await this.closeSqlQuery({ queryId: opened.queryId });
|
|
811
1025
|
}
|
|
812
|
-
return rows;
|
|
813
1026
|
}
|
|
814
|
-
async *
|
|
815
|
-
yield* this.
|
|
1027
|
+
async *readSqlQuery({ queryId }) {
|
|
1028
|
+
yield* this.streamArrow("read_sql_query", {
|
|
816
1029
|
kind: "start",
|
|
817
|
-
|
|
818
|
-
tables: normalizeTableRefs(tables),
|
|
819
|
-
params_json: params == null ? null : JSON.stringify(encodeDatabaseRecord(params)),
|
|
1030
|
+
query_id: queryId,
|
|
820
1031
|
});
|
|
821
1032
|
}
|
|
1033
|
+
async closeSqlQuery({ queryId }) {
|
|
1034
|
+
const response = await this.room.invoke({ toolkit: "dataset", tool: "close_sql_query", input: { query_id: queryId } });
|
|
1035
|
+
if (!(response instanceof EmptyContent)) {
|
|
1036
|
+
throw this._unexpectedResponseError("close_sql_query");
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
async cancelSqlQuery({ queryId }) {
|
|
1040
|
+
const response = await this.room.invoke({ toolkit: "dataset", tool: "cancel_sql_query", input: { query_id: queryId } });
|
|
1041
|
+
if (!(response instanceof JsonContent)
|
|
1042
|
+
|| !["cancelled", "cancelling", "not_cancellable"].includes(response.json.status)) {
|
|
1043
|
+
throw this._unexpectedResponseError("cancel_sql_query");
|
|
1044
|
+
}
|
|
1045
|
+
return { status: response.json.status };
|
|
1046
|
+
}
|
|
1047
|
+
async executeSqlStatement({ query, tables, params, namespace, branch }) {
|
|
1048
|
+
const response = await this.invokeContent("execute_sql_statement", new BinaryContent({
|
|
1049
|
+
data: params == null ? new Uint8Array() : tableToIPC(params, "stream"),
|
|
1050
|
+
headers: {
|
|
1051
|
+
query,
|
|
1052
|
+
tables: normalizeTableRefs(tables ?? []),
|
|
1053
|
+
namespace: namespace ?? null,
|
|
1054
|
+
branch: branch ?? null,
|
|
1055
|
+
},
|
|
1056
|
+
}));
|
|
1057
|
+
if (!(response instanceof JsonContent)
|
|
1058
|
+
|| typeof response.json.rows_affected !== "number"
|
|
1059
|
+
|| !Number.isInteger(response.json.rows_affected)) {
|
|
1060
|
+
throw this._unexpectedResponseError("execute_sql_statement");
|
|
1061
|
+
}
|
|
1062
|
+
return response.json.rows_affected;
|
|
1063
|
+
}
|
|
822
1064
|
async search({ table, text, vector, where, offset, limit, select, namespace, branch, version }) {
|
|
823
|
-
const
|
|
1065
|
+
const results = [];
|
|
824
1066
|
for await (const chunk of this.searchStream({
|
|
825
1067
|
table,
|
|
826
1068
|
text,
|
|
@@ -833,12 +1075,12 @@ export class DatabaseClient {
|
|
|
833
1075
|
branch,
|
|
834
1076
|
version,
|
|
835
1077
|
})) {
|
|
836
|
-
|
|
1078
|
+
results.push(chunk);
|
|
837
1079
|
}
|
|
838
|
-
return
|
|
1080
|
+
return results;
|
|
839
1081
|
}
|
|
840
1082
|
async *searchStream({ table, text, vector, where, offset, limit, select, namespace, branch, version }) {
|
|
841
|
-
yield* this.
|
|
1083
|
+
yield* this.streamArrow("search", {
|
|
842
1084
|
kind: "start",
|
|
843
1085
|
table,
|
|
844
1086
|
text: text ?? null,
|
|
@@ -870,35 +1112,53 @@ export class DatabaseClient {
|
|
|
870
1112
|
return response.json.count;
|
|
871
1113
|
}
|
|
872
1114
|
async inspect({ table, namespace, branch, version }) {
|
|
873
|
-
const response = await this.invoke(
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1115
|
+
const response = await this.room.invoke({
|
|
1116
|
+
toolkit: "dataset",
|
|
1117
|
+
tool: "inspect",
|
|
1118
|
+
input: {
|
|
1119
|
+
table,
|
|
1120
|
+
namespace: namespace ?? null,
|
|
1121
|
+
branch: branch ?? null,
|
|
1122
|
+
version: version ?? null,
|
|
1123
|
+
},
|
|
878
1124
|
});
|
|
879
|
-
if (!(response instanceof
|
|
1125
|
+
if (!(response instanceof BinaryContent)) {
|
|
880
1126
|
throw this._unexpectedResponseError("inspect");
|
|
881
1127
|
}
|
|
882
|
-
return
|
|
883
|
-
if (!isRecord(field) || typeof field.name !== "string") {
|
|
884
|
-
throw this._unexpectedResponseError("inspect");
|
|
885
|
-
}
|
|
886
|
-
return [field.name, DataType.fromJson(publicDataTypeJson(field.data_type))];
|
|
887
|
-
}));
|
|
1128
|
+
return tableFromIPCBytes(response.data).schema;
|
|
888
1129
|
}
|
|
889
1130
|
async optimize(tableOrParams) {
|
|
890
1131
|
const table = typeof tableOrParams === "string" ? tableOrParams : tableOrParams.table;
|
|
891
1132
|
const namespace = typeof tableOrParams === "string" ? undefined : tableOrParams.namespace;
|
|
892
1133
|
const branch = typeof tableOrParams === "string" ? undefined : tableOrParams.branch;
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1134
|
+
const config = typeof tableOrParams === "string" ? undefined : tableOrParams.config;
|
|
1135
|
+
const response = await this.invoke("optimize", {
|
|
1136
|
+
table,
|
|
1137
|
+
namespace: namespace ?? null,
|
|
1138
|
+
branch: branch ?? null,
|
|
1139
|
+
config: config ?? null,
|
|
897
1140
|
});
|
|
1141
|
+
if (!(response instanceof JsonContent)) {
|
|
1142
|
+
throw this._unexpectedResponseError("optimize");
|
|
1143
|
+
}
|
|
1144
|
+
return optimizeResultFromJson(response.json);
|
|
1145
|
+
}
|
|
1146
|
+
async stats({ table, namespace, branch, version, maxRowsPerGroup }) {
|
|
1147
|
+
const response = await this.invoke("stats", {
|
|
1148
|
+
table,
|
|
1149
|
+
namespace: namespace ?? null,
|
|
1150
|
+
branch: branch ?? null,
|
|
1151
|
+
version: version ?? null,
|
|
1152
|
+
max_rows_per_group: maxRowsPerGroup ?? null,
|
|
1153
|
+
});
|
|
1154
|
+
if (!(response instanceof JsonContent)) {
|
|
1155
|
+
throw this._unexpectedResponseError("stats");
|
|
1156
|
+
}
|
|
1157
|
+
return tableStatsFromJson(response.json);
|
|
898
1158
|
}
|
|
899
1159
|
async restore({ table, version, namespace, branch }) {
|
|
900
1160
|
await this.room.invoke({
|
|
901
|
-
toolkit: "
|
|
1161
|
+
toolkit: "dataset",
|
|
902
1162
|
tool: "restore",
|
|
903
1163
|
input: { table, version, namespace: namespace ?? null, branch: branch ?? null },
|
|
904
1164
|
});
|
|
@@ -914,25 +1174,11 @@ export class DatabaseClient {
|
|
|
914
1174
|
}
|
|
915
1175
|
return response.json.versions.map((version) => tableVersionFromJson(version));
|
|
916
1176
|
}
|
|
917
|
-
async
|
|
918
|
-
await this.room.invoke({
|
|
919
|
-
toolkit: "database",
|
|
920
|
-
tool: "create_vector_index",
|
|
921
|
-
input: { table, column, replace, namespace: namespace ?? null, branch: branch ?? null },
|
|
922
|
-
});
|
|
923
|
-
}
|
|
924
|
-
async createScalarIndex({ table, column, replace = false, namespace, branch }) {
|
|
925
|
-
await this.room.invoke({
|
|
926
|
-
toolkit: "database",
|
|
927
|
-
tool: "create_scalar_index",
|
|
928
|
-
input: { table, column, replace, namespace: namespace ?? null, branch: branch ?? null },
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
async createFullTextSearchIndex({ table, column, replace = false, namespace, branch }) {
|
|
1177
|
+
async createIndex({ table, config, namespace, branch }) {
|
|
932
1178
|
await this.room.invoke({
|
|
933
|
-
toolkit: "
|
|
934
|
-
tool: "
|
|
935
|
-
input: { table,
|
|
1179
|
+
toolkit: "dataset",
|
|
1180
|
+
tool: "create_index",
|
|
1181
|
+
input: { table, config, namespace: namespace ?? null, branch: branch ?? null },
|
|
936
1182
|
});
|
|
937
1183
|
}
|
|
938
1184
|
async listIndexes({ table, namespace, branch, version }) {
|
|
@@ -958,7 +1204,7 @@ export class DatabaseClient {
|
|
|
958
1204
|
}
|
|
959
1205
|
async createBranch({ branch, fromBranch, namespace }) {
|
|
960
1206
|
await this.room.invoke({
|
|
961
|
-
toolkit: "
|
|
1207
|
+
toolkit: "dataset",
|
|
962
1208
|
tool: "create_branch",
|
|
963
1209
|
input: {
|
|
964
1210
|
branch,
|
|
@@ -969,7 +1215,7 @@ export class DatabaseClient {
|
|
|
969
1215
|
}
|
|
970
1216
|
async deleteBranch({ branch, namespace }) {
|
|
971
1217
|
await this.room.invoke({
|
|
972
|
-
toolkit: "
|
|
1218
|
+
toolkit: "dataset",
|
|
973
1219
|
tool: "delete_branch",
|
|
974
1220
|
input: { branch, namespace: namespace ?? null },
|
|
975
1221
|
});
|