@evolu/common 8.0.0-next.4 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/Array.d.ts +279 -173
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +105 -101
- package/dist/src/Assert.d.ts +44 -44
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +44 -32
- package/dist/src/BigInt.d.ts +6 -3
- package/dist/src/BigInt.d.ts.map +1 -1
- package/dist/src/BigInt.js +5 -2
- package/dist/src/Brand.d.ts +27 -40
- package/dist/src/Brand.d.ts.map +1 -1
- package/dist/src/Buffer.d.ts +27 -11
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Cache.d.ts +9 -4
- package/dist/src/Cache.d.ts.map +1 -1
- package/dist/src/Cache.js +8 -3
- package/dist/src/Callbacks.d.ts +30 -19
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Console.d.ts +145 -66
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +91 -43
- package/dist/src/Crypto.d.ts +85 -42
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +44 -20
- package/dist/src/Eq.d.ts +87 -21
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +67 -19
- package/dist/src/Error.d.ts +5 -3
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +16 -0
- package/dist/src/Function.d.ts +119 -58
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +86 -43
- package/dist/src/Http.d.ts +394 -0
- package/dist/src/Http.d.ts.map +1 -0
- package/dist/src/Http.js +102 -0
- package/dist/src/Identicon.d.ts +16 -7
- package/dist/src/Identicon.d.ts.map +1 -1
- package/dist/src/Identicon.js +14 -5
- package/dist/src/LeakDetector.d.ts +88 -0
- package/dist/src/LeakDetector.d.ts.map +1 -0
- package/dist/src/LeakDetector.js +86 -0
- package/dist/src/LockManager.d.ts +18 -9
- package/dist/src/LockManager.d.ts.map +1 -1
- package/dist/src/LockManager.js +7 -8
- package/dist/src/Lookup.d.ts +43 -13
- package/dist/src/Lookup.d.ts.map +1 -1
- package/dist/src/Lookup.js +28 -8
- package/dist/src/Microtask.d.ts +1 -1
- package/dist/src/Microtask.d.ts.map +1 -1
- package/dist/src/Microtask.js +3 -7
- package/dist/src/Number.d.ts +49 -13
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +34 -5
- package/dist/src/Object.d.ts +160 -40
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +92 -46
- package/dist/src/Option.d.ts +10 -7
- package/dist/src/Option.d.ts.map +1 -1
- package/dist/src/Option.js +9 -6
- package/dist/src/Order.d.ts +21 -21
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +18 -21
- package/dist/src/Platform.d.ts +31 -0
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +98 -0
- package/dist/src/Random.d.ts +32 -17
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +12 -8
- package/dist/src/Redacted.d.ts +43 -29
- package/dist/src/Redacted.d.ts.map +1 -1
- package/dist/src/Redacted.js +14 -3
- package/dist/src/Ref.d.ts +11 -3
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Ref.js +2 -2
- package/dist/src/RefCount.d.ts +2 -6
- package/dist/src/RefCount.d.ts.map +1 -1
- package/dist/src/RefCount.js +33 -90
- package/dist/src/Relation.d.ts +70 -5
- package/dist/src/Relation.d.ts.map +1 -1
- package/dist/src/Relation.js +77 -2
- package/dist/src/Resource.d.ts +590 -155
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +592 -343
- package/dist/src/Result.d.ts +838 -191
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +163 -102
- package/dist/src/Schedule.d.ts +684 -231
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +766 -295
- package/dist/src/Set.d.ts +89 -40
- package/dist/src/Set.d.ts.map +1 -1
- package/dist/src/Set.js +50 -29
- package/dist/src/Sqlite.d.ts +29 -72
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +41 -46
- package/dist/src/StackTrace.d.ts +40 -0
- package/dist/src/StackTrace.d.ts.map +1 -0
- package/dist/src/StackTrace.js +140 -0
- package/dist/src/Store.d.ts +5 -4
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +5 -4
- package/dist/src/String.d.ts +7 -1
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +20 -7
- package/dist/src/Task.d.ts +3572 -1770
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1879 -1274
- package/dist/src/Test.d.ts +17 -125
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +17 -68
- package/dist/src/Time.d.ts +108 -49
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +172 -73
- package/dist/src/Type.d.ts +3542 -1926
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +3070 -1784
- package/dist/src/Types.d.ts +200 -108
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/Types.js +39 -8
- package/dist/src/WebSocket.d.ts +48 -23
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +15 -6
- package/dist/src/Worker.d.ts +94 -36
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +58 -24
- package/dist/src/index.d.ts +55 -52
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -1
- package/dist/src/intl/cs.d.ts +94 -0
- package/dist/src/intl/cs.d.ts.map +1 -0
- package/dist/src/intl/cs.js +220 -0
- package/dist/src/intl/index.d.ts +7 -0
- package/dist/src/intl/index.d.ts.map +1 -0
- package/dist/src/intl/index.js +6 -0
- package/dist/src/local-first/Db.d.ts +12 -8
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +109 -132
- package/dist/src/local-first/Error.d.ts +4 -4
- package/dist/src/local-first/Evolu.d.ts +338 -92
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +47 -55
- package/dist/src/local-first/LocalAuth.d.ts +4 -4
- package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
- package/dist/src/local-first/Owner.d.ts +61 -24
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +45 -8
- package/dist/src/local-first/Protocol.d.ts +44 -31
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +36 -13
- package/dist/src/local-first/Query.d.ts +147 -62
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +112 -58
- package/dist/src/local-first/Relay.d.ts +44 -14
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Schema.d.ts +92 -41
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +25 -8
- package/dist/src/local-first/Shared.d.ts +23 -21
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +100 -57
- package/dist/src/local-first/Storage.d.ts +53 -32
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +194 -234
- package/dist/src/local-first/Timestamp.d.ts +21 -21
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +2 -2
- package/dist/src/local-first/index.d.ts +11 -11
- package/package.json +14 -13
- package/src/Array.ts +306 -205
- package/src/Assert.ts +54 -69
- package/src/BigInt.ts +6 -3
- package/src/Brand.ts +27 -40
- package/src/Buffer.ts +28 -12
- package/src/Cache.ts +9 -4
- package/src/Callbacks.ts +30 -19
- package/src/Console.ts +156 -84
- package/src/Crypto.ts +83 -41
- package/src/Eq.ts +95 -22
- package/src/Error.ts +8 -4
- package/src/Function.ts +167 -59
- package/src/Http.ts +549 -0
- package/src/Identicon.ts +16 -7
- package/src/LeakDetector.ts +182 -0
- package/src/LockManager.ts +43 -39
- package/src/Lookup.ts +62 -28
- package/src/Microtask.ts +19 -23
- package/src/Number.ts +64 -12
- package/src/Object.ts +173 -49
- package/src/Option.ts +10 -7
- package/src/Order.ts +23 -21
- package/src/Platform.ts +136 -0
- package/src/Polyfills.ts +6 -12
- package/src/Random.ts +32 -19
- package/src/Redacted.ts +44 -30
- package/src/Ref.ts +14 -6
- package/src/RefCount.ts +38 -80
- package/src/Relation.ts +202 -10
- package/src/Resource.ts +1199 -591
- package/src/Result.ts +930 -271
- package/src/Schedule.ts +859 -343
- package/src/Set.ts +89 -40
- package/src/Sqlite.ts +144 -138
- package/src/StackTrace.ts +230 -0
- package/src/Store.ts +8 -7
- package/src/String.ts +22 -5
- package/src/Task.ts +5101 -2832
- package/src/Test.ts +20 -168
- package/src/Time.ts +293 -90
- package/src/Type.ts +10034 -4117
- package/src/Types.ts +227 -110
- package/src/WebSocket.ts +64 -36
- package/src/Worker.ts +180 -72
- package/src/index.ts +55 -52
- package/src/intl/cs.ts +388 -0
- package/src/intl/index.ts +7 -0
- package/src/local-first/Db.ts +152 -179
- package/src/local-first/Error.ts +4 -4
- package/src/local-first/Evolu.ts +495 -250
- package/src/local-first/LocalAuth.ts +6 -12
- package/src/local-first/Owner.ts +58 -21
- package/src/local-first/Protocol.ts +49 -31
- package/src/local-first/Query.ts +146 -61
- package/src/local-first/Relay.ts +51 -21
- package/src/local-first/Schema.ts +109 -51
- package/src/local-first/Shared.ts +266 -226
- package/src/local-first/Storage.ts +266 -264
- package/src/local-first/Timestamp.ts +25 -18
- package/src/local-first/index.ts +11 -11
- package/dist/src/Skiplist.d.ts +0 -28
- package/dist/src/Skiplist.d.ts.map +0 -1
- package/dist/src/Skiplist.js +0 -64
- package/dist/src/Tracer.d.ts +0 -48
- package/dist/src/Tracer.d.ts.map +0 -1
- package/dist/src/Tracer.js +0 -6
- package/src/Skiplist.ts +0 -108
- package/src/Tracer.ts +0 -54
|
@@ -11,7 +11,7 @@ import { decrement } from "../Number.js";
|
|
|
11
11
|
import { err, ok } from "../Result.js";
|
|
12
12
|
import { sql, SqliteValue } from "../Sqlite.js";
|
|
13
13
|
import { Millis } from "../Time.js";
|
|
14
|
-
import { Boolean, brand, Id, NonNegativeInt, nullOr, object, PositiveInt, record, String, } from "../Type.js";
|
|
14
|
+
import { Boolean, brand, Id, NonNegativeInt, Null, nullOr, object, PositiveInt, record, String, } from "../Type.js";
|
|
15
15
|
import { OwnerId, OwnerWriteKey } from "./Owner.js";
|
|
16
16
|
import { systemColumnsWithId } from "./Schema.js";
|
|
17
17
|
import { createTimestamp, orderTimestampBytes, Timestamp, TimestampBytes, } from "./Timestamp.js";
|
|
@@ -47,8 +47,8 @@ export const ValidDbChangeValues = /*#__PURE__*/ brand("ValidDbChangeValues", Db
|
|
|
47
47
|
value,
|
|
48
48
|
invalidColumns,
|
|
49
49
|
});
|
|
50
|
-
return ok(
|
|
51
|
-
});
|
|
50
|
+
return ok();
|
|
51
|
+
}, (error) => `DbChange values contain reserved system columns: ${error.invalidColumns.join(", ")}.`);
|
|
52
52
|
/**
|
|
53
53
|
* A DbChange is a change to a table row. Together with a unique
|
|
54
54
|
* {@link Timestamp}, it forms a {@link CrdtMessage}.
|
|
@@ -229,18 +229,31 @@ export const getTimestampInsertStrategy = (timestamp, firstTimestamp, lastTimest
|
|
|
229
229
|
return ["insert", firstTimestamp, lastTimestamp];
|
|
230
230
|
};
|
|
231
231
|
/**
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
232
|
+
* Append derives the new node's metadata in its insertion statement. Prepend
|
|
233
|
+
* and insert require a second statement to update existing Skiplist ancestors.
|
|
234
|
+
*
|
|
235
|
+
* A non-level-1 append summarizes the suffix after the nearest preceding node
|
|
236
|
+
* at the same or a higher level. A reverse primary-key scan finds that boundary
|
|
237
|
+
* directly, so metadata traversal starts one level below the new node instead
|
|
238
|
+
* of descending from the maximum level.
|
|
239
|
+
*
|
|
240
|
+
* Append is the fastest strategy because level 1, which occurs 75% of the time,
|
|
241
|
+
* needs only the insertion, and promoted nodes derive their metadata without
|
|
242
|
+
* updating existing nodes. Prepend inserts first, then updates one successor at
|
|
243
|
+
* each relevant higher level through predictable edge searches. Insert is the
|
|
244
|
+
* slowest because an arbitrary interior position requires predecessor metadata
|
|
245
|
+
* for the new node and successor discovery to update existing ancestors. All
|
|
246
|
+
* traversals use bounded index ranges, but insert necessarily performs the most
|
|
247
|
+
* Skiplist work.
|
|
236
248
|
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
* before insertion, which we need for quota checks anyway.
|
|
249
|
+
* SQLite does not support `LATERAL` subqueries. Recursive traversals therefore
|
|
250
|
+
* alternate between finding the next timestamp and loading that row by primary
|
|
251
|
+
* key instead of repeating the same correlated range lookup for every column.
|
|
241
252
|
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
253
|
+
* Inserts are idempotent to support direct calls and message replay. `on
|
|
254
|
+
* conflict do nothing` makes a duplicate insertion a no-op, and `changes() > 0`
|
|
255
|
+
* ensures ancestor metadata is updated only when the preceding insertion added
|
|
256
|
+
* a timestamp.
|
|
244
257
|
*/
|
|
245
258
|
const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
246
259
|
const [h1, h2] = fingerprintToSqliteFingerprint(timestampBytesToFingerprint(timestamp));
|
|
@@ -261,12 +274,20 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
261
274
|
fc(b, cl, pt, nt, ih1, ih2, ic) as (
|
|
262
275
|
select
|
|
263
276
|
0,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
277
|
+
${level} - 1,
|
|
278
|
+
ifnull(
|
|
279
|
+
(
|
|
280
|
+
select t
|
|
281
|
+
from evolu_timestamp
|
|
282
|
+
where
|
|
283
|
+
ownerId = ${ownerId}
|
|
284
|
+
and l >= ${level}
|
|
285
|
+
and t < ${timestamp}
|
|
286
|
+
order by t desc
|
|
287
|
+
limit 1
|
|
288
|
+
),
|
|
289
|
+
zeroblob(0)
|
|
268
290
|
),
|
|
269
|
-
zeroblob(0),
|
|
270
291
|
null,
|
|
271
292
|
0,
|
|
272
293
|
0,
|
|
@@ -292,33 +313,20 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
292
313
|
)
|
|
293
314
|
),
|
|
294
315
|
iif(
|
|
295
|
-
|
|
296
|
-
(
|
|
297
|
-
select (ih1 | h1) - (ih1 & h1)
|
|
298
|
-
from evolu_timestamp
|
|
299
|
-
where ownerId = ${ownerId} and t = nt
|
|
300
|
-
),
|
|
316
|
+
node.t is not null,
|
|
317
|
+
(ih1 | node.h1) - (ih1 & node.h1),
|
|
301
318
|
ih1
|
|
302
319
|
),
|
|
303
320
|
iif(
|
|
304
|
-
|
|
305
|
-
(
|
|
306
|
-
select (ih2 | h2) - (ih2 & h2)
|
|
307
|
-
from evolu_timestamp
|
|
308
|
-
where ownerId = ${ownerId} and t = nt
|
|
309
|
-
),
|
|
321
|
+
node.t is not null,
|
|
322
|
+
(ih2 | node.h2) - (ih2 & node.h2),
|
|
310
323
|
ih2
|
|
311
324
|
),
|
|
312
|
-
iif(
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
where ownerId = ${ownerId} and t = nt
|
|
318
|
-
),
|
|
319
|
-
ic
|
|
320
|
-
)
|
|
321
|
-
from fc
|
|
325
|
+
iif(node.t is not null, ic + node.c, ic)
|
|
326
|
+
from
|
|
327
|
+
fc
|
|
328
|
+
left join evolu_timestamp as node
|
|
329
|
+
on b and node.ownerId = ${ownerId} and node.t = nt
|
|
322
330
|
where cl > 0
|
|
323
331
|
)
|
|
324
332
|
insert into evolu_timestamp (ownerId, t, l, h1, h2, c)
|
|
@@ -352,64 +360,60 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
352
360
|
from evolu_timestamp
|
|
353
361
|
where ownerId = ${ownerId}
|
|
354
362
|
),
|
|
355
|
-
fp(b, cl, pt, nt, h1, h2
|
|
363
|
+
fp(b, cl, pt, nt, h1, h2) as (
|
|
356
364
|
select
|
|
357
365
|
0,
|
|
358
366
|
(select ml from ml),
|
|
359
367
|
null,
|
|
360
368
|
null,
|
|
361
369
|
null,
|
|
362
|
-
null,
|
|
363
370
|
null
|
|
364
371
|
union all
|
|
365
372
|
select
|
|
366
|
-
not b,
|
|
367
|
-
iif(b, cl - 1, cl),
|
|
373
|
+
not fp.b,
|
|
374
|
+
iif(fp.b, fp.cl - 1, fp.cl),
|
|
368
375
|
iif(
|
|
369
|
-
b,
|
|
370
|
-
iif(
|
|
371
|
-
|
|
376
|
+
fp.b,
|
|
377
|
+
iif(
|
|
378
|
+
fp.nt is not null and (fp.pt is null or fp.nt < fp.pt),
|
|
379
|
+
fp.nt,
|
|
380
|
+
fp.pt
|
|
381
|
+
),
|
|
382
|
+
fp.pt
|
|
372
383
|
),
|
|
373
384
|
iif(
|
|
374
|
-
b,
|
|
385
|
+
fp.b,
|
|
375
386
|
null,
|
|
376
387
|
(
|
|
377
388
|
select t
|
|
378
389
|
from evolu_timestamp
|
|
379
|
-
where
|
|
390
|
+
where
|
|
391
|
+
ownerId = ${ownerId}
|
|
392
|
+
and l = fp.cl
|
|
393
|
+
and t > ${timestamp}
|
|
380
394
|
order by t
|
|
381
395
|
limit 1
|
|
382
396
|
)
|
|
383
397
|
),
|
|
384
398
|
iif(
|
|
385
|
-
b
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
where ownerId = ${ownerId} and t = nt
|
|
390
|
-
),
|
|
391
|
-
null
|
|
392
|
-
),
|
|
393
|
-
iif(
|
|
394
|
-
b and nt is not null and (pt is null or nt < pt),
|
|
395
|
-
(
|
|
396
|
-
select h2
|
|
397
|
-
from evolu_timestamp
|
|
398
|
-
where ownerId = ${ownerId} and t = nt
|
|
399
|
-
),
|
|
399
|
+
fp.b
|
|
400
|
+
and fp.nt is not null
|
|
401
|
+
and (fp.pt is null or fp.nt < fp.pt),
|
|
402
|
+
node.h1,
|
|
400
403
|
null
|
|
401
404
|
),
|
|
402
405
|
iif(
|
|
403
|
-
b
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
where ownerId = ${ownerId} and t = nt
|
|
408
|
-
),
|
|
406
|
+
fp.b
|
|
407
|
+
and fp.nt is not null
|
|
408
|
+
and (fp.pt is null or fp.nt < fp.pt),
|
|
409
|
+
node.h2,
|
|
409
410
|
null
|
|
410
411
|
)
|
|
411
|
-
from
|
|
412
|
-
|
|
412
|
+
from
|
|
413
|
+
fp
|
|
414
|
+
left join evolu_timestamp as node
|
|
415
|
+
on fp.b and node.ownerId = ${ownerId} and node.t = fp.nt
|
|
416
|
+
where fp.cl > ${level}
|
|
413
417
|
),
|
|
414
418
|
u(t, h1, h2) as (
|
|
415
419
|
select
|
|
@@ -419,8 +423,7 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
419
423
|
from fp
|
|
420
424
|
where h1 is not null and pt is not null
|
|
421
425
|
order by pt
|
|
422
|
-
|
|
423
|
-
limit 10
|
|
426
|
+
limit ${sql.raw(skiplistMaxLevel.toString())}
|
|
424
427
|
)
|
|
425
428
|
update evolu_timestamp
|
|
426
429
|
set
|
|
@@ -446,19 +449,17 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
446
449
|
(${ownerId}, 1, ${timestamp}, ${h1}, ${h2}, 1)
|
|
447
450
|
on conflict do nothing;
|
|
448
451
|
`,
|
|
449
|
-
// DEV: Check whether a boolean flag is faster.
|
|
450
452
|
sql.prepared `
|
|
451
453
|
with
|
|
452
|
-
p(l, t,
|
|
454
|
+
p(l, t, pt) as (
|
|
453
455
|
select
|
|
454
456
|
(
|
|
455
457
|
select max(l) + 1
|
|
456
458
|
from evolu_timestamp
|
|
457
459
|
where ownerId = ${ownerId}
|
|
458
460
|
),
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
null
|
|
461
|
+
X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
|
|
462
|
+
X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
|
|
462
463
|
union all
|
|
463
464
|
select
|
|
464
465
|
p.l - 1,
|
|
@@ -470,46 +471,27 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
470
471
|
ownerId = ${ownerId}
|
|
471
472
|
and l = p.l - 1
|
|
472
473
|
and t > ${timestamp}
|
|
473
|
-
and
|
|
474
|
+
and t < p.t
|
|
474
475
|
order by t
|
|
475
476
|
limit 1
|
|
476
477
|
),
|
|
477
478
|
p.t
|
|
478
479
|
),
|
|
479
|
-
|
|
480
|
-
select h1
|
|
481
|
-
from evolu_timestamp
|
|
482
|
-
where
|
|
483
|
-
ownerId = ${ownerId}
|
|
484
|
-
and l = p.l - 1
|
|
485
|
-
and t > ${timestamp}
|
|
486
|
-
and (p.t is null or p.t > t)
|
|
487
|
-
order by t
|
|
488
|
-
limit 1
|
|
489
|
-
),
|
|
490
|
-
(
|
|
491
|
-
select h2
|
|
492
|
-
from evolu_timestamp
|
|
493
|
-
where
|
|
494
|
-
ownerId = ${ownerId}
|
|
495
|
-
and l = p.l - 1
|
|
496
|
-
and t > ${timestamp}
|
|
497
|
-
and (p.t is null or p.t > t)
|
|
498
|
-
order by t
|
|
499
|
-
limit 1
|
|
500
|
-
)
|
|
480
|
+
p.t
|
|
501
481
|
from p
|
|
502
482
|
where p.l > 2
|
|
503
|
-
|
|
504
|
-
limit 10
|
|
483
|
+
limit ${sql.raw(skiplistMaxLevel.toString())}
|
|
505
484
|
),
|
|
506
485
|
u(t, h1, h2) as (
|
|
507
486
|
select
|
|
508
|
-
t,
|
|
509
|
-
(${h1} | h1) - (${h1} & h1),
|
|
510
|
-
(${h2} | h2) - (${h2} & h2)
|
|
511
|
-
from
|
|
512
|
-
|
|
487
|
+
p.t,
|
|
488
|
+
(${h1} | node.h1) - (${h1} & node.h1),
|
|
489
|
+
(${h2} | node.h2) - (${h2} & node.h2)
|
|
490
|
+
from
|
|
491
|
+
p
|
|
492
|
+
join evolu_timestamp as node
|
|
493
|
+
on node.ownerId = ${ownerId} and node.t = p.t
|
|
494
|
+
where p.t is not p.pt
|
|
513
495
|
)
|
|
514
496
|
update evolu_timestamp
|
|
515
497
|
set
|
|
@@ -564,34 +546,16 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
564
546
|
limit 1
|
|
565
547
|
)
|
|
566
548
|
),
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
b and cl < ${level} and nt is not null,
|
|
578
|
-
(
|
|
579
|
-
select h2
|
|
580
|
-
from evolu_timestamp
|
|
581
|
-
where ownerId = ${ownerId} and t = nt
|
|
582
|
-
),
|
|
583
|
-
null
|
|
584
|
-
),
|
|
585
|
-
iif(
|
|
586
|
-
b and cl < ${level} and nt is not null,
|
|
587
|
-
(
|
|
588
|
-
select c
|
|
589
|
-
from evolu_timestamp
|
|
590
|
-
where ownerId = ${ownerId} and t = nt
|
|
591
|
-
),
|
|
592
|
-
null
|
|
593
|
-
)
|
|
594
|
-
from c0
|
|
549
|
+
node.h1,
|
|
550
|
+
node.h2,
|
|
551
|
+
node.c
|
|
552
|
+
from
|
|
553
|
+
c0
|
|
554
|
+
left join evolu_timestamp as node
|
|
555
|
+
on c0.b
|
|
556
|
+
and c0.cl < ${level}
|
|
557
|
+
and node.ownerId = ${ownerId}
|
|
558
|
+
and node.t = c0.nt
|
|
595
559
|
where cl > 0
|
|
596
560
|
),
|
|
597
561
|
c1(l, t, h1, h2, c) as (
|
|
@@ -629,22 +593,27 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
629
593
|
from c3
|
|
630
594
|
group by l
|
|
631
595
|
),
|
|
632
|
-
--
|
|
633
|
-
n(l, t, h1, h2, c) as (
|
|
596
|
+
-- Alternate range discovery and primary-key loading.
|
|
597
|
+
n(b, l, t, nt, h1, h2, c) as (
|
|
634
598
|
select
|
|
599
|
+
1,
|
|
635
600
|
(
|
|
636
601
|
select max(l) + 1
|
|
637
602
|
from evolu_timestamp
|
|
638
603
|
where ownerId = ${ownerId}
|
|
639
604
|
),
|
|
605
|
+
X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
|
|
640
606
|
null,
|
|
641
607
|
null,
|
|
642
608
|
null,
|
|
643
609
|
null
|
|
644
610
|
union all
|
|
645
611
|
select
|
|
646
|
-
n.
|
|
647
|
-
|
|
612
|
+
not n.b,
|
|
613
|
+
iif(n.b, n.l - 1, n.l),
|
|
614
|
+
iif(n.b, n.t, ifnull(n.nt, n.t)),
|
|
615
|
+
iif(
|
|
616
|
+
n.b,
|
|
648
617
|
(
|
|
649
618
|
select t
|
|
650
619
|
from evolu_timestamp
|
|
@@ -652,47 +621,22 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
652
621
|
ownerId = ${ownerId}
|
|
653
622
|
and l = n.l - 1
|
|
654
623
|
and t > ${timestamp}
|
|
655
|
-
and
|
|
624
|
+
and t < n.t
|
|
656
625
|
order by t
|
|
657
626
|
limit 1
|
|
658
627
|
),
|
|
659
|
-
|
|
660
|
-
),
|
|
661
|
-
(
|
|
662
|
-
select h1
|
|
663
|
-
from evolu_timestamp
|
|
664
|
-
where
|
|
665
|
-
ownerId = ${ownerId}
|
|
666
|
-
and l = n.l - 1
|
|
667
|
-
and t > ${timestamp}
|
|
668
|
-
and (n.t is null or t < n.t)
|
|
669
|
-
order by t
|
|
670
|
-
limit 1
|
|
671
|
-
),
|
|
672
|
-
(
|
|
673
|
-
select h2
|
|
674
|
-
from evolu_timestamp
|
|
675
|
-
where
|
|
676
|
-
ownerId = ${ownerId}
|
|
677
|
-
and l = n.l - 1
|
|
678
|
-
and t > ${timestamp}
|
|
679
|
-
and (n.t is null or t < n.t)
|
|
680
|
-
order by t
|
|
681
|
-
limit 1
|
|
628
|
+
null
|
|
682
629
|
),
|
|
683
|
-
(
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
)
|
|
694
|
-
from n
|
|
695
|
-
where l - 1 > (select min(l) from c4)
|
|
630
|
+
iif(not n.b and n.nt is not null, node.h1, null),
|
|
631
|
+
iif(not n.b and n.nt is not null, node.h2, null),
|
|
632
|
+
iif(not n.b and n.nt is not null, node.c, null)
|
|
633
|
+
from
|
|
634
|
+
n
|
|
635
|
+
left join evolu_timestamp as node
|
|
636
|
+
on not n.b
|
|
637
|
+
and node.ownerId = ${ownerId}
|
|
638
|
+
and node.t = n.nt
|
|
639
|
+
where iif(n.b, n.l - 1 > (select min(l) from c4), 1)
|
|
696
640
|
),
|
|
697
641
|
u(ut, uh1, uh2, uc) as (
|
|
698
642
|
select t, h1, h2, c from c4 where t = ${timestamp}
|
|
@@ -730,9 +674,9 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
730
674
|
)
|
|
731
675
|
)
|
|
732
676
|
from n
|
|
677
|
+
where b
|
|
733
678
|
group by t
|
|
734
|
-
|
|
735
|
-
limit 10
|
|
679
|
+
limit ${sql.raw(skiplistMaxLevel.toString())}
|
|
736
680
|
)
|
|
737
681
|
update evolu_timestamp
|
|
738
682
|
set
|
|
@@ -753,6 +697,13 @@ export const timestampBytesToFingerprint = (timestamp) => {
|
|
|
753
697
|
const hash = sha256(timestamp).slice(0, fingerprintSize);
|
|
754
698
|
return hash;
|
|
755
699
|
};
|
|
700
|
+
/**
|
|
701
|
+
* Computes a brute-force {@link Fingerprint} from {@link TimestampBytes} values
|
|
702
|
+
* for tests and benchmarks.
|
|
703
|
+
*/
|
|
704
|
+
export const testFingerprintTimestamps = (timestamps) => timestamps
|
|
705
|
+
.map(timestampBytesToFingerprint)
|
|
706
|
+
.reduce((left, right) => Uint8Array.from(left, (byte, index) => byte ^ right[index]), zeroFingerprint);
|
|
756
707
|
/**
|
|
757
708
|
* Generates a random skiplist level in the range [1, skiplistMaxLevel].
|
|
758
709
|
* Probabilistic approach avoids the need for explicit tree balancing.
|
|
@@ -772,13 +723,32 @@ const randomSkiplistLevel = (deps) => {
|
|
|
772
723
|
*/
|
|
773
724
|
const skiplistProbability = 0.25;
|
|
774
725
|
/**
|
|
775
|
-
*
|
|
776
|
-
*
|
|
777
|
-
*
|
|
778
|
-
*
|
|
726
|
+
* Maximum Skiplist level and exact upper bound for planner-sensitive CTEs.
|
|
727
|
+
*
|
|
728
|
+
* The three CTEs bounded by this value feed `update ... from` statements in the
|
|
729
|
+
* prepend and insert paths. Each emits at most one row per Skiplist level.
|
|
730
|
+
* Without a known integer `limit`, SQLite estimates a recursive CTE at about
|
|
731
|
+
* 2^32 rows and scans every timestamp for the owner instead of scanning the CTE
|
|
732
|
+
* and looking up each timestamp by primary key.
|
|
733
|
+
*
|
|
734
|
+
* Evolu does not run SQLite's `ANALYZE` command or `PRAGMA optimize`. `ANALYZE`
|
|
735
|
+
* samples database contents and stores statistics in tables such as
|
|
736
|
+
* `sqlite_stat1`; without those statistics, the query planner uses default
|
|
737
|
+
* estimates. With those default estimates, 10 is the planner crossover.
|
|
738
|
+
* Collected statistics or a planner change can move it because this is
|
|
739
|
+
* undocumented cost model behavior. Do not enable planner statistics without
|
|
740
|
+
* benchmarking every storage workload and verifying its query plans on a
|
|
741
|
+
* representative populated database.
|
|
779
742
|
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
743
|
+
* The queries inject this value with `sql.raw`. SQLite versions before 3.47
|
|
744
|
+
* cannot derive the estimate from a bound parameter. Newer versions require
|
|
745
|
+
* QPSG to be disabled and invalidate the query plan whenever that parameter is
|
|
746
|
+
* bound, which would recompile these hot cached statements on every execution.
|
|
747
|
+
*
|
|
748
|
+
* Because the limit can emit at most one row per Skiplist level, the Skiplist
|
|
749
|
+
* is capped at the same value to prevent updates from being truncated. With
|
|
750
|
+
* {@link skiplistProbability} 0.25, level 10 occurs once per 4^9 rows on
|
|
751
|
+
* average, keeping the highest-level scan small for millions of rows.
|
|
782
752
|
*/
|
|
783
753
|
const skiplistMaxLevel = 10;
|
|
784
754
|
const fingerprintToSqliteFingerprint = (fingerprint) => {
|
|
@@ -922,12 +892,15 @@ const fingerprint = (deps) => (ownerId, begin, end) => {
|
|
|
922
892
|
return fingerprintRanges(deps)(ownerId, [begin, end])[1].fingerprint;
|
|
923
893
|
};
|
|
924
894
|
/**
|
|
925
|
-
*
|
|
895
|
+
* Computes all RBSR fingerprint buckets in one SQL statement.
|
|
896
|
+
*
|
|
897
|
+
* Continuous ranges reduce the required traversals from 32 to 16. Each
|
|
898
|
+
* recursive step first finds the next timestamp, then loads its count and
|
|
899
|
+
* fingerprint by primary key in the following step. This two-phase traversal
|
|
900
|
+
* avoids repeated correlated range lookups because SQLite does not support
|
|
901
|
+
* `LATERAL` subqueries.
|
|
926
902
|
*
|
|
927
|
-
*
|
|
928
|
-
* traversals. 16 instead of 32. And we compute all of them in a single SQL
|
|
929
|
-
* select. If only we could get rid of those subqueries. Then, it would be
|
|
930
|
-
* perfect. Btw, reading h1, h2, c in the second step would be slighly faster.
|
|
903
|
+
* See https://logperiodic.com/rbsr.html#tree-friendly-functions.
|
|
931
904
|
*/
|
|
932
905
|
const fingerprintRanges = (deps) => (ownerId, buckets, upperBound = InfiniteUpperBound) => {
|
|
933
906
|
const bucketsJson = JSON.stringify(buckets);
|
|
@@ -937,14 +910,11 @@ const fingerprintRanges = (deps) => (ownerId, buckets, upperBound = InfiniteUppe
|
|
|
937
910
|
select max(l) from evolu_timestamp where ownerId = ${ownerId}
|
|
938
911
|
),
|
|
939
912
|
c0(c) as (select value as c from json_each(${bucketsJson})),
|
|
940
|
-
c1(c, b, nt,
|
|
913
|
+
c1(c, b, nt, ft, tt, dl, ic, h1, h2) as (
|
|
941
914
|
select
|
|
942
915
|
c,
|
|
943
916
|
1,
|
|
944
917
|
null,
|
|
945
|
-
null,
|
|
946
|
-
null,
|
|
947
|
-
null,
|
|
948
918
|
zeroblob(0),
|
|
949
919
|
X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
|
|
950
920
|
ml,
|
|
@@ -956,60 +926,50 @@ const fingerprintRanges = (deps) => (ownerId, buckets, upperBound = InfiniteUppe
|
|
|
956
926
|
ml
|
|
957
927
|
union all
|
|
958
928
|
select
|
|
959
|
-
c,
|
|
960
|
-
not b,
|
|
929
|
+
c1.c,
|
|
930
|
+
not c1.b,
|
|
961
931
|
iif(
|
|
962
|
-
b,
|
|
932
|
+
c1.b,
|
|
963
933
|
(
|
|
964
934
|
select t
|
|
965
935
|
from evolu_timestamp
|
|
966
|
-
where
|
|
936
|
+
where
|
|
937
|
+
l = c1.dl
|
|
938
|
+
and t > c1.ft
|
|
939
|
+
and t < c1.tt
|
|
940
|
+
and ownerId = ${ownerId}
|
|
967
941
|
order by t
|
|
968
942
|
limit 1
|
|
969
943
|
),
|
|
970
944
|
null
|
|
971
945
|
),
|
|
946
|
+
iif(c1.b, c1.ft, iif(c1.ic + node.c <= c1.c, c1.nt, c1.ft)),
|
|
972
947
|
iif(
|
|
973
|
-
b,
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
from evolu_timestamp
|
|
977
|
-
where l = dl and t > ft and t < tt and ownerId = ${ownerId}
|
|
978
|
-
order by t
|
|
979
|
-
limit 1
|
|
980
|
-
),
|
|
981
|
-
null
|
|
948
|
+
c1.b,
|
|
949
|
+
c1.tt,
|
|
950
|
+
iif(c1.ic + node.c <= c1.c, c1.tt, ifnull(c1.nt, c1.tt))
|
|
982
951
|
),
|
|
952
|
+
iif(c1.b, c1.dl, iif(c1.ic + node.c <= c1.c, c1.dl, c1.dl - 1)),
|
|
983
953
|
iif(
|
|
984
|
-
b,
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
from evolu_timestamp
|
|
988
|
-
where l = dl and t > ft and t < tt and ownerId = ${ownerId}
|
|
989
|
-
order by t
|
|
990
|
-
limit 1
|
|
991
|
-
),
|
|
992
|
-
null
|
|
954
|
+
c1.b,
|
|
955
|
+
c1.ic,
|
|
956
|
+
iif(c1.ic + node.c <= c1.c, c1.ic + node.c, c1.ic)
|
|
993
957
|
),
|
|
994
958
|
iif(
|
|
995
|
-
b,
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
from evolu_timestamp
|
|
999
|
-
where l = dl and t > ft and t < tt and ownerId = ${ownerId}
|
|
1000
|
-
order by t
|
|
1001
|
-
limit 1
|
|
1002
|
-
),
|
|
1003
|
-
null
|
|
959
|
+
c1.b,
|
|
960
|
+
c1.h1,
|
|
961
|
+
iif(c1.ic + node.c <= c1.c, ${x("c1.h1", "node.h1")}, c1.h1)
|
|
1004
962
|
),
|
|
1005
|
-
iif(
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
963
|
+
iif(
|
|
964
|
+
c1.b,
|
|
965
|
+
c1.h2,
|
|
966
|
+
iif(c1.ic + node.c <= c1.c, ${x("c1.h2", "node.h2")}, c1.h2)
|
|
967
|
+
)
|
|
968
|
+
from
|
|
969
|
+
c1
|
|
970
|
+
left join evolu_timestamp as node
|
|
971
|
+
on not c1.b and node.ownerId = ${ownerId} and node.t = c1.nt
|
|
972
|
+
where iif(c1.b, 1, c1.ic != c1.c)
|
|
1013
973
|
),
|
|
1014
974
|
c2(h1, h2, t, rn) as (
|
|
1015
975
|
select
|