@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
|
@@ -5,40 +5,46 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
8
|
-
import type { NonEmptyReadonlyArray } from "../Array.
|
|
9
|
-
import { firstInArray, isNonEmptyArray } from "../Array.
|
|
10
|
-
import { assert } from "../Assert.
|
|
11
|
-
import type { Brand } from "../Brand.
|
|
12
|
-
import { concatBytes } from "../Buffer.
|
|
13
|
-
import { decrement } from "../Number.
|
|
14
|
-
import type { RandomDep } from "../Random.
|
|
15
|
-
import { err, ok } from "../Result.
|
|
16
|
-
import type { SqliteDep } from "../Sqlite.
|
|
17
|
-
import { sql, SqliteValue } from "../Sqlite.
|
|
18
|
-
import type { Task } from "../Task.
|
|
19
|
-
import { Millis } from "../Time.
|
|
20
|
-
import type { InferType, Int64String, Typed, TypeError } from "../Type.js";
|
|
8
|
+
import type { NonEmptyReadonlyArray } from "../Array.ts";
|
|
9
|
+
import { firstInArray, isNonEmptyArray } from "../Array.ts";
|
|
10
|
+
import { assert } from "../Assert.ts";
|
|
11
|
+
import type { Brand } from "../Brand.ts";
|
|
12
|
+
import { concatBytes } from "../Buffer.ts";
|
|
13
|
+
import { decrement } from "../Number.ts";
|
|
14
|
+
import type { RandomDep } from "../Random.ts";
|
|
15
|
+
import { err, ok } from "../Result.ts";
|
|
16
|
+
import type { SqliteDep } from "../Sqlite.ts";
|
|
17
|
+
import { sql, SqliteValue } from "../Sqlite.ts";
|
|
18
|
+
import type { Task } from "../Task.ts";
|
|
19
|
+
import { Millis } from "../Time.ts";
|
|
21
20
|
import {
|
|
22
21
|
Boolean,
|
|
23
22
|
brand,
|
|
24
23
|
Id,
|
|
24
|
+
type InferType,
|
|
25
|
+
type Int64String,
|
|
25
26
|
NonNegativeInt,
|
|
27
|
+
Null,
|
|
26
28
|
nullOr,
|
|
27
29
|
object,
|
|
30
|
+
type ObjectType,
|
|
28
31
|
PositiveInt,
|
|
29
32
|
record,
|
|
30
33
|
String,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
import {
|
|
34
|
+
type Typed,
|
|
35
|
+
type TypeError,
|
|
36
|
+
type UnionType,
|
|
37
|
+
} from "../Type.ts";
|
|
38
|
+
import type { Awaitable } from "../Types.ts";
|
|
39
|
+
import type { Owner, OwnerError, OwnerIdBytes } from "./Owner.ts";
|
|
40
|
+
import { OwnerId, OwnerWriteKey } from "./Owner.ts";
|
|
41
|
+
import { systemColumnsWithId } from "./Schema.ts";
|
|
36
42
|
import {
|
|
37
43
|
createTimestamp,
|
|
38
44
|
orderTimestampBytes,
|
|
39
45
|
Timestamp,
|
|
40
46
|
TimestampBytes,
|
|
41
|
-
} from "./Timestamp.
|
|
47
|
+
} from "./Timestamp.ts";
|
|
42
48
|
|
|
43
49
|
export interface StorageConfig {
|
|
44
50
|
/**
|
|
@@ -56,18 +62,37 @@ export interface StorageConfig {
|
|
|
56
62
|
* The callback returns a boolean rather than an error because error handling
|
|
57
63
|
* and logging are the responsibility of the callback implementation.
|
|
58
64
|
*
|
|
65
|
+
* Relay deployments configure this callback. Client applications can observe
|
|
66
|
+
* a denied relay write as a `ProtocolQuotaError` by subscribing once to the
|
|
67
|
+
* shared `EvoluErrorDep.evoluError` store returned by `createEvoluDeps`.
|
|
68
|
+
*
|
|
59
69
|
* ### Example
|
|
60
70
|
*
|
|
61
71
|
* ```ts
|
|
62
|
-
*
|
|
63
|
-
*
|
|
72
|
+
* import {
|
|
73
|
+
* createAppOwner,
|
|
74
|
+
* createOwnerSecret,
|
|
75
|
+
* createRandomBytes,
|
|
76
|
+
* PositiveInt,
|
|
77
|
+
* } from "@evolu/common";
|
|
78
|
+
* import type { StorageConfig } from "@evolu/common/local-first";
|
|
64
79
|
*
|
|
65
|
-
* //
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
80
|
+
* // Create once, persist the mnemonic securely, and restore it on later runs.
|
|
81
|
+
* const appOwner = createAppOwner(
|
|
82
|
+
* createOwnerSecret({ randomBytes: createRandomBytes() }),
|
|
83
|
+
* );
|
|
84
|
+
* const maxBytes = PositiveInt.orThrow(1024);
|
|
85
|
+
* const relayStorageConfig: StorageConfig = {
|
|
86
|
+
* isOwnerWithinQuota: (ownerId, requiredBytes) =>
|
|
87
|
+
* ownerId === appOwner.id && requiredBytes <= maxBytes,
|
|
70
88
|
* };
|
|
89
|
+
*
|
|
90
|
+
* expect(
|
|
91
|
+
* await relayStorageConfig.isOwnerWithinQuota(
|
|
92
|
+
* appOwner.id,
|
|
93
|
+
* PositiveInt.orThrow(2048),
|
|
94
|
+
* ),
|
|
95
|
+
* ).toBe(false);
|
|
71
96
|
* ```
|
|
72
97
|
*/
|
|
73
98
|
readonly isOwnerWithinQuota: (
|
|
@@ -270,7 +295,7 @@ export const testCreateCrdtMessage = (
|
|
|
270
295
|
});
|
|
271
296
|
|
|
272
297
|
export const DbChangeValues = /*#__PURE__*/ record(String, SqliteValue);
|
|
273
|
-
export type DbChangeValues = typeof DbChangeValues.
|
|
298
|
+
export type DbChangeValues = typeof DbChangeValues.Output;
|
|
274
299
|
|
|
275
300
|
export const ValidDbChangeValues = /*#__PURE__*/ brand(
|
|
276
301
|
"ValidDbChangeValues",
|
|
@@ -284,12 +309,15 @@ export const ValidDbChangeValues = /*#__PURE__*/ brand(
|
|
|
284
309
|
invalidColumns,
|
|
285
310
|
});
|
|
286
311
|
|
|
287
|
-
return ok(
|
|
312
|
+
return ok();
|
|
288
313
|
},
|
|
314
|
+
(error) =>
|
|
315
|
+
`DbChange values contain reserved system columns: ${error.invalidColumns.join(", ")}.`,
|
|
289
316
|
);
|
|
290
|
-
export type ValidDbChangeValues = typeof ValidDbChangeValues.
|
|
317
|
+
export type ValidDbChangeValues = typeof ValidDbChangeValues.Output;
|
|
291
318
|
|
|
292
319
|
export interface ValidDbChangeValuesError extends TypeError<"ValidDbChangeValues"> {
|
|
320
|
+
readonly value: DbChangeValues;
|
|
293
321
|
readonly invalidColumns: ReadonlyArray<string>;
|
|
294
322
|
}
|
|
295
323
|
|
|
@@ -297,7 +325,13 @@ export interface ValidDbChangeValuesError extends TypeError<"ValidDbChangeValues
|
|
|
297
325
|
* A DbChange is a change to a table row. Together with a unique
|
|
298
326
|
* {@link Timestamp}, it forms a {@link CrdtMessage}.
|
|
299
327
|
*/
|
|
300
|
-
export const DbChange
|
|
328
|
+
export const DbChange: ObjectType<{
|
|
329
|
+
readonly table: typeof String;
|
|
330
|
+
readonly id: typeof Id;
|
|
331
|
+
readonly values: typeof ValidDbChangeValues;
|
|
332
|
+
readonly isInsert: typeof Boolean;
|
|
333
|
+
readonly isDelete: UnionType<readonly [typeof Boolean, typeof Null]>;
|
|
334
|
+
}> = /*#__PURE__*/ object({
|
|
301
335
|
table: String,
|
|
302
336
|
id: Id,
|
|
303
337
|
values: ValidDbChangeValues,
|
|
@@ -320,9 +354,6 @@ export interface DbChange extends InferType<typeof DbChange> {}
|
|
|
320
354
|
* storage](https://github.com/hoytech/negentropy), except we use a Skiplist to
|
|
321
355
|
* leverage SQLite indexes, which makes the code simpler.
|
|
322
356
|
*
|
|
323
|
-
* Note: A paid review by the SQLite team is planned, as they use the same
|
|
324
|
-
* algorithm for their rsync tool.
|
|
325
|
-
*
|
|
326
357
|
* The ideal storage for a Relay should use an architecture like
|
|
327
358
|
* [strfry](https://github.com/hoytech/strfry) (a KV storage), but with Skiplist
|
|
328
359
|
* to ensure that insertion order doesn't matter (local-first apps can often
|
|
@@ -569,18 +600,31 @@ export const getTimestampInsertStrategy = (
|
|
|
569
600
|
};
|
|
570
601
|
|
|
571
602
|
/**
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
603
|
+
* Append derives the new node's metadata in its insertion statement. Prepend
|
|
604
|
+
* and insert require a second statement to update existing Skiplist ancestors.
|
|
605
|
+
*
|
|
606
|
+
* A non-level-1 append summarizes the suffix after the nearest preceding node
|
|
607
|
+
* at the same or a higher level. A reverse primary-key scan finds that boundary
|
|
608
|
+
* directly, so metadata traversal starts one level below the new node instead
|
|
609
|
+
* of descending from the maximum level.
|
|
610
|
+
*
|
|
611
|
+
* Append is the fastest strategy because level 1, which occurs 75% of the time,
|
|
612
|
+
* needs only the insertion, and promoted nodes derive their metadata without
|
|
613
|
+
* updating existing nodes. Prepend inserts first, then updates one successor at
|
|
614
|
+
* each relevant higher level through predictable edge searches. Insert is the
|
|
615
|
+
* slowest because an arbitrary interior position requires predecessor metadata
|
|
616
|
+
* for the new node and successor discovery to update existing ancestors. All
|
|
617
|
+
* traversals use bounded index ranges, but insert necessarily performs the most
|
|
618
|
+
* Skiplist work.
|
|
576
619
|
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
* before insertion, which we need for quota checks anyway.
|
|
620
|
+
* SQLite does not support `LATERAL` subqueries. Recursive traversals therefore
|
|
621
|
+
* alternate between finding the next timestamp and loading that row by primary
|
|
622
|
+
* key instead of repeating the same correlated range lookup for every column.
|
|
581
623
|
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
624
|
+
* Inserts are idempotent to support direct calls and message replay. `on
|
|
625
|
+
* conflict do nothing` makes a duplicate insertion a no-op, and `changes() > 0`
|
|
626
|
+
* ensures ancestor metadata is updated only when the preceding insertion added
|
|
627
|
+
* a timestamp.
|
|
584
628
|
*/
|
|
585
629
|
const insertTimestamp =
|
|
586
630
|
(deps: SqliteDep) =>
|
|
@@ -612,12 +656,20 @@ const insertTimestamp =
|
|
|
612
656
|
fc(b, cl, pt, nt, ih1, ih2, ic) as (
|
|
613
657
|
select
|
|
614
658
|
0,
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
659
|
+
${level} - 1,
|
|
660
|
+
ifnull(
|
|
661
|
+
(
|
|
662
|
+
select t
|
|
663
|
+
from evolu_timestamp
|
|
664
|
+
where
|
|
665
|
+
ownerId = ${ownerId}
|
|
666
|
+
and l >= ${level}
|
|
667
|
+
and t < ${timestamp}
|
|
668
|
+
order by t desc
|
|
669
|
+
limit 1
|
|
670
|
+
),
|
|
671
|
+
zeroblob(0)
|
|
619
672
|
),
|
|
620
|
-
zeroblob(0),
|
|
621
673
|
null,
|
|
622
674
|
0,
|
|
623
675
|
0,
|
|
@@ -643,33 +695,20 @@ const insertTimestamp =
|
|
|
643
695
|
)
|
|
644
696
|
),
|
|
645
697
|
iif(
|
|
646
|
-
|
|
647
|
-
(
|
|
648
|
-
select (ih1 | h1) - (ih1 & h1)
|
|
649
|
-
from evolu_timestamp
|
|
650
|
-
where ownerId = ${ownerId} and t = nt
|
|
651
|
-
),
|
|
698
|
+
node.t is not null,
|
|
699
|
+
(ih1 | node.h1) - (ih1 & node.h1),
|
|
652
700
|
ih1
|
|
653
701
|
),
|
|
654
702
|
iif(
|
|
655
|
-
|
|
656
|
-
(
|
|
657
|
-
select (ih2 | h2) - (ih2 & h2)
|
|
658
|
-
from evolu_timestamp
|
|
659
|
-
where ownerId = ${ownerId} and t = nt
|
|
660
|
-
),
|
|
703
|
+
node.t is not null,
|
|
704
|
+
(ih2 | node.h2) - (ih2 & node.h2),
|
|
661
705
|
ih2
|
|
662
706
|
),
|
|
663
|
-
iif(
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
where ownerId = ${ownerId} and t = nt
|
|
669
|
-
),
|
|
670
|
-
ic
|
|
671
|
-
)
|
|
672
|
-
from fc
|
|
707
|
+
iif(node.t is not null, ic + node.c, ic)
|
|
708
|
+
from
|
|
709
|
+
fc
|
|
710
|
+
left join evolu_timestamp as node
|
|
711
|
+
on b and node.ownerId = ${ownerId} and node.t = nt
|
|
673
712
|
where cl > 0
|
|
674
713
|
)
|
|
675
714
|
insert into evolu_timestamp (ownerId, t, l, h1, h2, c)
|
|
@@ -704,64 +743,60 @@ const insertTimestamp =
|
|
|
704
743
|
from evolu_timestamp
|
|
705
744
|
where ownerId = ${ownerId}
|
|
706
745
|
),
|
|
707
|
-
fp(b, cl, pt, nt, h1, h2
|
|
746
|
+
fp(b, cl, pt, nt, h1, h2) as (
|
|
708
747
|
select
|
|
709
748
|
0,
|
|
710
749
|
(select ml from ml),
|
|
711
750
|
null,
|
|
712
751
|
null,
|
|
713
752
|
null,
|
|
714
|
-
null,
|
|
715
753
|
null
|
|
716
754
|
union all
|
|
717
755
|
select
|
|
718
|
-
not b,
|
|
719
|
-
iif(b, cl - 1, cl),
|
|
756
|
+
not fp.b,
|
|
757
|
+
iif(fp.b, fp.cl - 1, fp.cl),
|
|
720
758
|
iif(
|
|
721
|
-
b,
|
|
722
|
-
iif(
|
|
723
|
-
|
|
759
|
+
fp.b,
|
|
760
|
+
iif(
|
|
761
|
+
fp.nt is not null and (fp.pt is null or fp.nt < fp.pt),
|
|
762
|
+
fp.nt,
|
|
763
|
+
fp.pt
|
|
764
|
+
),
|
|
765
|
+
fp.pt
|
|
724
766
|
),
|
|
725
767
|
iif(
|
|
726
|
-
b,
|
|
768
|
+
fp.b,
|
|
727
769
|
null,
|
|
728
770
|
(
|
|
729
771
|
select t
|
|
730
772
|
from evolu_timestamp
|
|
731
|
-
where
|
|
773
|
+
where
|
|
774
|
+
ownerId = ${ownerId}
|
|
775
|
+
and l = fp.cl
|
|
776
|
+
and t > ${timestamp}
|
|
732
777
|
order by t
|
|
733
778
|
limit 1
|
|
734
779
|
)
|
|
735
780
|
),
|
|
736
781
|
iif(
|
|
737
|
-
b
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
where ownerId = ${ownerId} and t = nt
|
|
742
|
-
),
|
|
743
|
-
null
|
|
744
|
-
),
|
|
745
|
-
iif(
|
|
746
|
-
b and nt is not null and (pt is null or nt < pt),
|
|
747
|
-
(
|
|
748
|
-
select h2
|
|
749
|
-
from evolu_timestamp
|
|
750
|
-
where ownerId = ${ownerId} and t = nt
|
|
751
|
-
),
|
|
782
|
+
fp.b
|
|
783
|
+
and fp.nt is not null
|
|
784
|
+
and (fp.pt is null or fp.nt < fp.pt),
|
|
785
|
+
node.h1,
|
|
752
786
|
null
|
|
753
787
|
),
|
|
754
788
|
iif(
|
|
755
|
-
b
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
where ownerId = ${ownerId} and t = nt
|
|
760
|
-
),
|
|
789
|
+
fp.b
|
|
790
|
+
and fp.nt is not null
|
|
791
|
+
and (fp.pt is null or fp.nt < fp.pt),
|
|
792
|
+
node.h2,
|
|
761
793
|
null
|
|
762
794
|
)
|
|
763
|
-
from
|
|
764
|
-
|
|
795
|
+
from
|
|
796
|
+
fp
|
|
797
|
+
left join evolu_timestamp as node
|
|
798
|
+
on fp.b and node.ownerId = ${ownerId} and node.t = fp.nt
|
|
799
|
+
where fp.cl > ${level}
|
|
765
800
|
),
|
|
766
801
|
u(t, h1, h2) as (
|
|
767
802
|
select
|
|
@@ -771,8 +806,7 @@ const insertTimestamp =
|
|
|
771
806
|
from fp
|
|
772
807
|
where h1 is not null and pt is not null
|
|
773
808
|
order by pt
|
|
774
|
-
|
|
775
|
-
limit 10
|
|
809
|
+
limit ${sql.raw(skiplistMaxLevel.toString())}
|
|
776
810
|
)
|
|
777
811
|
update evolu_timestamp
|
|
778
812
|
set
|
|
@@ -800,19 +834,17 @@ const insertTimestamp =
|
|
|
800
834
|
on conflict do nothing;
|
|
801
835
|
`,
|
|
802
836
|
|
|
803
|
-
// DEV: Check whether a boolean flag is faster.
|
|
804
837
|
sql.prepared`
|
|
805
838
|
with
|
|
806
|
-
p(l, t,
|
|
839
|
+
p(l, t, pt) as (
|
|
807
840
|
select
|
|
808
841
|
(
|
|
809
842
|
select max(l) + 1
|
|
810
843
|
from evolu_timestamp
|
|
811
844
|
where ownerId = ${ownerId}
|
|
812
845
|
),
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
null
|
|
846
|
+
X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
|
|
847
|
+
X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
|
|
816
848
|
union all
|
|
817
849
|
select
|
|
818
850
|
p.l - 1,
|
|
@@ -824,46 +856,27 @@ const insertTimestamp =
|
|
|
824
856
|
ownerId = ${ownerId}
|
|
825
857
|
and l = p.l - 1
|
|
826
858
|
and t > ${timestamp}
|
|
827
|
-
and
|
|
859
|
+
and t < p.t
|
|
828
860
|
order by t
|
|
829
861
|
limit 1
|
|
830
862
|
),
|
|
831
863
|
p.t
|
|
832
864
|
),
|
|
833
|
-
|
|
834
|
-
select h1
|
|
835
|
-
from evolu_timestamp
|
|
836
|
-
where
|
|
837
|
-
ownerId = ${ownerId}
|
|
838
|
-
and l = p.l - 1
|
|
839
|
-
and t > ${timestamp}
|
|
840
|
-
and (p.t is null or p.t > t)
|
|
841
|
-
order by t
|
|
842
|
-
limit 1
|
|
843
|
-
),
|
|
844
|
-
(
|
|
845
|
-
select h2
|
|
846
|
-
from evolu_timestamp
|
|
847
|
-
where
|
|
848
|
-
ownerId = ${ownerId}
|
|
849
|
-
and l = p.l - 1
|
|
850
|
-
and t > ${timestamp}
|
|
851
|
-
and (p.t is null or p.t > t)
|
|
852
|
-
order by t
|
|
853
|
-
limit 1
|
|
854
|
-
)
|
|
865
|
+
p.t
|
|
855
866
|
from p
|
|
856
867
|
where p.l > 2
|
|
857
|
-
|
|
858
|
-
limit 10
|
|
868
|
+
limit ${sql.raw(skiplistMaxLevel.toString())}
|
|
859
869
|
),
|
|
860
870
|
u(t, h1, h2) as (
|
|
861
871
|
select
|
|
862
|
-
t,
|
|
863
|
-
(${h1} | h1) - (${h1} & h1),
|
|
864
|
-
(${h2} | h2) - (${h2} & h2)
|
|
865
|
-
from
|
|
866
|
-
|
|
872
|
+
p.t,
|
|
873
|
+
(${h1} | node.h1) - (${h1} & node.h1),
|
|
874
|
+
(${h2} | node.h2) - (${h2} & node.h2)
|
|
875
|
+
from
|
|
876
|
+
p
|
|
877
|
+
join evolu_timestamp as node
|
|
878
|
+
on node.ownerId = ${ownerId} and node.t = p.t
|
|
879
|
+
where p.t is not p.pt
|
|
867
880
|
)
|
|
868
881
|
update evolu_timestamp
|
|
869
882
|
set
|
|
@@ -919,34 +932,16 @@ const insertTimestamp =
|
|
|
919
932
|
limit 1
|
|
920
933
|
)
|
|
921
934
|
),
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
b and cl < ${level} and nt is not null,
|
|
933
|
-
(
|
|
934
|
-
select h2
|
|
935
|
-
from evolu_timestamp
|
|
936
|
-
where ownerId = ${ownerId} and t = nt
|
|
937
|
-
),
|
|
938
|
-
null
|
|
939
|
-
),
|
|
940
|
-
iif(
|
|
941
|
-
b and cl < ${level} and nt is not null,
|
|
942
|
-
(
|
|
943
|
-
select c
|
|
944
|
-
from evolu_timestamp
|
|
945
|
-
where ownerId = ${ownerId} and t = nt
|
|
946
|
-
),
|
|
947
|
-
null
|
|
948
|
-
)
|
|
949
|
-
from c0
|
|
935
|
+
node.h1,
|
|
936
|
+
node.h2,
|
|
937
|
+
node.c
|
|
938
|
+
from
|
|
939
|
+
c0
|
|
940
|
+
left join evolu_timestamp as node
|
|
941
|
+
on c0.b
|
|
942
|
+
and c0.cl < ${level}
|
|
943
|
+
and node.ownerId = ${ownerId}
|
|
944
|
+
and node.t = c0.nt
|
|
950
945
|
where cl > 0
|
|
951
946
|
),
|
|
952
947
|
c1(l, t, h1, h2, c) as (
|
|
@@ -984,22 +979,27 @@ const insertTimestamp =
|
|
|
984
979
|
from c3
|
|
985
980
|
group by l
|
|
986
981
|
),
|
|
987
|
-
--
|
|
988
|
-
n(l, t, h1, h2, c) as (
|
|
982
|
+
-- Alternate range discovery and primary-key loading.
|
|
983
|
+
n(b, l, t, nt, h1, h2, c) as (
|
|
989
984
|
select
|
|
985
|
+
1,
|
|
990
986
|
(
|
|
991
987
|
select max(l) + 1
|
|
992
988
|
from evolu_timestamp
|
|
993
989
|
where ownerId = ${ownerId}
|
|
994
990
|
),
|
|
991
|
+
X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
|
|
995
992
|
null,
|
|
996
993
|
null,
|
|
997
994
|
null,
|
|
998
995
|
null
|
|
999
996
|
union all
|
|
1000
997
|
select
|
|
1001
|
-
n.
|
|
1002
|
-
|
|
998
|
+
not n.b,
|
|
999
|
+
iif(n.b, n.l - 1, n.l),
|
|
1000
|
+
iif(n.b, n.t, ifnull(n.nt, n.t)),
|
|
1001
|
+
iif(
|
|
1002
|
+
n.b,
|
|
1003
1003
|
(
|
|
1004
1004
|
select t
|
|
1005
1005
|
from evolu_timestamp
|
|
@@ -1007,47 +1007,22 @@ const insertTimestamp =
|
|
|
1007
1007
|
ownerId = ${ownerId}
|
|
1008
1008
|
and l = n.l - 1
|
|
1009
1009
|
and t > ${timestamp}
|
|
1010
|
-
and
|
|
1010
|
+
and t < n.t
|
|
1011
1011
|
order by t
|
|
1012
1012
|
limit 1
|
|
1013
1013
|
),
|
|
1014
|
-
|
|
1015
|
-
),
|
|
1016
|
-
(
|
|
1017
|
-
select h1
|
|
1018
|
-
from evolu_timestamp
|
|
1019
|
-
where
|
|
1020
|
-
ownerId = ${ownerId}
|
|
1021
|
-
and l = n.l - 1
|
|
1022
|
-
and t > ${timestamp}
|
|
1023
|
-
and (n.t is null or t < n.t)
|
|
1024
|
-
order by t
|
|
1025
|
-
limit 1
|
|
1026
|
-
),
|
|
1027
|
-
(
|
|
1028
|
-
select h2
|
|
1029
|
-
from evolu_timestamp
|
|
1030
|
-
where
|
|
1031
|
-
ownerId = ${ownerId}
|
|
1032
|
-
and l = n.l - 1
|
|
1033
|
-
and t > ${timestamp}
|
|
1034
|
-
and (n.t is null or t < n.t)
|
|
1035
|
-
order by t
|
|
1036
|
-
limit 1
|
|
1014
|
+
null
|
|
1037
1015
|
),
|
|
1038
|
-
(
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
)
|
|
1049
|
-
from n
|
|
1050
|
-
where l - 1 > (select min(l) from c4)
|
|
1016
|
+
iif(not n.b and n.nt is not null, node.h1, null),
|
|
1017
|
+
iif(not n.b and n.nt is not null, node.h2, null),
|
|
1018
|
+
iif(not n.b and n.nt is not null, node.c, null)
|
|
1019
|
+
from
|
|
1020
|
+
n
|
|
1021
|
+
left join evolu_timestamp as node
|
|
1022
|
+
on not n.b
|
|
1023
|
+
and node.ownerId = ${ownerId}
|
|
1024
|
+
and node.t = n.nt
|
|
1025
|
+
where iif(n.b, n.l - 1 > (select min(l) from c4), 1)
|
|
1051
1026
|
),
|
|
1052
1027
|
u(ut, uh1, uh2, uc) as (
|
|
1053
1028
|
select t, h1, h2, c from c4 where t = ${timestamp}
|
|
@@ -1085,9 +1060,9 @@ const insertTimestamp =
|
|
|
1085
1060
|
)
|
|
1086
1061
|
)
|
|
1087
1062
|
from n
|
|
1063
|
+
where b
|
|
1088
1064
|
group by t
|
|
1089
|
-
|
|
1090
|
-
limit 10
|
|
1065
|
+
limit ${sql.raw(skiplistMaxLevel.toString())}
|
|
1091
1066
|
)
|
|
1092
1067
|
update evolu_timestamp
|
|
1093
1068
|
set
|
|
@@ -1113,6 +1088,24 @@ export const timestampBytesToFingerprint = (
|
|
|
1113
1088
|
return hash as Fingerprint;
|
|
1114
1089
|
};
|
|
1115
1090
|
|
|
1091
|
+
/**
|
|
1092
|
+
* Computes a brute-force {@link Fingerprint} from {@link TimestampBytes} values
|
|
1093
|
+
* for tests and benchmarks.
|
|
1094
|
+
*/
|
|
1095
|
+
export const testFingerprintTimestamps = (
|
|
1096
|
+
timestamps: ReadonlyArray<TimestampBytes>,
|
|
1097
|
+
): Fingerprint =>
|
|
1098
|
+
timestamps
|
|
1099
|
+
.map(timestampBytesToFingerprint)
|
|
1100
|
+
.reduce<Fingerprint>(
|
|
1101
|
+
(left, right) =>
|
|
1102
|
+
Uint8Array.from(
|
|
1103
|
+
left,
|
|
1104
|
+
(byte, index) => byte ^ right[index],
|
|
1105
|
+
) as Fingerprint,
|
|
1106
|
+
zeroFingerprint,
|
|
1107
|
+
);
|
|
1108
|
+
|
|
1116
1109
|
/**
|
|
1117
1110
|
* Generates a random skiplist level in the range [1, skiplistMaxLevel].
|
|
1118
1111
|
* Probabilistic approach avoids the need for explicit tree balancing.
|
|
@@ -1136,13 +1129,32 @@ const randomSkiplistLevel = (deps: RandomDep): PositiveInt => {
|
|
|
1136
1129
|
const skiplistProbability = 0.25;
|
|
1137
1130
|
|
|
1138
1131
|
/**
|
|
1139
|
-
*
|
|
1140
|
-
*
|
|
1141
|
-
*
|
|
1142
|
-
*
|
|
1132
|
+
* Maximum Skiplist level and exact upper bound for planner-sensitive CTEs.
|
|
1133
|
+
*
|
|
1134
|
+
* The three CTEs bounded by this value feed `update ... from` statements in the
|
|
1135
|
+
* prepend and insert paths. Each emits at most one row per Skiplist level.
|
|
1136
|
+
* Without a known integer `limit`, SQLite estimates a recursive CTE at about
|
|
1137
|
+
* 2^32 rows and scans every timestamp for the owner instead of scanning the CTE
|
|
1138
|
+
* and looking up each timestamp by primary key.
|
|
1143
1139
|
*
|
|
1144
|
-
*
|
|
1145
|
-
*
|
|
1140
|
+
* Evolu does not run SQLite's `ANALYZE` command or `PRAGMA optimize`. `ANALYZE`
|
|
1141
|
+
* samples database contents and stores statistics in tables such as
|
|
1142
|
+
* `sqlite_stat1`; without those statistics, the query planner uses default
|
|
1143
|
+
* estimates. With those default estimates, 10 is the planner crossover.
|
|
1144
|
+
* Collected statistics or a planner change can move it because this is
|
|
1145
|
+
* undocumented cost model behavior. Do not enable planner statistics without
|
|
1146
|
+
* benchmarking every storage workload and verifying its query plans on a
|
|
1147
|
+
* representative populated database.
|
|
1148
|
+
*
|
|
1149
|
+
* The queries inject this value with `sql.raw`. SQLite versions before 3.47
|
|
1150
|
+
* cannot derive the estimate from a bound parameter. Newer versions require
|
|
1151
|
+
* QPSG to be disabled and invalidate the query plan whenever that parameter is
|
|
1152
|
+
* bound, which would recompile these hot cached statements on every execution.
|
|
1153
|
+
*
|
|
1154
|
+
* Because the limit can emit at most one row per Skiplist level, the Skiplist
|
|
1155
|
+
* is capped at the same value to prevent updates from being truncated. With
|
|
1156
|
+
* {@link skiplistProbability} 0.25, level 10 occurs once per 4^9 rows on
|
|
1157
|
+
* average, keeping the highest-level scan small for millions of rows.
|
|
1146
1158
|
*/
|
|
1147
1159
|
const skiplistMaxLevel = 10;
|
|
1148
1160
|
|
|
@@ -1340,12 +1352,15 @@ const fingerprint =
|
|
|
1340
1352
|
};
|
|
1341
1353
|
|
|
1342
1354
|
/**
|
|
1343
|
-
*
|
|
1355
|
+
* Computes all RBSR fingerprint buckets in one SQL statement.
|
|
1356
|
+
*
|
|
1357
|
+
* Continuous ranges reduce the required traversals from 32 to 16. Each
|
|
1358
|
+
* recursive step first finds the next timestamp, then loads its count and
|
|
1359
|
+
* fingerprint by primary key in the following step. This two-phase traversal
|
|
1360
|
+
* avoids repeated correlated range lookups because SQLite does not support
|
|
1361
|
+
* `LATERAL` subqueries.
|
|
1344
1362
|
*
|
|
1345
|
-
*
|
|
1346
|
-
* traversals. 16 instead of 32. And we compute all of them in a single SQL
|
|
1347
|
-
* select. If only we could get rid of those subqueries. Then, it would be
|
|
1348
|
-
* perfect. Btw, reading h1, h2, c in the second step would be slighly faster.
|
|
1363
|
+
* See https://logperiodic.com/rbsr.html#tree-friendly-functions.
|
|
1349
1364
|
*/
|
|
1350
1365
|
const fingerprintRanges =
|
|
1351
1366
|
(deps: SqliteDep) =>
|
|
@@ -1366,14 +1381,11 @@ const fingerprintRanges =
|
|
|
1366
1381
|
select max(l) from evolu_timestamp where ownerId = ${ownerId}
|
|
1367
1382
|
),
|
|
1368
1383
|
c0(c) as (select value as c from json_each(${bucketsJson})),
|
|
1369
|
-
c1(c, b, nt,
|
|
1384
|
+
c1(c, b, nt, ft, tt, dl, ic, h1, h2) as (
|
|
1370
1385
|
select
|
|
1371
1386
|
c,
|
|
1372
1387
|
1,
|
|
1373
1388
|
null,
|
|
1374
|
-
null,
|
|
1375
|
-
null,
|
|
1376
|
-
null,
|
|
1377
1389
|
zeroblob(0),
|
|
1378
1390
|
X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
|
|
1379
1391
|
ml,
|
|
@@ -1385,60 +1397,50 @@ const fingerprintRanges =
|
|
|
1385
1397
|
ml
|
|
1386
1398
|
union all
|
|
1387
1399
|
select
|
|
1388
|
-
c,
|
|
1389
|
-
not b,
|
|
1400
|
+
c1.c,
|
|
1401
|
+
not c1.b,
|
|
1390
1402
|
iif(
|
|
1391
|
-
b,
|
|
1403
|
+
c1.b,
|
|
1392
1404
|
(
|
|
1393
1405
|
select t
|
|
1394
1406
|
from evolu_timestamp
|
|
1395
|
-
where
|
|
1407
|
+
where
|
|
1408
|
+
l = c1.dl
|
|
1409
|
+
and t > c1.ft
|
|
1410
|
+
and t < c1.tt
|
|
1411
|
+
and ownerId = ${ownerId}
|
|
1396
1412
|
order by t
|
|
1397
1413
|
limit 1
|
|
1398
1414
|
),
|
|
1399
1415
|
null
|
|
1400
1416
|
),
|
|
1417
|
+
iif(c1.b, c1.ft, iif(c1.ic + node.c <= c1.c, c1.nt, c1.ft)),
|
|
1401
1418
|
iif(
|
|
1402
|
-
b,
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
from evolu_timestamp
|
|
1406
|
-
where l = dl and t > ft and t < tt and ownerId = ${ownerId}
|
|
1407
|
-
order by t
|
|
1408
|
-
limit 1
|
|
1409
|
-
),
|
|
1410
|
-
null
|
|
1419
|
+
c1.b,
|
|
1420
|
+
c1.tt,
|
|
1421
|
+
iif(c1.ic + node.c <= c1.c, c1.tt, ifnull(c1.nt, c1.tt))
|
|
1411
1422
|
),
|
|
1423
|
+
iif(c1.b, c1.dl, iif(c1.ic + node.c <= c1.c, c1.dl, c1.dl - 1)),
|
|
1412
1424
|
iif(
|
|
1413
|
-
b,
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
from evolu_timestamp
|
|
1417
|
-
where l = dl and t > ft and t < tt and ownerId = ${ownerId}
|
|
1418
|
-
order by t
|
|
1419
|
-
limit 1
|
|
1420
|
-
),
|
|
1421
|
-
null
|
|
1425
|
+
c1.b,
|
|
1426
|
+
c1.ic,
|
|
1427
|
+
iif(c1.ic + node.c <= c1.c, c1.ic + node.c, c1.ic)
|
|
1422
1428
|
),
|
|
1423
1429
|
iif(
|
|
1424
|
-
b,
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
from evolu_timestamp
|
|
1428
|
-
where l = dl and t > ft and t < tt and ownerId = ${ownerId}
|
|
1429
|
-
order by t
|
|
1430
|
-
limit 1
|
|
1431
|
-
),
|
|
1432
|
-
null
|
|
1430
|
+
c1.b,
|
|
1431
|
+
c1.h1,
|
|
1432
|
+
iif(c1.ic + node.c <= c1.c, ${x("c1.h1", "node.h1")}, c1.h1)
|
|
1433
1433
|
),
|
|
1434
|
-
iif(
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1434
|
+
iif(
|
|
1435
|
+
c1.b,
|
|
1436
|
+
c1.h2,
|
|
1437
|
+
iif(c1.ic + node.c <= c1.c, ${x("c1.h2", "node.h2")}, c1.h2)
|
|
1438
|
+
)
|
|
1439
|
+
from
|
|
1440
|
+
c1
|
|
1441
|
+
left join evolu_timestamp as node
|
|
1442
|
+
on not c1.b and node.ownerId = ${ownerId} and node.t = c1.nt
|
|
1443
|
+
where iif(c1.b, 1, c1.ic != c1.c)
|
|
1442
1444
|
),
|
|
1443
1445
|
c2(h1, h2, t, rn) as (
|
|
1444
1446
|
select
|