@minnowdb/core 0.8.0 → 0.9.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/dist/engine/artifact-cache.js +3 -2
- package/dist/engine/buffered-writer.js +12 -2
- package/dist/engine/client.d.ts +8 -2
- package/dist/engine/client.js +106 -22
- package/dist/engine/database.d.ts +5 -3
- package/dist/engine/database.js +2044 -1674
- package/dist/engine/errors.d.ts +19 -0
- package/dist/engine/errors.js +29 -0
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +4 -0
- package/dist/engine/live-accept.js +13 -0
- package/dist/engine/live-aggregate.js +54 -16
- package/dist/engine/live.d.ts +2 -0
- package/dist/engine/live.js +109 -108
- package/dist/engine/query-cache.js +17 -2
- package/dist/engine/query.d.ts +1 -2
- package/dist/engine/query.js +4 -7
- package/dist/engine/result-state.d.ts +7 -0
- package/dist/engine/result-state.js +15 -0
- package/dist/engine/vector.d.ts +4 -0
- package/dist/engine/vector.js +14 -14
- package/dist/engine/windows.js +15 -14
- package/dist/engine/worker-server.d.ts +1 -1
- package/dist/engine/worker-server.js +28 -15
- package/dist/engine/write-coordinator.js +54 -0
- package/dist/storage/indexeddb.js +134 -89
- package/dist/storage/opfs/index.d.ts +1 -1
- package/dist/storage/opfs/index.js +3 -1
- package/dist/storage/opfs/leader.js +20 -9
- package/dist/storage/opfs/rpc.js +3 -1
- package/dist/storage/opfs/store.d.ts +2 -0
- package/dist/storage/opfs/store.js +128 -40
- package/dist/storage/toolkit/record-core.js +2 -1
- package/dist/storage/types.d.ts +14 -0
- package/dist/storage/types.js +21 -0
- package/dist/transactions/index.d.ts +3 -0
- package/dist/transactions/index.js +4 -1
- package/dist/worker-protocol/index.d.ts +1 -1
- package/dist/worker-protocol/index.js +1 -1
- package/package.json +2 -2
- package/dist/date-value.d.ts +0 -20
- package/dist/engine/artifact-cache.d.ts +0 -29
- package/dist/engine/byte-estimates.d.ts +0 -11
- package/dist/engine/cancellation.d.ts +0 -2
- package/dist/engine/defaults.d.ts +0 -29
- package/dist/engine/group-index.d.ts +0 -33
- package/dist/engine/join-index.d.ts +0 -10
- package/dist/engine/live-aggregate.d.ts +0 -12
- package/dist/engine/live-equal.d.ts +0 -7
- package/dist/engine/point-read.d.ts +0 -59
- package/dist/engine/query-cache.d.ts +0 -21
- package/dist/engine/query-generations.d.ts +0 -8
- package/dist/engine/query-identity.d.ts +0 -3
- package/dist/engine/result-wire.d.ts +0 -70
- package/dist/engine/sort-keys.d.ts +0 -73
- package/dist/engine/sql-domains.d.ts +0 -97
- package/dist/engine/sql-functions.d.ts +0 -11
- package/dist/engine/sql-json.d.ts +0 -40
- package/dist/engine/sql-semantics.d.ts +0 -66
- package/dist/engine/worker-store-indexeddb.d.ts +0 -2
- package/dist/engine/worker-store-memory.d.ts +0 -2
- package/dist/engine/worker-store-opfs.d.ts +0 -2
- package/dist/engine/write-block-planner.d.ts +0 -19
- package/dist/storage/opfs/leader.d.ts +0 -460
- package/dist/storage/opfs/rpc.d.ts +0 -82
- package/dist/storage/opfs/snapshot-ledger.d.ts +0 -41
package/dist/engine/live.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createLiveQueryPatch } from "./live-patch.js";
|
|
2
|
-
import {
|
|
2
|
+
import { acceptLiveExecution } from "./live-accept.js";
|
|
3
|
+
import { copyQueryResult, queryResultRetainedBytes, sameQueryRow } from "./query-cache.js";
|
|
3
4
|
import { encodeQueryIdentity } from "./query-identity.js";
|
|
5
|
+
import { OpfsCoordinationError } from "../storage/types.js";
|
|
4
6
|
import { LiveQueryLimitError } from "./errors.js";
|
|
5
7
|
const DEFAULT_LIVE_QUERY_MAX_GROUPS = 256;
|
|
6
8
|
const DEFAULT_LIVE_QUERY_MAX_SUBSCRIPTIONS = 1024;
|
|
@@ -9,12 +11,6 @@ const MAX_LIVE_QUERY_SUBSCRIPTIONS = 16384;
|
|
|
9
11
|
const MAX_LIVE_QUERY_SETS_PER_DATABASE = 256;
|
|
10
12
|
const LIVE_QUERY_EXECUTION_CONCURRENCY = 8;
|
|
11
13
|
import { LiveQueryLimitError as LiveQueryLimitError2 } from "./errors.js";
|
|
12
|
-
function sameQueryValue(left, right) {
|
|
13
|
-
if (left instanceof Date || right instanceof Date) {
|
|
14
|
-
return left instanceof Date && right instanceof Date && Object.is(dateMilliseconds(left), dateMilliseconds(right));
|
|
15
|
-
}
|
|
16
|
-
return Object.is(left, right);
|
|
17
|
-
}
|
|
18
14
|
function sameResult(left, right) {
|
|
19
15
|
if (left.columns.length !== right.columns.length || left.rows.length !== right.rows.length) {
|
|
20
16
|
return false;
|
|
@@ -32,40 +28,11 @@ function sameResult(left, right) {
|
|
|
32
28
|
const rightRow = right.rows[rowIndex];
|
|
33
29
|
if (leftRow === void 0 || rightRow === void 0)
|
|
34
30
|
return false;
|
|
35
|
-
if (leftRow
|
|
36
|
-
|
|
37
|
-
for (const column of columns) {
|
|
38
|
-
if (!sameQueryValue(leftRow[column] ?? null, rightRow[column] ?? null))
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
31
|
+
if (!sameQueryRow(leftRow, rightRow, columns))
|
|
32
|
+
return false;
|
|
41
33
|
}
|
|
42
34
|
return true;
|
|
43
35
|
}
|
|
44
|
-
function cloneRow(row, columns) {
|
|
45
|
-
const cloned = {};
|
|
46
|
-
for (const column of columns) {
|
|
47
|
-
const value = row[column] ?? null;
|
|
48
|
-
const copy = value instanceof Date ? copyDate(value) : value;
|
|
49
|
-
if (column === "__proto__") {
|
|
50
|
-
Object.defineProperty(cloned, column, {
|
|
51
|
-
value: copy,
|
|
52
|
-
enumerable: true,
|
|
53
|
-
configurable: true,
|
|
54
|
-
writable: true
|
|
55
|
-
});
|
|
56
|
-
} else
|
|
57
|
-
cloned[column] = copy;
|
|
58
|
-
}
|
|
59
|
-
return cloned;
|
|
60
|
-
}
|
|
61
|
-
function cloneResult(result) {
|
|
62
|
-
const columns = [...result.columns];
|
|
63
|
-
return {
|
|
64
|
-
columns,
|
|
65
|
-
columnDomains: structuredClone(result.columnDomains),
|
|
66
|
-
rows: result.rows.map((row) => cloneRow(row, columns))
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
36
|
function queryKey(query) {
|
|
70
37
|
return encodeQueryIdentity(typeof query === "string" ? ["sql", query] : query.kind === "sql-query" ? ["sql-query", query.sql, query.params] : ["typed-query", query.plan]);
|
|
71
38
|
}
|
|
@@ -102,9 +69,9 @@ function groupExecutes(group) {
|
|
|
102
69
|
}
|
|
103
70
|
return false;
|
|
104
71
|
}
|
|
105
|
-
function groupMemoizes(group) {
|
|
72
|
+
function groupMemoizes(group, changed = true) {
|
|
106
73
|
for (const subscriber of group.subscribers) {
|
|
107
|
-
if (!subscriber.closed && subscriber.kind === "observer")
|
|
74
|
+
if (!subscriber.closed && subscriber.kind === "observer" && (changed || subscriber.options.suppressUnchanged !== true))
|
|
108
75
|
return true;
|
|
109
76
|
}
|
|
110
77
|
return false;
|
|
@@ -152,9 +119,12 @@ class LiveQuerySet {
|
|
|
152
119
|
#executionWaiters = [];
|
|
153
120
|
#sweepChain = Promise.resolve();
|
|
154
121
|
#sweepQueued = false;
|
|
122
|
+
#retryTimer;
|
|
123
|
+
#retryDelayMs = 100;
|
|
155
124
|
#pollTimer;
|
|
156
125
|
#closed = false;
|
|
157
126
|
#subscriptionCount = 0;
|
|
127
|
+
#retainedBytes = 0;
|
|
158
128
|
#ownsChannel;
|
|
159
129
|
#onClosed;
|
|
160
130
|
constructor(host, options = {}) {
|
|
@@ -191,10 +161,7 @@ class LiveQuerySet {
|
|
|
191
161
|
let retainedRows = 0;
|
|
192
162
|
for (const group of this.#groups.values())
|
|
193
163
|
retainedRows += group.result?.rows.length ?? 0;
|
|
194
|
-
|
|
195
|
-
for (const group of this.#groups.values())
|
|
196
|
-
retainedBytes += group.retainedBytes;
|
|
197
|
-
return { ...this.#stats, retainedRows, retainedBytes };
|
|
164
|
+
return { ...this.#stats, retainedRows, retainedBytes: this.#retainedBytes };
|
|
198
165
|
}
|
|
199
166
|
#freshProbe() {
|
|
200
167
|
let pending = this.#pendingProbe;
|
|
@@ -218,43 +185,14 @@ class LiveQuerySet {
|
|
|
218
185
|
}
|
|
219
186
|
this.#subscriptionCount += 1;
|
|
220
187
|
}
|
|
221
|
-
|
|
222
|
-
this.#
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
this.#throwIfClosed();
|
|
230
|
-
subscriber = {
|
|
231
|
-
kind: "result",
|
|
232
|
-
options,
|
|
233
|
-
delivered: false,
|
|
234
|
-
closed: false,
|
|
235
|
-
seenDelivery: -1
|
|
236
|
-
};
|
|
237
|
-
group.subscribers.add(subscriber);
|
|
238
|
-
await this.#settleGroup(group, opened.fresh, true);
|
|
239
|
-
this.#throwIfClosed();
|
|
240
|
-
if (!subscriber.delivered) {
|
|
241
|
-
const result = group.result ?? (await this.#executeGroup(group)).result;
|
|
242
|
-
this.#throwIfClosed();
|
|
243
|
-
if (!subscriber.closed) {
|
|
244
|
-
this.#deliverResult(group, subscriber, result, { ...group.seenProbe, initial: true });
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
} catch (error) {
|
|
248
|
-
if (group !== void 0 && subscriber !== void 0) {
|
|
249
|
-
this.#removeSubscriber(group, subscriber, false);
|
|
250
|
-
} else {
|
|
251
|
-
this.#subscriptionCount -= 1;
|
|
252
|
-
if (group !== void 0)
|
|
253
|
-
this.#removeEmptyGroup(group);
|
|
254
|
-
}
|
|
255
|
-
throw error;
|
|
256
|
-
}
|
|
257
|
-
return this.#subscriptionHandle(group, subscriber);
|
|
188
|
+
subscribe(query, options) {
|
|
189
|
+
return this.#register(query, {
|
|
190
|
+
kind: "result",
|
|
191
|
+
options,
|
|
192
|
+
delivered: false,
|
|
193
|
+
closed: false,
|
|
194
|
+
seenDelivery: -1
|
|
195
|
+
});
|
|
258
196
|
}
|
|
259
197
|
subscribePatches(query, options) {
|
|
260
198
|
return this.subscribe(query, {
|
|
@@ -264,34 +202,42 @@ class LiveQuerySet {
|
|
|
264
202
|
...options.onComplete === void 0 ? {} : { onComplete: options.onComplete.bind(options) }
|
|
265
203
|
});
|
|
266
204
|
}
|
|
267
|
-
|
|
205
|
+
observe(query, options) {
|
|
206
|
+
return this.#register(query, { kind: "observer", options, delivered: false, closed: false });
|
|
207
|
+
}
|
|
208
|
+
async #register(query, subscriber) {
|
|
268
209
|
this.#throwIfClosed();
|
|
269
210
|
this.#reserveSubscription();
|
|
270
211
|
let group;
|
|
271
|
-
let
|
|
212
|
+
let installed = false;
|
|
272
213
|
try {
|
|
273
214
|
const opened = await this.#getOrOpenGroup(query);
|
|
274
215
|
group = opened.group;
|
|
275
216
|
this.#throwIfClosed();
|
|
276
|
-
subscriber = {
|
|
277
|
-
kind: "observer",
|
|
278
|
-
options,
|
|
279
|
-
delivered: false,
|
|
280
|
-
closed: false
|
|
281
|
-
};
|
|
282
217
|
group.subscribers.add(subscriber);
|
|
283
|
-
|
|
218
|
+
group.pendingSubscriptions -= 1;
|
|
219
|
+
installed = true;
|
|
220
|
+
await this.#settleGroup(group, opened.fresh, subscriber.kind === "result" || subscriber.options.suppressUnchanged === true);
|
|
284
221
|
this.#throwIfClosed();
|
|
285
222
|
if (!subscriber.delivered) {
|
|
286
|
-
|
|
223
|
+
if (subscriber.kind === "observer")
|
|
224
|
+
this.#deliverInvalidation(subscriber, { ...group.seenProbe, initial: true });
|
|
225
|
+
else {
|
|
226
|
+
const result = group.result ?? (await this.#executeGroup(group)).result;
|
|
227
|
+
this.#throwIfClosed();
|
|
228
|
+
if (!subscriber.closed)
|
|
229
|
+
this.#deliverResult(group, subscriber, result, { ...group.seenProbe, initial: true });
|
|
230
|
+
}
|
|
287
231
|
}
|
|
288
232
|
} catch (error) {
|
|
289
|
-
if (group !== void 0 &&
|
|
233
|
+
if (group !== void 0 && installed) {
|
|
290
234
|
this.#removeSubscriber(group, subscriber, false);
|
|
291
235
|
} else {
|
|
292
236
|
this.#subscriptionCount -= 1;
|
|
293
|
-
if (group !== void 0)
|
|
237
|
+
if (group !== void 0) {
|
|
238
|
+
group.pendingSubscriptions -= 1;
|
|
294
239
|
this.#removeEmptyGroup(group);
|
|
240
|
+
}
|
|
295
241
|
}
|
|
296
242
|
throw error;
|
|
297
243
|
}
|
|
@@ -327,30 +273,39 @@ class LiveQuerySet {
|
|
|
327
273
|
subscriber.closed = true;
|
|
328
274
|
this.#subscriptionCount -= 1;
|
|
329
275
|
group.subscribers.delete(subscriber);
|
|
276
|
+
this.#removeEmptyGroup(group);
|
|
330
277
|
if (complete)
|
|
331
278
|
callLiveCallback(() => subscriber.options.onComplete?.());
|
|
332
|
-
this.#removeEmptyGroup(group);
|
|
333
279
|
}
|
|
334
280
|
#removeEmptyGroup(group) {
|
|
335
|
-
if (group.subscribers.size !== 0)
|
|
281
|
+
if (group.subscribers.size !== 0 || group.pendingSubscriptions !== 0)
|
|
336
282
|
return;
|
|
337
283
|
if (this.#groups.get(group.key) !== group)
|
|
338
284
|
return;
|
|
339
285
|
this.#groups.delete(group.key);
|
|
286
|
+
this.#retainedBytes -= group.retainedBytes;
|
|
287
|
+
group.retainedBytes = 0;
|
|
288
|
+
group.result = void 0;
|
|
289
|
+
group.maintenance = void 0;
|
|
340
290
|
this.#lagging.delete(group);
|
|
341
291
|
this.#unindexGroup(group, group.dependencies);
|
|
342
292
|
}
|
|
343
293
|
async #getOrOpenGroup(query) {
|
|
294
|
+
if (typeof query !== "string")
|
|
295
|
+
query = structuredClone(query);
|
|
344
296
|
const key = queryKey(query);
|
|
345
297
|
const existing = this.#groups.get(key);
|
|
346
298
|
if (existing !== void 0) {
|
|
347
299
|
this.#stats.sharedExecutions += 1;
|
|
300
|
+
existing.pendingSubscriptions += 1;
|
|
348
301
|
return { group: existing, fresh: false };
|
|
349
302
|
}
|
|
350
303
|
const opening = this.#opening.get(key);
|
|
351
304
|
if (opening !== void 0) {
|
|
352
305
|
this.#stats.sharedExecutions += 1;
|
|
353
|
-
|
|
306
|
+
const group = await opening;
|
|
307
|
+
group.pendingSubscriptions += 1;
|
|
308
|
+
return { group, fresh: false };
|
|
354
309
|
}
|
|
355
310
|
if (this.#groups.size + this.#opening.size >= this.#maxGroups) {
|
|
356
311
|
throw new LiveQueryLimitError("group", this.#maxGroups);
|
|
@@ -358,7 +313,9 @@ class LiveQuerySet {
|
|
|
358
313
|
const created = this.#openGroup(key, query);
|
|
359
314
|
this.#opening.set(key, created);
|
|
360
315
|
try {
|
|
361
|
-
|
|
316
|
+
const group = await created;
|
|
317
|
+
group.pendingSubscriptions += 1;
|
|
318
|
+
return { group, fresh: true };
|
|
362
319
|
} finally {
|
|
363
320
|
this.#opening.delete(key);
|
|
364
321
|
}
|
|
@@ -370,6 +327,7 @@ class LiveQuerySet {
|
|
|
370
327
|
query,
|
|
371
328
|
dependencies,
|
|
372
329
|
subscribers: /* @__PURE__ */ new Set(),
|
|
330
|
+
pendingSubscriptions: 0,
|
|
373
331
|
seenProbe: after,
|
|
374
332
|
result: void 0,
|
|
375
333
|
retainedBytes: 0,
|
|
@@ -437,12 +395,14 @@ class LiveQuerySet {
|
|
|
437
395
|
let executed;
|
|
438
396
|
let maintenance;
|
|
439
397
|
let retainedBytes;
|
|
398
|
+
let candidate;
|
|
440
399
|
try {
|
|
441
400
|
if (this.#incremental && this.#host.executeMaintainable !== void 0 && !group.unmaintainable) {
|
|
442
401
|
const maintained = await this.#host.executeMaintainable(group.query, context);
|
|
443
402
|
if (maintained === void 0)
|
|
444
403
|
group.unmaintainable = true;
|
|
445
404
|
else {
|
|
405
|
+
candidate = maintained;
|
|
446
406
|
executed = maintained.result;
|
|
447
407
|
maintenance = maintained.state;
|
|
448
408
|
retainedBytes = maintained.retainedBytes;
|
|
@@ -454,7 +414,9 @@ class LiveQuerySet {
|
|
|
454
414
|
}
|
|
455
415
|
const previous = group.result;
|
|
456
416
|
const changed = previous === void 0 || !sameResult(previous, executed);
|
|
457
|
-
this.#retain(group, executed, maintenance, retainedBytes);
|
|
417
|
+
this.#retain(group, executed, maintenance, retainedBytes, candidate);
|
|
418
|
+
if (candidate !== void 0 && context !== void 0 && groupMemoizes(group, changed))
|
|
419
|
+
await this.#memoizeGroup(group, executed, context.probe);
|
|
458
420
|
return { result: executed, changed };
|
|
459
421
|
})();
|
|
460
422
|
group.execution = execution;
|
|
@@ -465,17 +427,27 @@ class LiveQuerySet {
|
|
|
465
427
|
group.execution = void 0;
|
|
466
428
|
}
|
|
467
429
|
}
|
|
468
|
-
#
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
430
|
+
async #memoizeGroup(group, result, probe) {
|
|
431
|
+
if (this.#closed || this.#groups.get(group.key) !== group)
|
|
432
|
+
return;
|
|
433
|
+
try {
|
|
434
|
+
await this.#host.memoize?.(group.query, result, probe);
|
|
435
|
+
} catch {
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
#retain(group, result, state, hint, candidate) {
|
|
439
|
+
if (this.#closed || this.#groups.get(group.key) !== group)
|
|
440
|
+
return;
|
|
441
|
+
const bytes = hint ?? (result === group.result && state === group.maintenance ? group.retainedBytes : queryResultRetainedBytes(result) + result.rows.length * 48);
|
|
442
|
+
const total = this.#retainedBytes - group.retainedBytes + bytes;
|
|
443
|
+
if (!Number.isSafeInteger(bytes) || bytes < 0 || !Number.isSafeInteger(total) || total > this.#maxRetainedBytes)
|
|
475
444
|
throw new LiveQueryLimitError("byte", this.#maxRetainedBytes);
|
|
445
|
+
if (candidate !== void 0)
|
|
446
|
+
acceptLiveExecution(candidate);
|
|
476
447
|
group.result = result;
|
|
477
448
|
group.maintenance = state;
|
|
478
449
|
group.retainedBytes = bytes;
|
|
450
|
+
this.#retainedBytes = total;
|
|
479
451
|
}
|
|
480
452
|
async #acquireExecutionSlot() {
|
|
481
453
|
if (this.#executing < LIVE_QUERY_EXECUTION_CONCURRENCY) {
|
|
@@ -513,7 +485,9 @@ class LiveQuerySet {
|
|
|
513
485
|
verdict.declined = true;
|
|
514
486
|
return { result: retained, changed: false };
|
|
515
487
|
}
|
|
516
|
-
this.#retain(group, maintained.result, maintained.state, maintained.retainedBytes);
|
|
488
|
+
this.#retain(group, maintained.result, maintained.state, maintained.retainedBytes, maintained);
|
|
489
|
+
if (groupMemoizes(group, maintained.changed))
|
|
490
|
+
await this.#memoizeGroup(group, maintained.result, probe);
|
|
517
491
|
return {
|
|
518
492
|
result: maintained.result,
|
|
519
493
|
changed: maintained.changed,
|
|
@@ -534,7 +508,7 @@ class LiveQuerySet {
|
|
|
534
508
|
return;
|
|
535
509
|
subscriber.delivered = true;
|
|
536
510
|
const consecutive = subscriber.seenDelivery === group.deliveries - 1;
|
|
537
|
-
subscriber.options.onChange(this.#sharedResults || subscriber.options.sharedResults === true ? result :
|
|
511
|
+
subscriber.options.onChange(this.#sharedResults || subscriber.options.sharedResults === true ? result : copyQueryResult(result), retained !== void 0 && consecutive && !delivery.initial ? { ...delivery, retained } : delivery);
|
|
538
512
|
subscriber.seenDelivery = group.deliveries;
|
|
539
513
|
}
|
|
540
514
|
#deliverInvalidation(subscriber, invalidation) {
|
|
@@ -560,11 +534,17 @@ class LiveQuerySet {
|
|
|
560
534
|
this.#channel?.close?.();
|
|
561
535
|
if (this.#pollTimer !== void 0)
|
|
562
536
|
clearInterval(this.#pollTimer);
|
|
537
|
+
if (this.#retryTimer !== void 0)
|
|
538
|
+
clearTimeout(this.#retryTimer);
|
|
563
539
|
const groups = [...this.#groups.values()];
|
|
564
540
|
this.#groups.clear();
|
|
541
|
+
this.#retainedBytes = 0;
|
|
565
542
|
this.#groupsByTable.clear();
|
|
566
543
|
this.#lagging.clear();
|
|
567
544
|
for (const group of groups) {
|
|
545
|
+
group.result = void 0;
|
|
546
|
+
group.maintenance = void 0;
|
|
547
|
+
group.retainedBytes = 0;
|
|
568
548
|
const subscribers = [...group.subscribers];
|
|
569
549
|
group.subscribers.clear();
|
|
570
550
|
for (const subscriber of subscribers) {
|
|
@@ -581,19 +561,37 @@ class LiveQuerySet {
|
|
|
581
561
|
if (this.#closed)
|
|
582
562
|
return;
|
|
583
563
|
this.#stats.hints += 1;
|
|
564
|
+
if (this.#retryTimer !== void 0)
|
|
565
|
+
return;
|
|
584
566
|
if (this.#sweepQueued)
|
|
585
567
|
return;
|
|
586
568
|
this.#sweepQueued = true;
|
|
587
569
|
this.#sweepChain = this.#sweepChain.then(async () => {
|
|
588
570
|
this.#sweepQueued = false;
|
|
571
|
+
if (this.#retryTimer !== void 0)
|
|
572
|
+
return;
|
|
589
573
|
try {
|
|
590
574
|
await this.#sweep();
|
|
575
|
+
this.#retryDelayMs = 100;
|
|
591
576
|
} catch (error) {
|
|
577
|
+
if (error instanceof OpfsCoordinationError) {
|
|
578
|
+
this.#retrySweep();
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
592
581
|
for (const group of this.#groups.values())
|
|
593
582
|
this.#notifyGroupError(group, error);
|
|
594
583
|
}
|
|
595
584
|
});
|
|
596
585
|
}
|
|
586
|
+
#retrySweep() {
|
|
587
|
+
if (this.#closed || this.#groups.size === 0 || this.#retryTimer !== void 0)
|
|
588
|
+
return;
|
|
589
|
+
this.#retryTimer = setTimeout(() => {
|
|
590
|
+
this.#retryTimer = void 0;
|
|
591
|
+
this.#hint();
|
|
592
|
+
}, this.#retryDelayMs);
|
|
593
|
+
this.#retryDelayMs = Math.min(this.#retryDelayMs * 2, 5e3);
|
|
594
|
+
}
|
|
597
595
|
#stillOpen() {
|
|
598
596
|
return !this.#closed;
|
|
599
597
|
}
|
|
@@ -671,6 +669,8 @@ class LiveQuerySet {
|
|
|
671
669
|
await this.#refreshDependencies(group);
|
|
672
670
|
} catch (error) {
|
|
673
671
|
this.#lagging.add(group);
|
|
672
|
+
if (error instanceof OpfsCoordinationError)
|
|
673
|
+
throw error;
|
|
674
674
|
this.#notifyGroupError(group, error);
|
|
675
675
|
return;
|
|
676
676
|
}
|
|
@@ -761,6 +761,8 @@ class LiveQuerySet {
|
|
|
761
761
|
this.#settleProbe(group, current);
|
|
762
762
|
} catch (error) {
|
|
763
763
|
this.#lagging.add(group);
|
|
764
|
+
if (error instanceof OpfsCoordinationError)
|
|
765
|
+
throw error;
|
|
764
766
|
this.#notifyGroupError(group, error);
|
|
765
767
|
}
|
|
766
768
|
}
|
|
@@ -800,7 +802,6 @@ class LiveQuerySet {
|
|
|
800
802
|
}
|
|
801
803
|
}
|
|
802
804
|
}
|
|
803
|
-
import { copyDate, dateMilliseconds } from "../date-value.js";
|
|
804
805
|
export {
|
|
805
806
|
DEFAULT_LIVE_QUERY_MAX_GROUPS,
|
|
806
807
|
DEFAULT_LIVE_QUERY_MAX_SUBSCRIPTIONS,
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { encodeQueryIdentity } from "./query-identity.js";
|
|
2
2
|
import { copyDate, dateMilliseconds } from "../date-value.js";
|
|
3
3
|
import { estimateValuesBytes } from "./byte-estimates.js";
|
|
4
|
-
import { copyQueryResultExternalization } from "./
|
|
4
|
+
import { copyQueryResultExternalization } from "./result-state.js";
|
|
5
5
|
import { defineSqlResultProperty } from "./sql-semantics.js";
|
|
6
6
|
const RESULT_MEMO_MAX_BYTES = 4 * 1024 * 1024;
|
|
7
|
+
function sameQueryRow(left, right, columns) {
|
|
8
|
+
if (left === right)
|
|
9
|
+
return true;
|
|
10
|
+
for (const column of columns) {
|
|
11
|
+
const a = left[column] ?? null;
|
|
12
|
+
const b = right[column] ?? null;
|
|
13
|
+
if (a instanceof Date || b instanceof Date) {
|
|
14
|
+
if (!(a instanceof Date && b instanceof Date) || !Object.is(dateMilliseconds(a), dateMilliseconds(b)))
|
|
15
|
+
return false;
|
|
16
|
+
} else if (!Object.is(a, b))
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
7
21
|
function queryResultMemoKey(sql, params) {
|
|
8
22
|
return JSON.stringify([sql, params.map(encodeParameter)]);
|
|
9
23
|
}
|
|
@@ -56,5 +70,6 @@ export {
|
|
|
56
70
|
copyQueryResult,
|
|
57
71
|
planMemoKey,
|
|
58
72
|
queryResultMemoKey,
|
|
59
|
-
queryResultRetainedBytes
|
|
73
|
+
queryResultRetainedBytes,
|
|
74
|
+
sameQueryRow
|
|
60
75
|
};
|
package/dist/engine/query.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { copyQueryResultExternalization, markQueryResultExternal } from "./result-state.js";
|
|
1
2
|
import type { DatabaseRow } from "./database.js";
|
|
2
3
|
import type { ColumnDefault, ColumnGenerated, SqlDomain } from "../storage/types.js";
|
|
3
4
|
import type { AggregateName, ComparisonOperator, CompiledQuery, Expression, FtsStats, JoinPlan, Predicate, PredicateOperator, QueryResult, QueryRow, QueryValue, ScalarFunctionName, SelectItem, SelectTail, SetOperator, TableSource, WindowSpec } from "../plan/model.js";
|
|
@@ -708,8 +709,6 @@ export declare function expressionAliases(expression: Expression): Set<string>;
|
|
|
708
709
|
* so comparison falls through to the next term.
|
|
709
710
|
*/
|
|
710
711
|
export declare function nullOrder(left: unknown, right: unknown, nulls: "first" | "last" | undefined, direction: "asc" | "desc"): number | undefined;
|
|
711
|
-
/** Carries the internal no-conversion proof across a defensive result copy. */
|
|
712
|
-
export declare function copyQueryResultExternalization(source: QueryResult, copy: QueryResult): QueryResult;
|
|
713
712
|
/** Removes internal domain tags only after every comparison, group, join, and sort is complete. */
|
|
714
713
|
export declare function externalizeQueryResult(result: QueryResult): QueryResult;
|
|
715
714
|
interface SelectBlockParts {
|
package/dist/engine/query.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { encodeQueryIdentity } from "./query-identity.js";
|
|
2
|
+
import { alreadyExternalResults, copyQueryResultExternalization } from "./result-state.js";
|
|
3
|
+
import { copyQueryResultExternalization as copyQueryResultExternalization2, markQueryResultExternal } from "./result-state.js";
|
|
2
4
|
import { civilFromDays, copyDate, dateIsoString, dateMilliseconds, daysFromCivil, epochDays, dateUtcDate, dateUtcFullYear, dateUtcMonth, setDateUtcDate, setDateUtcMonth } from "../date-value.js";
|
|
3
5
|
import { crossJoinPlan } from "../plan/model.js";
|
|
4
6
|
import { assertWellFormedString, wellFormedUtf8ByteLength } from "../block-format/unicode.js";
|
|
@@ -4674,17 +4676,11 @@ function asQueryValue(value) {
|
|
|
4674
4676
|
return null;
|
|
4675
4677
|
throw new TypeError("Query produced an unsupported value");
|
|
4676
4678
|
}
|
|
4677
|
-
const alreadyExternalResults = /* @__PURE__ */ new WeakSet();
|
|
4678
4679
|
function markExternalizationState(result, outputNeedsExternalization) {
|
|
4679
4680
|
if (outputNeedsExternalization === false)
|
|
4680
4681
|
alreadyExternalResults.add(result);
|
|
4681
4682
|
return result;
|
|
4682
4683
|
}
|
|
4683
|
-
function copyQueryResultExternalization(source, copy) {
|
|
4684
|
-
if (alreadyExternalResults.has(source))
|
|
4685
|
-
alreadyExternalResults.add(copy);
|
|
4686
|
-
return copy;
|
|
4687
|
-
}
|
|
4688
4684
|
function externalizeQueryResult(result) {
|
|
4689
4685
|
if (alreadyExternalResults.has(result))
|
|
4690
4686
|
return result;
|
|
@@ -9900,7 +9896,7 @@ export {
|
|
|
9900
9896
|
compileQuery,
|
|
9901
9897
|
compileStatement,
|
|
9902
9898
|
compoundSelectBlock,
|
|
9903
|
-
copyQueryResultExternalization,
|
|
9899
|
+
copyQueryResultExternalization2 as copyQueryResultExternalization,
|
|
9904
9900
|
createPreparedColumnarQuery,
|
|
9905
9901
|
createPreparedQuery,
|
|
9906
9902
|
createRecursiveCteState,
|
|
@@ -9940,6 +9936,7 @@ export {
|
|
|
9940
9936
|
likeMatches,
|
|
9941
9937
|
mapBlockExpressions,
|
|
9942
9938
|
mapChildExpressions,
|
|
9939
|
+
markQueryResultExternal,
|
|
9943
9940
|
nullOrder,
|
|
9944
9941
|
orderOutputName,
|
|
9945
9942
|
parseQuantified,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { QueryResult } from "../plan/model.js";
|
|
2
|
+
/** Shared boundary state; result copies must not need the SQL compiler or evaluators. */
|
|
3
|
+
export declare const alreadyExternalResults: WeakSet<QueryResult>;
|
|
4
|
+
/** Internal ownership boundary for results assembled entirely from already-public row values. */
|
|
5
|
+
export declare function markQueryResultExternal(result: QueryResult): QueryResult;
|
|
6
|
+
/** Carries the internal no-conversion proof across a defensive result copy. */
|
|
7
|
+
export declare function copyQueryResultExternalization(source: QueryResult, copy: QueryResult): QueryResult;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const alreadyExternalResults = /* @__PURE__ */ new WeakSet();
|
|
2
|
+
function markQueryResultExternal(result) {
|
|
3
|
+
alreadyExternalResults.add(result);
|
|
4
|
+
return result;
|
|
5
|
+
}
|
|
6
|
+
function copyQueryResultExternalization(source, copy) {
|
|
7
|
+
if (alreadyExternalResults.has(source))
|
|
8
|
+
alreadyExternalResults.add(copy);
|
|
9
|
+
return copy;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
alreadyExternalResults,
|
|
13
|
+
copyQueryResultExternalization,
|
|
14
|
+
markQueryResultExternal
|
|
15
|
+
};
|
package/dist/engine/vector.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface VectorWindow {
|
|
|
12
12
|
readonly length: number;
|
|
13
13
|
}
|
|
14
14
|
interface VectorBase {
|
|
15
|
+
/** Present on immutable block vectors, so keyed reads need no decoded-block lookup. */
|
|
16
|
+
readonly nullCount?: number;
|
|
15
17
|
readonly validity: Uint8Array;
|
|
16
18
|
readonly length: number;
|
|
17
19
|
readonly window?: VectorWindow;
|
|
@@ -110,5 +112,7 @@ interface PrepareVectorQueryOptions {
|
|
|
110
112
|
export declare function createColumnarTable(name: string, columns: ReadonlyMap<string, ColumnarColumnInput>, uniqueKey?: string): ColumnarTable;
|
|
111
113
|
export declare function columnarTableFromRows(name: string, rows: readonly DatabaseRow[], projectedColumnNames?: readonly string[], protectText?: boolean): ColumnarTable;
|
|
112
114
|
export declare function prepareVectorQuery(plan: CompiledQuery, inputTables: ReadonlyMap<string, ColumnarTable>, options?: PrepareVectorQueryOptions): PreparedVectorQuery;
|
|
115
|
+
/** Builds directly from a row/column reader without retaining an intermediate value array. */
|
|
116
|
+
export declare function createColumnVector(type: VectorType, length: number, valueAt: (index: number) => unknown): ColumnVector;
|
|
113
117
|
export declare function vectorValue(vector: ColumnVector, rowIndex: number): QueryValue;
|
|
114
118
|
export {};
|
package/dist/engine/vector.js
CHANGED
|
@@ -90,7 +90,7 @@ function createColumnarTable(name, columns, uniqueKey) {
|
|
|
90
90
|
if (column.values.length !== rowCount) {
|
|
91
91
|
throw new Error(`Column row count mismatch: ${name}.${columnName}`);
|
|
92
92
|
}
|
|
93
|
-
vectors.set(columnName,
|
|
93
|
+
vectors.set(columnName, createColumnVector(column.type, rowCount, (index) => column.values[index]));
|
|
94
94
|
}
|
|
95
95
|
if (uniqueKey !== void 0 && !vectors.has(uniqueKey)) {
|
|
96
96
|
throw new Error(`Unique-key vector is missing: ${name}.${uniqueKey}`);
|
|
@@ -205,21 +205,20 @@ function prepareVectorQuery(plan, inputTables, options = {}) {
|
|
|
205
205
|
throw error;
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
-
function
|
|
209
|
-
validateVectorType(
|
|
210
|
-
const length = input.values.length;
|
|
208
|
+
function createColumnVector(type, length, valueAt) {
|
|
209
|
+
validateVectorType(type);
|
|
211
210
|
const validity = new Uint8Array(Math.ceil(length / 8));
|
|
212
|
-
if (
|
|
211
|
+
if (type === "string") {
|
|
213
212
|
const dictionary = [];
|
|
214
213
|
const dictionaryIndex = /* @__PURE__ */ new Map();
|
|
215
214
|
const codes = new Uint32Array(length);
|
|
216
215
|
codes.fill(NULL_STRING_CODE);
|
|
217
216
|
for (let index = 0; index < length; index += 1) {
|
|
218
|
-
const value =
|
|
217
|
+
const value = valueAt(index);
|
|
219
218
|
if (value === null)
|
|
220
219
|
continue;
|
|
221
220
|
if (typeof value !== "string")
|
|
222
|
-
throw vectorTypeError(
|
|
221
|
+
throw vectorTypeError(type, value);
|
|
223
222
|
setValid(validity, index);
|
|
224
223
|
let code = dictionaryIndex.get(value);
|
|
225
224
|
if (code === void 0) {
|
|
@@ -231,14 +230,14 @@ function createVector(input) {
|
|
|
231
230
|
}
|
|
232
231
|
return { kind: "string", length, validity, codes, dictionary };
|
|
233
232
|
}
|
|
234
|
-
if (
|
|
233
|
+
if (type === "boolean") {
|
|
235
234
|
const values2 = new Uint8Array(length);
|
|
236
235
|
for (let index = 0; index < length; index += 1) {
|
|
237
|
-
const value =
|
|
236
|
+
const value = valueAt(index);
|
|
238
237
|
if (value === null)
|
|
239
238
|
continue;
|
|
240
239
|
if (typeof value !== "boolean")
|
|
241
|
-
throw vectorTypeError(
|
|
240
|
+
throw vectorTypeError(type, value);
|
|
242
241
|
setValid(validity, index);
|
|
243
242
|
values2[index] = value ? 1 : 0;
|
|
244
243
|
}
|
|
@@ -246,16 +245,16 @@ function createVector(input) {
|
|
|
246
245
|
}
|
|
247
246
|
const values = new Float64Array(length);
|
|
248
247
|
for (let index = 0; index < length; index += 1) {
|
|
249
|
-
const value =
|
|
248
|
+
const value = valueAt(index);
|
|
250
249
|
if (value === null)
|
|
251
250
|
continue;
|
|
252
|
-
const numericValue =
|
|
251
|
+
const numericValue = type === "datetime" ? value instanceof Date ? dateMilliseconds(value) : Number.NaN : typeof value === "number" ? value : Number.NaN;
|
|
253
252
|
if (!Number.isFinite(numericValue))
|
|
254
|
-
throw vectorTypeError(
|
|
253
|
+
throw vectorTypeError(type, value);
|
|
255
254
|
setValid(validity, index);
|
|
256
255
|
values[index] = numericValue;
|
|
257
256
|
}
|
|
258
|
-
return
|
|
257
|
+
return type === "datetime" ? { kind: "datetime", length, validity, values } : { kind: "number", length, validity, values };
|
|
259
258
|
}
|
|
260
259
|
function validateVectorType(type) {
|
|
261
260
|
if (type !== "boolean" && type !== "number" && type !== "string" && type !== "datetime") {
|
|
@@ -4876,6 +4875,7 @@ function safeMemoryProduct(left, right, label) {
|
|
|
4876
4875
|
}
|
|
4877
4876
|
export {
|
|
4878
4877
|
columnarTableFromRows,
|
|
4878
|
+
createColumnVector,
|
|
4879
4879
|
createColumnarTable,
|
|
4880
4880
|
prepareVectorQuery,
|
|
4881
4881
|
vectorValue
|