@agentshouse/mdruntime 0.1.0-c44866799613e → 0.1.0-c44d5cc00aee5
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/README.md +18 -17
- package/dist/acceptance.d.ts +3 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +299 -0
- package/dist/act-lanes.d.ts +30 -0
- package/dist/act-lanes.d.ts.map +1 -0
- package/dist/act-lanes.js +225 -0
- package/dist/actions.d.ts +26 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +302 -0
- package/dist/adapter.d.ts +110 -112
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +3 -11
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +28 -51
- package/dist/bootstrap.d.ts +15 -18
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +252 -158
- package/dist/cell.d.ts +6 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +9 -0
- package/dist/chain.d.ts +2 -1
- package/dist/chain.d.ts.map +1 -1
- package/dist/change-set.d.ts +4 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +6 -0
- package/dist/change.d.ts +1 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +4 -2
- package/dist/chat-types.d.ts +3 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +11 -0
- package/dist/client.d.ts +27 -21
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +36 -17
- package/dist/collection.d.ts +23 -7
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +13 -19
- package/dist/compile.d.ts +8 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +288 -329
- package/dist/connection.d.ts +5 -143
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +109 -5104
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation-live.d.ts +143 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +254 -0
- package/dist/conversation-row.d.ts +33 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +53 -0
- package/dist/conversation.d.ts +65 -48
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +987 -838
- package/dist/declaration.d.ts +10 -0
- package/dist/declaration.d.ts.map +1 -0
- package/dist/declaration.js +24 -0
- package/dist/discovery.d.ts +10 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +150 -0
- package/dist/durability.d.ts +19 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +4 -0
- package/dist/entry.d.ts +3 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +19 -9
- package/dist/expectation.d.ts +26 -13
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +72 -19
- package/dist/failure.d.ts +3 -0
- package/dist/failure.d.ts.map +1 -1
- package/dist/failure.js +6 -0
- package/dist/figures.d.ts +49 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +197 -0
- package/dist/holdings.d.ts +29 -0
- package/dist/holdings.d.ts.map +1 -0
- package/dist/holdings.js +95 -0
- package/dist/host.d.ts +21 -8
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +73 -40
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +34 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -5
- package/dist/install.d.ts +54 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +530 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +277 -0
- package/dist/listing.d.ts +48 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +139 -0
- package/dist/live.d.ts +5 -4
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +64 -23
- package/dist/locks.d.ts +7 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +32 -0
- package/dist/lost.d.ts +5 -0
- package/dist/lost.d.ts.map +1 -0
- package/dist/lost.js +49 -0
- package/dist/media.d.ts +2 -2
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +23 -13
- package/dist/memory.d.ts +17 -7
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +210 -97
- package/dist/messages.d.ts +33 -5
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +104 -36
- package/dist/model.d.ts +2 -11
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -23
- package/dist/models.d.ts +10 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +153 -0
- package/dist/observation.d.ts +4 -3
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +23 -17
- package/dist/opened.d.ts +1 -1
- package/dist/opened.d.ts.map +1 -1
- package/dist/opened.js +5 -5
- package/dist/opening.d.ts +4 -0
- package/dist/opening.d.ts.map +1 -0
- package/dist/opening.js +53 -0
- package/dist/outcome.d.ts +7 -4
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +18 -0
- package/dist/overlay.d.ts +6 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +47 -21
- package/dist/page-metadata.d.ts +8 -0
- package/dist/page-metadata.d.ts.map +1 -0
- package/dist/page-metadata.js +19 -0
- package/dist/parts.d.ts +4 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +13 -0
- package/dist/paths.d.ts +2 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +26 -0
- package/dist/preparation.d.ts +20 -0
- package/dist/preparation.d.ts.map +1 -0
- package/dist/preparation.js +227 -0
- package/dist/presence.d.ts +52 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +220 -0
- package/dist/queries.d.ts +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +282 -0
- package/dist/query.d.ts +14 -210
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -505
- package/dist/questions.d.ts +29 -0
- package/dist/questions.d.ts.map +1 -0
- package/dist/questions.js +115 -0
- package/dist/queue.d.ts +40 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +450 -0
- package/dist/reach.d.ts +15 -12
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +240 -149
- package/dist/react.d.ts +15 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +33 -1
- package/dist/read.d.ts +7 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/read.js +17 -0
- package/dist/readiness.d.ts +4 -19
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +4 -10
- package/dist/reads.d.ts +38 -10
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +163 -85
- package/dist/recovery.d.ts +3 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +57 -1
- package/dist/refresh.d.ts +7 -5
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +35 -7
- package/dist/refusal.d.ts +5 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +55 -0
- package/dist/replica.d.ts +12 -18
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +64 -34
- package/dist/retried.d.ts +30 -0
- package/dist/retried.d.ts.map +1 -0
- package/dist/retried.js +72 -0
- package/dist/room-draft.d.ts +24 -10
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +142 -106
- package/dist/room-drafts.d.ts +24 -0
- package/dist/room-drafts.d.ts.map +1 -0
- package/dist/room-drafts.js +217 -0
- package/dist/rows.d.ts +14 -21
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +49 -186
- package/dist/runtime.d.ts +34 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/save.d.ts +42 -84
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +349 -170
- package/dist/saves.d.ts +32 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +410 -0
- package/dist/search.d.ts +12 -63
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +9 -52
- package/dist/seed.d.ts +11 -2
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +71 -1
- package/dist/shared.d.ts +1 -7
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +13 -105
- package/dist/shown.d.ts +33 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +302 -0
- package/dist/socket/admission.d.ts +7 -0
- package/dist/socket/admission.d.ts.map +1 -0
- package/dist/socket/admission.js +39 -0
- package/dist/socket/answers.d.ts +6 -0
- package/dist/socket/answers.d.ts.map +1 -0
- package/dist/socket/answers.js +20 -0
- package/dist/socket/lanes.d.ts +28 -0
- package/dist/socket/lanes.d.ts.map +1 -0
- package/dist/socket/lanes.js +103 -0
- package/dist/socket/reads.d.ts +10 -0
- package/dist/socket/reads.d.ts.map +1 -0
- package/dist/socket/reads.js +31 -0
- package/dist/socket/session.d.ts +38 -0
- package/dist/socket/session.d.ts.map +1 -0
- package/dist/socket/session.js +23 -0
- package/dist/socket.d.ts +67 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +164 -0
- package/dist/source.d.ts +54 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +82 -0
- package/dist/status.d.ts +4 -5
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -7
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +34 -0
- package/dist/store/readiness.d.ts +10 -0
- package/dist/store/readiness.d.ts.map +1 -0
- package/dist/store/readiness.js +52 -0
- package/dist/store/records.d.ts +214 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +438 -0
- package/dist/store/room-held.d.ts +8 -0
- package/dist/store/room-held.d.ts.map +1 -0
- package/dist/store/room-held.js +17 -0
- package/dist/store/space.d.ts +11 -0
- package/dist/store/space.d.ts.map +1 -0
- package/dist/store/space.js +41 -0
- package/dist/store/watchers.d.ts +17 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +56 -0
- package/dist/store/writes.d.ts +24 -0
- package/dist/store/writes.d.ts.map +1 -0
- package/dist/store/writes.js +125 -0
- package/dist/store.d.ts +120 -171
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +166 -414
- package/dist/stored.d.ts +62 -57
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +317 -239
- package/dist/surface.d.ts +88 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +220 -0
- package/dist/sync.d.ts +19 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +535 -0
- package/dist/tables.d.ts +126 -65
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +250 -113
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +128 -0
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/validation.d.ts +3 -4
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +42 -100
- package/dist/windows.d.ts +1 -12
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +2 -75
- package/dist/wire.d.ts +65 -9
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +22 -5
- package/package.json +3 -2
- package/dist/attempt.d.ts +0 -16
- package/dist/attempt.d.ts.map +0 -1
- package/dist/attempt.js +0 -70
- package/dist/fence.d.ts +0 -6
- package/dist/fence.d.ts.map +0 -1
- package/dist/fence.js +0 -10
- package/dist/fulltext.d.ts +0 -27
- package/dist/fulltext.d.ts.map +0 -1
- package/dist/fulltext.js +0 -368
- package/dist/log.d.ts +0 -5
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js +0 -14
- package/dist/words.d.ts +0 -19
- package/dist/words.d.ts.map +0 -1
- package/dist/words.js +0 -150
package/dist/compile.js
CHANGED
|
@@ -1,119 +1,38 @@
|
|
|
1
|
-
import { ARRAY_INDEX, parse, pointerSegments } from '@agentshouse/mdmodel';
|
|
1
|
+
import { ARRAY_INDEX, bodied, byCodePoint, compared, cursorOf, heldKind, parse, pointerSegments, sql, } from '@agentshouse/mdmodel';
|
|
2
2
|
import { binaryRecord } from './binary.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
function
|
|
10
|
-
return {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function any(parts) {
|
|
19
|
-
return parts.length === 0 ? fragment('0') : joined(parts.map(grouped), ' OR ');
|
|
20
|
-
}
|
|
21
|
-
function grouped(part) {
|
|
22
|
-
return { sql: `(${part.sql})`, bind: part.bind };
|
|
23
|
-
}
|
|
24
|
-
function list(values) {
|
|
25
|
-
return fragment(`(${values.map(() => '?').join(', ')})`, values);
|
|
26
|
-
}
|
|
27
|
-
function sqlValue(value) {
|
|
28
|
-
if (value === null)
|
|
29
|
-
return { kind: 'null', value: null };
|
|
30
|
-
if (typeof value === 'boolean')
|
|
31
|
-
return { kind: 'boolean', value: value ? 1 : 0 };
|
|
32
|
-
if (typeof value === 'number')
|
|
33
|
-
return { kind: 'number', value };
|
|
34
|
-
return { kind: 'string', value };
|
|
35
|
-
}
|
|
36
|
-
function same(alias, value) {
|
|
37
|
-
const held = sqlValue(value);
|
|
38
|
-
return held.kind === 'null'
|
|
39
|
-
? fragment(`${alias}.kind = 'null'`)
|
|
40
|
-
: fragment(`${alias}.kind = ? AND ${alias}.value = ?`, [held.kind, held.value]);
|
|
41
|
-
}
|
|
42
|
-
const BOUNDS = { gt: '>', gte: '>=', lt: '<', lte: '<=' };
|
|
43
|
-
function pointed(alias, field, reach) {
|
|
44
|
-
const { keys, places, glob } = placedOf(field, reach);
|
|
45
|
-
return fragment(`${alias}.keys = ? AND ${alias}.places ${glob ? 'GLOB' : '='} ?`, [keys, places]);
|
|
46
|
-
}
|
|
47
|
-
function heldValues(doc, field, alias, where) {
|
|
48
|
-
return joined([
|
|
49
|
-
fragment(`SELECT 1 FROM replica_values ${alias} WHERE ${alias}.doc = ${doc}.id AND `),
|
|
50
|
-
grouped(pointed(alias, field, 'value')),
|
|
51
|
-
fragment(' AND '),
|
|
52
|
-
grouped(where),
|
|
53
|
-
], '');
|
|
54
|
-
}
|
|
55
|
-
function exists(part) {
|
|
56
|
-
return { sql: `EXISTS (${part.sql})`, bind: part.bind };
|
|
57
|
-
}
|
|
58
|
-
function itemOf(held, where) {
|
|
59
|
-
return exists(joined([
|
|
60
|
-
fragment(`SELECT 1 FROM replica_values i WHERE i.doc = ${held}.doc AND i.keys = ${held}.keys AND i.places = ${held}.places AND i.item >= 0 AND `),
|
|
61
|
-
grouped(where),
|
|
62
|
-
], ''));
|
|
63
|
-
}
|
|
64
|
-
function matching(held, values) {
|
|
65
|
-
return any(values.map((value) => any([
|
|
66
|
-
same(held, value),
|
|
67
|
-
all([fragment(`${held}.kind = 'array'`), itemOf(held, same('i', value))]),
|
|
68
|
-
])));
|
|
69
|
-
}
|
|
70
|
-
function tested(doc, field, test) {
|
|
71
|
-
const reached = exists(heldValues(doc, field, 'r', fragment('r.item = -1')));
|
|
72
|
-
if (test.op === 'exists')
|
|
73
|
-
return test.exists ? reached : fragment(`NOT ${reached.sql}`, reached.bind);
|
|
74
|
-
if (test.op === 'eq' || test.op === 'in') {
|
|
75
|
-
const values = test.op === 'eq' ? [test.value] : test.values;
|
|
76
|
-
return exists(heldValues(doc, field, 'v', any(values.map((value) => same('v', value)))));
|
|
77
|
-
}
|
|
78
|
-
if (test.op === 'ne' || test.op === 'nin') {
|
|
79
|
-
const values = test.op === 'ne' ? [test.value] : test.values;
|
|
80
|
-
const unmatched = matching('h', values);
|
|
81
|
-
return any([
|
|
82
|
-
fragment(`NOT ${reached.sql}`, reached.bind),
|
|
83
|
-
exists(heldValues(doc, field, 'h', all([fragment('h.item = -1'), fragment(`NOT (${unmatched.sql})`, unmatched.bind)]))),
|
|
84
|
-
]);
|
|
85
|
-
}
|
|
86
|
-
if (test.op === 'range') {
|
|
87
|
-
return exists(heldValues(doc, field, 'v', all([
|
|
88
|
-
fragment('v.item = -1'),
|
|
89
|
-
...test.bounds.map(([operator, bound]) => {
|
|
90
|
-
const held = sqlValue(bound);
|
|
91
|
-
return fragment(`v.kind = ? AND v.value ${BOUNDS[operator]} ?`, [
|
|
92
|
-
held.kind,
|
|
93
|
-
held.value,
|
|
94
|
-
]);
|
|
95
|
-
}),
|
|
96
|
-
])));
|
|
97
|
-
}
|
|
98
|
-
if (test.op === 'all') {
|
|
99
|
-
return exists(heldValues(doc, field, 'h', all([
|
|
100
|
-
fragment("h.item = -1 AND h.kind = 'array'"),
|
|
101
|
-
...test.values.map((value) => itemOf('h', same('i', value))),
|
|
102
|
-
])));
|
|
103
|
-
}
|
|
104
|
-
return exists(heldValues(doc, field, 'v', fragment("v.item = -1 AND v.kind = 'array' AND v.size = ?", [test.size])));
|
|
3
|
+
import { definitionsAt } from './collection.js';
|
|
4
|
+
import { documentKey, pathOfKey, roomOfKey } from './rows.js';
|
|
5
|
+
import { FROM_START, MAXIMUM_WINDOW } from './windows.js';
|
|
6
|
+
const UNTYPED = 'any';
|
|
7
|
+
const { all, any, exists, fragment, grouped, joined, listed } = sql;
|
|
8
|
+
export const VISIBLE = (alias) => `${alias}.visible = 1 AND ${alias}.present = 1 AND ${alias}.queried = 1`;
|
|
9
|
+
function located(alias) {
|
|
10
|
+
return fragment(`(SELECT k.locator FROM replica_keys k WHERE k.key = ${alias}.key)`);
|
|
11
|
+
}
|
|
12
|
+
export function within(alias, path) {
|
|
13
|
+
return fragment(`(${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [
|
|
14
|
+
path,
|
|
15
|
+
`${path}/`,
|
|
16
|
+
`${path}0`,
|
|
17
|
+
]);
|
|
105
18
|
}
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return
|
|
111
|
-
|
|
19
|
+
function ancestors(area) {
|
|
20
|
+
const depth = area.path.split('/').length;
|
|
21
|
+
return area.open.flatMap((open) => {
|
|
22
|
+
const segments = open.split('/');
|
|
23
|
+
return segments.slice(depth - 1, -1).map((_, at) => segments.slice(0, depth + at).join('/'));
|
|
24
|
+
});
|
|
112
25
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
26
|
+
function fenced(alias, key, area, folders) {
|
|
27
|
+
const opened = any([
|
|
28
|
+
...area.open.map((open) => within(alias, open)),
|
|
29
|
+
...(folders ? ancestors(area).map((path) => fragment(`${alias}.path = ?`, [path])) : []),
|
|
30
|
+
]);
|
|
31
|
+
return all([
|
|
32
|
+
fragment(`${alias}.key = ?`, [key]),
|
|
33
|
+
within(alias, area.path),
|
|
34
|
+
fragment(`NOT ${grouped(opened).sql}`, opened.bind),
|
|
35
|
+
]);
|
|
117
36
|
}
|
|
118
37
|
function reached(bound, alias) {
|
|
119
38
|
const reach = bound.reach;
|
|
@@ -127,42 +46,20 @@ function reached(bound, alias) {
|
|
|
127
46
|
const key = bound.keys.get(roomOfKey(held));
|
|
128
47
|
return key === undefined ? [] : [[key, pathOfKey(held)]];
|
|
129
48
|
});
|
|
130
|
-
const
|
|
49
|
+
const outside = any(reach.areas.flatMap((area) => {
|
|
131
50
|
const key = bound.keys.get(area.room);
|
|
132
|
-
return key === undefined ? [] : [
|
|
133
|
-
});
|
|
134
|
-
const beneath = (area) => fragment(`${alias}.key = ? AND (${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [area.key, area.path, `${area.path}/`, `${area.path}0`]);
|
|
135
|
-
const typed = (types) => fragment(`${alias}.type IN ${list(types).sql}`, types);
|
|
136
|
-
const outside = any(areas.map((area) => area.types.length === 0
|
|
137
|
-
? beneath(area)
|
|
138
|
-
: all([beneath(area), fragment(`NOT ${grouped(typed(area.types)).sql}`, area.types)])));
|
|
51
|
+
return key === undefined ? [] : [fenced(alias, key, area, false)];
|
|
52
|
+
}));
|
|
139
53
|
return all([
|
|
140
54
|
any([
|
|
141
|
-
...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${
|
|
55
|
+
...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${listed(rooms).sql}`, rooms)]),
|
|
142
56
|
...(documents.length === 0
|
|
143
57
|
? []
|
|
144
58
|
: [
|
|
145
59
|
fragment(`EXISTS (SELECT 1 FROM json_each(?) j WHERE j.value ->> 0 = ${alias}.key AND j.value ->> 1 = ${alias}.path)`, [JSON.stringify(documents)]),
|
|
146
60
|
]),
|
|
147
|
-
...areas.flatMap((area) => area.types.length === 0 ? [] : [all([beneath(area), typed(area.types)])]),
|
|
148
61
|
]),
|
|
149
|
-
...(areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
|
|
150
|
-
]);
|
|
151
|
-
}
|
|
152
|
-
function scoped(alias, scope) {
|
|
153
|
-
return all([
|
|
154
|
-
...(scope.types === null
|
|
155
|
-
? []
|
|
156
|
-
: [fragment(`${alias}.type IN ${list(scope.types).sql}`, scope.types)]),
|
|
157
|
-
...(scope.pathPrefix === null || scope.pathPrefix === ''
|
|
158
|
-
? []
|
|
159
|
-
: [
|
|
160
|
-
fragment(`${alias}.path >= ? AND ${alias}.path < ?`, [
|
|
161
|
-
`${scope.pathPrefix}/`,
|
|
162
|
-
`${scope.pathPrefix}0`,
|
|
163
|
-
]),
|
|
164
|
-
]),
|
|
165
|
-
...(scope.where === null ? [] : [conditioned(alias, scope.where)]),
|
|
62
|
+
...(reach.areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
|
|
166
63
|
]);
|
|
167
64
|
}
|
|
168
65
|
function related(bound, plan) {
|
|
@@ -170,17 +67,17 @@ function related(bound, plan) {
|
|
|
170
67
|
if (plan.hop !== null) {
|
|
171
68
|
parts.push(exists(joined([
|
|
172
69
|
fragment(`SELECT 1 FROM replica_links l JOIN replica_entries m ON m.key = d.key AND m.path = l.target AND ${VISIBLE('m')} WHERE l.doc = d.id AND l.kind = 'field' AND`),
|
|
173
|
-
grouped(
|
|
70
|
+
grouped(sql.placed('l', plan.hop.field, 'link')),
|
|
174
71
|
fragment('AND'),
|
|
175
|
-
grouped(all([scoped('m', plan.hop), reached(bound, 'm')])),
|
|
72
|
+
grouped(all([sql.scoped('m', plan.hop), reached(bound, 'm')])),
|
|
176
73
|
], ' ')));
|
|
177
74
|
}
|
|
178
75
|
if (plan.backlinks !== null) {
|
|
179
76
|
const field = plan.backlinks.field;
|
|
180
77
|
parts.push(exists(joined([
|
|
181
78
|
fragment(`SELECT 1 FROM replica_links l JOIN replica_entries s ON s.id = l.doc AND ${VISIBLE('s')} WHERE l.key = d.key AND l.target = d.path AND l.kind = 'field' AND`),
|
|
182
|
-
...(field === null ? [] : [grouped(
|
|
183
|
-
grouped(all([scoped('s', plan.backlinks), reached(bound, 's')])),
|
|
79
|
+
...(field === null ? [] : [grouped(sql.placed('l', field, 'link')), fragment('AND')]),
|
|
80
|
+
grouped(all([sql.scoped('s', plan.backlinks), reached(bound, 's')])),
|
|
184
81
|
], ' ')));
|
|
185
82
|
}
|
|
186
83
|
return parts;
|
|
@@ -188,11 +85,9 @@ function related(bound, plan) {
|
|
|
188
85
|
function filtered(bound, plan) {
|
|
189
86
|
const keys = bound.rooms.map(({ key }) => key);
|
|
190
87
|
return all([
|
|
191
|
-
fragment(`d.key IN ${
|
|
192
|
-
...(plan.paths === null ? [] : [fragment(`d.path IN ${list(plan.paths).sql}`, plan.paths)]),
|
|
193
|
-
...(plan.folder === null ? [] : [fragment('d.folder = ?', [plan.folder])]),
|
|
88
|
+
fragment(`d.key IN ${listed(keys).sql} AND ${VISIBLE('d')}`, keys),
|
|
194
89
|
reached(bound, 'd'),
|
|
195
|
-
|
|
90
|
+
sql.tests('d', plan),
|
|
196
91
|
...related(bound, plan),
|
|
197
92
|
]);
|
|
198
93
|
}
|
|
@@ -223,108 +118,65 @@ const GROUP_TIES = {
|
|
|
223
118
|
locale: undefined,
|
|
224
119
|
localeOptions: undefined,
|
|
225
120
|
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return { placement: 1, klass: 3, scalar: JSON.stringify(value) };
|
|
237
|
-
}
|
|
238
|
-
function placement(term) {
|
|
239
|
-
const nulls = term.ordering.nulls === 'first' ? 0 : 2;
|
|
240
|
-
return fragment(`CASE WHEN ${term.kind.sql} IS NULL OR ${term.kind.sql} = 'null' THEN ${nulls} ELSE 1 END`, [...term.kind.bind, ...term.kind.bind]);
|
|
241
|
-
}
|
|
242
|
-
function klass(term) {
|
|
243
|
-
return fragment(`CASE ${term.kind.sql} WHEN 'boolean' THEN 1 WHEN 'number' THEN 1 WHEN 'string' THEN 2 WHEN 'array' THEN 3 ELSE 4 END`, term.kind.bind);
|
|
244
|
-
}
|
|
245
|
-
function classed(term, which) {
|
|
246
|
-
const kinds = which === 1 ? "('boolean', 'number')" : which === 2 ? "('string')" : "('array')";
|
|
247
|
-
return fragment(`(CASE WHEN ${term.kind.sql} IN ${kinds} THEN ${term.value.sql} END)`, [
|
|
248
|
-
...term.kind.bind,
|
|
249
|
-
...term.value.bind,
|
|
250
|
-
]);
|
|
251
|
-
}
|
|
252
|
-
function collated(bound, term, which) {
|
|
253
|
-
if (which === 1)
|
|
254
|
-
return '';
|
|
255
|
-
const name = bound.collate(term.ordering, which === 3, term.total === true);
|
|
256
|
-
return name === null ? '' : ` COLLATE ${name}`;
|
|
257
|
-
}
|
|
258
|
-
function sorted(bound, term) {
|
|
259
|
-
const direction = term.ordering.direction === 'desc' ? 'DESC' : 'ASC';
|
|
260
|
-
return joined([
|
|
261
|
-
placement(term),
|
|
262
|
-
fragment(`${klass(term).sql} ${direction}`, klass(term).bind),
|
|
263
|
-
...[1, 2, 3].map((which) => {
|
|
264
|
-
const value = classed(term, which);
|
|
265
|
-
return fragment(`${value.sql}${collated(bound, term, which)} ${direction}`, value.bind);
|
|
266
|
-
}),
|
|
267
|
-
], ', ');
|
|
268
|
-
}
|
|
269
|
-
function beyond(bound, term, value) {
|
|
270
|
-
const held = classOf(value, term.ordering);
|
|
271
|
-
const place = placement(term);
|
|
272
|
-
if (held.placement !== 1) {
|
|
273
|
-
return {
|
|
274
|
-
beyond: fragment(`${place.sql} > ${held.placement}`, place.bind),
|
|
275
|
-
equal: fragment(`${place.sql} = ${held.placement}`, place.bind),
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
const after = term.ordering.direction === 'desc' ? '<' : '>';
|
|
279
|
-
const kind = klass(term);
|
|
280
|
-
const scalar = classed(term, held.klass);
|
|
281
|
-
const collation = collated(bound, term, held.klass);
|
|
282
|
-
return {
|
|
283
|
-
beyond: any([
|
|
284
|
-
fragment(`${place.sql} > 1`, place.bind),
|
|
285
|
-
all([
|
|
286
|
-
fragment(`${place.sql} = 1`, place.bind),
|
|
287
|
-
any([
|
|
288
|
-
fragment(`${kind.sql} ${after} ${held.klass}`, kind.bind),
|
|
289
|
-
all([
|
|
290
|
-
fragment(`${kind.sql} = ${held.klass}`, kind.bind),
|
|
291
|
-
fragment(`${scalar.sql} ${after} ?${collation}`, [...scalar.bind, held.scalar]),
|
|
292
|
-
]),
|
|
293
|
-
]),
|
|
294
|
-
]),
|
|
295
|
-
]),
|
|
296
|
-
equal: all([
|
|
297
|
-
fragment(`${place.sql} = 1 AND ${kind.sql} = ${held.klass}`, [...place.bind, ...kind.bind]),
|
|
298
|
-
fragment(`${scalar.sql} = ?${collation}`, [...scalar.bind, held.scalar]),
|
|
299
|
-
]),
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
function following(parts, tie) {
|
|
303
|
-
let condition = tie;
|
|
304
|
-
for (const part of [...parts].reverse()) {
|
|
305
|
-
condition = any([part.beyond, all([part.equal, condition])]);
|
|
306
|
-
}
|
|
307
|
-
return condition;
|
|
121
|
+
const GREATEST = { ...GROUP_ORDER, direction: 'desc', nulls: 'last' };
|
|
122
|
+
const TIMES = {
|
|
123
|
+
created: { value: 'd.created', instant: 'd.created_instant' },
|
|
124
|
+
updated: { value: "(d.change ->> '$.time')", instant: 'd.changed_instant' },
|
|
125
|
+
};
|
|
126
|
+
function subfolderOf(column, prefix) {
|
|
127
|
+
const root = prefix ?? '';
|
|
128
|
+
const base = root === '' ? '' : `${root}/`;
|
|
129
|
+
const rest = `substr(${column}, length(?) + 1)`;
|
|
130
|
+
return fragment(`(CASE WHEN ${column} = ? THEN NULL WHEN instr(${rest}, '/') = 0 THEN ${column} ELSE ? || substr(${rest}, 1, instr(${rest}, '/') - 1) END)`, [root, base, base, base, base]);
|
|
308
131
|
}
|
|
309
|
-
function keyTerm(
|
|
132
|
+
function keyTerm(key, ordering, index, prefix) {
|
|
310
133
|
if (key.of === 'field') {
|
|
311
134
|
const alias = `o${index}`;
|
|
312
135
|
return {
|
|
313
|
-
term: {
|
|
136
|
+
term: {
|
|
137
|
+
ordering,
|
|
138
|
+
kind: fragment(`${alias}.kind`),
|
|
139
|
+
value: fragment(`${alias}.value`),
|
|
140
|
+
instant: fragment(`${alias}.instant`),
|
|
141
|
+
},
|
|
314
142
|
join: joined([
|
|
315
143
|
fragment(`LEFT JOIN replica_values ${alias} ON ${alias}.doc = d.id AND `),
|
|
316
|
-
grouped(
|
|
144
|
+
grouped(sql.placed(alias, key.field, 'exact')),
|
|
317
145
|
fragment(` AND ${alias}.item = -1 AND ${alias}.kind NOT IN ('object', 'undefined')`),
|
|
318
146
|
], ''),
|
|
319
147
|
};
|
|
320
148
|
}
|
|
149
|
+
if (key.of === 'size') {
|
|
150
|
+
return {
|
|
151
|
+
term: {
|
|
152
|
+
ordering,
|
|
153
|
+
kind: fragment("(CASE WHEN d.bytes IS NULL THEN 'null' ELSE 'number' END)"),
|
|
154
|
+
value: fragment('d.bytes'),
|
|
155
|
+
},
|
|
156
|
+
join: null,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (key.of === 'created' || key.of === 'updated') {
|
|
160
|
+
const { value, instant } = TIMES[key.of];
|
|
161
|
+
return {
|
|
162
|
+
term: {
|
|
163
|
+
ordering,
|
|
164
|
+
kind: fragment(`(CASE WHEN ${value} IS NULL THEN 'null' ELSE 'string' END)`),
|
|
165
|
+
value: fragment(value),
|
|
166
|
+
instant: fragment(instant),
|
|
167
|
+
},
|
|
168
|
+
join: null,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
321
171
|
const expression = key.of === 'room'
|
|
322
|
-
? located(
|
|
172
|
+
? located('d')
|
|
323
173
|
: key.of === 'path'
|
|
324
174
|
? fragment('d.path')
|
|
325
175
|
: key.of === 'type'
|
|
326
176
|
? fragment('d.type')
|
|
327
|
-
:
|
|
177
|
+
: key.of === 'subfolder'
|
|
178
|
+
? subfolderOf('d.folder', prefix)
|
|
179
|
+
: fragment('d.folder');
|
|
328
180
|
return {
|
|
329
181
|
term: {
|
|
330
182
|
ordering,
|
|
@@ -345,12 +197,20 @@ function decoded(kind, value) {
|
|
|
345
197
|
return JSON.parse(value);
|
|
346
198
|
return null;
|
|
347
199
|
}
|
|
200
|
+
function sortedOf(kind, value, instant) {
|
|
201
|
+
return sql.sortValue(kind, decoded(kind, value), instant);
|
|
202
|
+
}
|
|
203
|
+
function sortedValue(row, index) {
|
|
204
|
+
return sortedOf(row[`k${index}`], row[`v${index}`], row[`i${index}`]);
|
|
205
|
+
}
|
|
348
206
|
function decodedKey(key, row, index) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
return
|
|
352
|
-
|
|
353
|
-
|
|
207
|
+
const kind = row[`k${index}`];
|
|
208
|
+
if (key.of === 'field' && (kind === null || kind === undefined))
|
|
209
|
+
return undefined;
|
|
210
|
+
return sortedValue(row, index);
|
|
211
|
+
}
|
|
212
|
+
function instantOf(term) {
|
|
213
|
+
return term.instant ?? fragment('NULL');
|
|
354
214
|
}
|
|
355
215
|
const registered = new WeakMap();
|
|
356
216
|
function hashed(text) {
|
|
@@ -401,9 +261,6 @@ function collating(connection) {
|
|
|
401
261
|
return name;
|
|
402
262
|
};
|
|
403
263
|
}
|
|
404
|
-
function bodied(shape) {
|
|
405
|
-
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
406
|
-
}
|
|
407
264
|
function masked(pointers) {
|
|
408
265
|
const root = { whole: false, members: new Map() };
|
|
409
266
|
for (const pointer of pointers) {
|
|
@@ -455,11 +312,20 @@ function sectionsOf(sections, names) {
|
|
|
455
312
|
return sections;
|
|
456
313
|
return Object.fromEntries(names.flatMap((name) => (Object.hasOwn(sections, name) ? [[name, sections[name]]] : [])));
|
|
457
314
|
}
|
|
458
|
-
function
|
|
315
|
+
function freeParts(room, path, type, content) {
|
|
316
|
+
const definitions = room.composition === undefined ? undefined : definitionsAt(room.composition, path);
|
|
317
|
+
const definition = definitions?.(type === UNTYPED ? undefined : type);
|
|
318
|
+
if (definition === undefined && type !== UNTYPED)
|
|
319
|
+
return null;
|
|
320
|
+
const read = parse(content, path, definition);
|
|
321
|
+
return read.ok ? read.document : null;
|
|
322
|
+
}
|
|
323
|
+
function documentOf(shape, queried, row, linked) {
|
|
324
|
+
const room = queried.locator;
|
|
459
325
|
const path = row.path;
|
|
460
326
|
const type = row.type;
|
|
461
|
-
const fileId = row.file_id
|
|
462
|
-
const revision = row.revision
|
|
327
|
+
const fileId = row.file_id;
|
|
328
|
+
const revision = row.revision;
|
|
463
329
|
if (type === null) {
|
|
464
330
|
const binary = row.binary === null ? null : JSON.parse(row.binary);
|
|
465
331
|
const content = row.content ?? null;
|
|
@@ -472,12 +338,15 @@ function documentOf(shape, room, row, linked) {
|
|
|
472
338
|
binaryFile: binaryRecord(path, binary, revision ?? undefined),
|
|
473
339
|
}),
|
|
474
340
|
...(shape.markdown && content !== null ? { markdown: content } : {}),
|
|
341
|
+
...(shape.change ? { change: changeOf(row) } : {}),
|
|
475
342
|
...(shape.fileId ? { fileId } : {}),
|
|
476
343
|
...(shape.revision ? { revision } : {}),
|
|
344
|
+
...heldFacts(shape, row),
|
|
477
345
|
};
|
|
478
346
|
}
|
|
479
|
-
const
|
|
480
|
-
|
|
347
|
+
const body = shape.sections !== null || shape.preamble
|
|
348
|
+
? freeParts(queried, path, type, row.content)
|
|
349
|
+
: null;
|
|
481
350
|
const fields = JSON.parse(row.fields ?? '{}');
|
|
482
351
|
return {
|
|
483
352
|
room,
|
|
@@ -494,15 +363,21 @@ function documentOf(shape, room, row, linked) {
|
|
|
494
363
|
? {}
|
|
495
364
|
: { sections: sectionsOf(body.sections, shape.sections) }),
|
|
496
365
|
...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
|
|
497
|
-
...(shape.markdown
|
|
366
|
+
...(shape.markdown ? { markdown: row.content } : {}),
|
|
498
367
|
...(shape.backlinks ? { backlinks: linked.get(documentKey(room, path)) ?? [] } : {}),
|
|
499
|
-
...(shape.change
|
|
500
|
-
? {
|
|
501
|
-
change: row.change === null ? null : JSON.parse(row.change),
|
|
502
|
-
}
|
|
503
|
-
: {}),
|
|
368
|
+
...(shape.change ? { change: changeOf(row) } : {}),
|
|
504
369
|
...(shape.fileId ? { fileId } : {}),
|
|
505
370
|
...(shape.revision ? { revision } : {}),
|
|
371
|
+
...heldFacts(shape, row),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function changeOf(row) {
|
|
375
|
+
return row.change === null ? null : JSON.parse(row.change);
|
|
376
|
+
}
|
|
377
|
+
function heldFacts(shape, row) {
|
|
378
|
+
return {
|
|
379
|
+
...(shape.size ? { size: row.bytes === null ? null : Number(row.bytes) } : {}),
|
|
380
|
+
...(shape.created ? { created: row.created } : {}),
|
|
506
381
|
};
|
|
507
382
|
}
|
|
508
383
|
function backlinksOf(connection, bound, rows) {
|
|
@@ -514,8 +389,8 @@ function backlinksOf(connection, bound, rows) {
|
|
|
514
389
|
held.push(row.path);
|
|
515
390
|
byKey.set(key, held);
|
|
516
391
|
}
|
|
392
|
+
const reach = reached(bound, 's');
|
|
517
393
|
for (const [key, targets] of byKey) {
|
|
518
|
-
const reach = reached(bound, 's');
|
|
519
394
|
const room = bound.rooms.find((one) => one.key === key).locator;
|
|
520
395
|
const found = connection.rows(`SELECT target, path, type, field FROM (SELECT l.target, s.path, s.type, l.field, ROW_NUMBER() OVER (PARTITION BY l.target ORDER BY s.path, l.field) AS rank FROM (SELECT DISTINCT doc, target, field FROM replica_links WHERE key = ? AND kind = 'field' AND target IN (SELECT value FROM json_each(?))) l JOIN replica_entries s ON s.id = l.doc AND ${VISIBLE('s')} WHERE ${reach.sql}) WHERE rank <= ? ORDER BY target, path, field`, [key, JSON.stringify(targets), ...reach.bind, MAXIMUM_WINDOW]);
|
|
521
396
|
for (const row of found) {
|
|
@@ -528,38 +403,36 @@ function backlinksOf(connection, bound, rows) {
|
|
|
528
403
|
return linked;
|
|
529
404
|
}
|
|
530
405
|
function documents(connection, bound, plan, shape, budget) {
|
|
531
|
-
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(
|
|
406
|
+
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(key, orderingOf(options), index, plan.root.pathPrefix));
|
|
532
407
|
const filter = filtered(bound, plan);
|
|
533
408
|
const cursor = plan.after?.values ?? null;
|
|
534
|
-
const room = located(
|
|
535
|
-
const
|
|
409
|
+
const room = located('d');
|
|
410
|
+
const after = cursor === null
|
|
536
411
|
? null
|
|
537
412
|
: (() => {
|
|
538
413
|
const key = cursor[shape.orderBy.length];
|
|
539
|
-
return any([
|
|
414
|
+
return sql.following(terms.map(({ term }, index) => sql.beyond(bound.collate, term, cursor[index])), any([
|
|
540
415
|
fragment(`${room.sql} > ?`, [...room.bind, roomOfKey(key)]),
|
|
541
416
|
fragment(`${room.sql} = ? AND d.path > ?`, [
|
|
542
417
|
...room.bind,
|
|
543
418
|
roomOfKey(key),
|
|
544
419
|
pathOfKey(key),
|
|
545
420
|
]),
|
|
546
|
-
]);
|
|
421
|
+
]));
|
|
547
422
|
})();
|
|
548
|
-
const after = cursor === null || tie === null
|
|
549
|
-
? null
|
|
550
|
-
: following(terms.map(({ term }, index) => beyond(bound, term, cursor[index])), tie);
|
|
551
423
|
const selected = joined([
|
|
552
|
-
fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id'),
|
|
424
|
+
fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id, d.bytes, d.created'),
|
|
553
425
|
...terms.map(({ term }, index) => joined([
|
|
554
426
|
fragment(`, ${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
555
427
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
428
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
556
429
|
], '')),
|
|
557
430
|
fragment(' FROM replica_entries d'),
|
|
558
431
|
...terms.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
|
|
559
432
|
fragment(' WHERE '),
|
|
560
433
|
after === null ? filter : all([filter, after]),
|
|
561
434
|
fragment(' ORDER BY '),
|
|
562
|
-
joined([...terms.map(({ term }) => sorted(bound, term)), room, fragment('d.path')], ', '),
|
|
435
|
+
joined([...terms.map(({ term }) => sql.sorted(bound.collate, term)), room, fragment('d.path')], ', '),
|
|
563
436
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
564
437
|
], '');
|
|
565
438
|
const rows = connection.rows(selected.sql, selected.bind);
|
|
@@ -567,8 +440,9 @@ function documents(connection, bound, plan, shape, budget) {
|
|
|
567
440
|
const kept = bodied(shape) ? withContent(connection, windowed, budget) : windowed;
|
|
568
441
|
const cut = kept.length;
|
|
569
442
|
const roomOf = new Map(bound.rooms.map(({ key, locator }) => [key, locator]));
|
|
443
|
+
const queried = new Map(bound.rooms.map((one) => [one.key, one]));
|
|
570
444
|
const linked = shape.backlinks ? backlinksOf(connection, bound, kept) : new Map();
|
|
571
|
-
const found = kept.map((row) => documentOf(shape,
|
|
445
|
+
const found = kept.map((row) => documentOf(shape, queried.get(Number(row.key)), row, linked));
|
|
572
446
|
const last = kept.at(-1);
|
|
573
447
|
const cursorAt = (row) => cursorOf([
|
|
574
448
|
...shape.orderBy.map(({ key }, index) => decodedKey(key, row, index)),
|
|
@@ -598,7 +472,7 @@ function withContent(connection, rows, budget) {
|
|
|
598
472
|
return rows;
|
|
599
473
|
const ids = rows.map((row) => row.id);
|
|
600
474
|
const sizes = new Map(connection
|
|
601
|
-
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${
|
|
475
|
+
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${listed(ids).sql}`, ids)
|
|
602
476
|
.map((row) => [row.id, Number(row.size ?? 0)]));
|
|
603
477
|
let cut = rows.length;
|
|
604
478
|
let spent = 0;
|
|
@@ -613,125 +487,213 @@ function withContent(connection, rows, budget) {
|
|
|
613
487
|
const kept = rows.slice(0, cut);
|
|
614
488
|
const read = kept.map((row) => row.id);
|
|
615
489
|
const held = new Map(connection
|
|
616
|
-
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${
|
|
490
|
+
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${listed(read).sql}`, read)
|
|
617
491
|
.map((row) => [row.id, row.content]));
|
|
618
492
|
return kept.map((row) => ({ ...row, content: held.get(row.id) ?? null }));
|
|
619
493
|
}
|
|
620
|
-
function storedFolders(bound, plan) {
|
|
621
|
-
if (plan.paths !== null || plan.
|
|
494
|
+
function storedFolders(bound, plan, subfolder) {
|
|
495
|
+
if (plan.paths !== null || plan.fileIds !== null)
|
|
496
|
+
return null;
|
|
497
|
+
if (plan.hop !== null || plan.backlinks !== null)
|
|
498
|
+
return null;
|
|
499
|
+
if (subfolder && plan.folder !== null)
|
|
622
500
|
return null;
|
|
623
501
|
const whole = bound.rooms.filter(({ locator }) => bound.reach === null || bound.reach.rooms.includes(locator));
|
|
624
502
|
if (whole.length === 0)
|
|
625
503
|
return null;
|
|
626
504
|
const keys = whole.map(({ key }) => key);
|
|
627
|
-
const prefix = plan.root.pathPrefix;
|
|
628
|
-
const
|
|
505
|
+
const prefix = plan.root.pathPrefix ?? '';
|
|
506
|
+
const outside = (bound.reach?.areas ?? []).flatMap((area) => {
|
|
629
507
|
const key = bound.keys.get(area.room);
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
key,
|
|
635
|
-
area.path,
|
|
636
|
-
`${area.path}/`,
|
|
637
|
-
`${area.path}0`,
|
|
638
|
-
]),
|
|
639
|
-
];
|
|
508
|
+
if (key === undefined)
|
|
509
|
+
return [];
|
|
510
|
+
const fence = fenced('f', key, area, true);
|
|
511
|
+
return [fragment(`NOT ${grouped(fence).sql}`, fence.bind)];
|
|
640
512
|
});
|
|
513
|
+
const under = prefix === ''
|
|
514
|
+
? subfolder
|
|
515
|
+
? [fragment("f.path <> ''")]
|
|
516
|
+
: []
|
|
517
|
+
: [
|
|
518
|
+
subfolder
|
|
519
|
+
? fragment('(f.path >= ? AND f.path < ?)', [`${prefix}/`, `${prefix}0`])
|
|
520
|
+
: within('f', prefix),
|
|
521
|
+
];
|
|
641
522
|
return all([
|
|
642
|
-
fragment(`f.key IN ${
|
|
643
|
-
...
|
|
644
|
-
...
|
|
645
|
-
|
|
646
|
-
: [
|
|
647
|
-
fragment('(f.path = ? OR (f.path >= ? AND f.path < ?))', [
|
|
648
|
-
prefix,
|
|
649
|
-
`${prefix}/`,
|
|
650
|
-
`${prefix}0`,
|
|
651
|
-
]),
|
|
652
|
-
]),
|
|
523
|
+
fragment(`f.key IN ${listed(keys).sql} AND f.layer = 0 AND f.present = 1`, keys),
|
|
524
|
+
...outside,
|
|
525
|
+
...under,
|
|
526
|
+
...(plan.outside ?? []).map((folder) => fragment(`NOT ${within('f', folder).sql}`, within('f', folder).bind)),
|
|
653
527
|
...(plan.folder === null ? [] : [fragment('f.path = ?', [plan.folder])]),
|
|
654
528
|
]);
|
|
655
529
|
}
|
|
530
|
+
const SIZES = [
|
|
531
|
+
'CASE WHEN d.binary IS NULL THEN 1 ELSE 0 END AS dn',
|
|
532
|
+
'CASE WHEN d.binary IS NULL THEN d.bytes ELSE 0 END AS ds',
|
|
533
|
+
'CASE WHEN d.binary IS NULL THEN 0 ELSE 1 END AS fn',
|
|
534
|
+
'CASE WHEN d.binary IS NULL THEN 0 ELSE d.bytes END AS fs',
|
|
535
|
+
];
|
|
536
|
+
const LATEST = "d.changed_instant || ' ' || (d.change ->> '$.time') AS u";
|
|
537
|
+
function greatestTerm(ordering) {
|
|
538
|
+
return { ordering, kind: fragment('gk'), value: fragment('gv'), instant: fragment('gi') };
|
|
539
|
+
}
|
|
540
|
+
function latestOf(held) {
|
|
541
|
+
if (typeof held !== 'string')
|
|
542
|
+
return null;
|
|
543
|
+
return held.slice(held.indexOf(' ') + 1);
|
|
544
|
+
}
|
|
656
545
|
function groups(connection, bound, plan, shape) {
|
|
546
|
+
const prefix = plan.root.pathPrefix;
|
|
657
547
|
const keyed = shape.groupBy.map((key, index) => {
|
|
658
|
-
const { term, join } = keyTerm(
|
|
548
|
+
const { term, join } = keyTerm(key, GROUP_ORDER, index, prefix);
|
|
659
549
|
const kind = fragment(`COALESCE(${term.kind.sql}, 'null')`, term.kind.bind);
|
|
660
550
|
return { term: { ...term, kind }, join };
|
|
661
551
|
});
|
|
552
|
+
const ordering = shape.orderBy.map(({ by }) => by);
|
|
553
|
+
const sized = shape.size || ordering.includes('size');
|
|
554
|
+
const dated = shape.updated || ordering.includes('updated');
|
|
555
|
+
const greatest = shape.greatest === null
|
|
556
|
+
? null
|
|
557
|
+
: keyTerm({ of: 'field', field: shape.greatest }, GREATEST, keyed.length, prefix);
|
|
662
558
|
const filter = filtered(bound, plan);
|
|
663
559
|
const inner = joined([
|
|
664
560
|
fragment('SELECT '),
|
|
665
561
|
joined(keyed.map(({ term }, index) => joined([
|
|
666
562
|
fragment(`${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
667
563
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
564
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
668
565
|
], '')), ', '),
|
|
669
|
-
fragment(', 1 AS n
|
|
670
|
-
...
|
|
566
|
+
fragment(['', '1 AS n', ...(sized ? SIZES : []), ...(dated ? [LATEST] : [])].join(', ')),
|
|
567
|
+
...(greatest === null
|
|
568
|
+
? []
|
|
569
|
+
: [
|
|
570
|
+
fragment(`, ${greatest.term.kind.sql} AS gk`, greatest.term.kind.bind),
|
|
571
|
+
fragment(`, ${greatest.term.value.sql} AS gv`, greatest.term.value.bind),
|
|
572
|
+
fragment(`, ${instantOf(greatest.term).sql} AS gi`, instantOf(greatest.term).bind),
|
|
573
|
+
]),
|
|
574
|
+
fragment(' FROM replica_entries d'),
|
|
575
|
+
...[...keyed, ...(greatest === null ? [] : [greatest])].flatMap(({ join }) => join === null ? [] : [fragment(' '), join]),
|
|
671
576
|
fragment(' WHERE '),
|
|
672
577
|
filter,
|
|
673
578
|
], '');
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
579
|
+
const only = shape.groupBy.length === 1 ? shape.groupBy[0].of : null;
|
|
580
|
+
const subfolder = only === 'subfolder';
|
|
581
|
+
const folders = only === 'folder' || subfolder ? storedFolders(bound, plan, subfolder) : null;
|
|
582
|
+
const folderKey = subfolder ? subfolderOf('f.path', prefix) : fragment('f.path');
|
|
583
|
+
const zeros = [
|
|
584
|
+
...(sized ? ['0', '0', '0', '0'] : []),
|
|
585
|
+
...(dated ? ['NULL'] : []),
|
|
586
|
+
...(greatest === null ? [] : ['NULL', 'NULL', 'NULL']),
|
|
587
|
+
];
|
|
677
588
|
const source = folders === null
|
|
678
589
|
? inner
|
|
679
590
|
: joined([
|
|
680
591
|
inner,
|
|
681
|
-
fragment(
|
|
592
|
+
fragment(` UNION ALL SELECT 'string' AS k0, ${folderKey.sql} AS v0, NULL AS i0, 0 AS n${zeros.map((one) => `, ${one}`).join('')} FROM replica_folders f WHERE `, folderKey.bind),
|
|
682
593
|
folders,
|
|
683
594
|
], '');
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
595
|
+
const held = keyed
|
|
596
|
+
.map((_, index) => `k${index}, i${index}, CASE WHEN i${index} IS NULL THEN v${index} END`)
|
|
597
|
+
.join(', ');
|
|
598
|
+
const ranked = greatest === null
|
|
599
|
+
? source
|
|
600
|
+
: joined([
|
|
601
|
+
fragment(`SELECT *, ROW_NUMBER() OVER (PARTITION BY ${held} ORDER BY `),
|
|
602
|
+
sql.sorted(bound.collate, greatestTerm(GREATEST)),
|
|
603
|
+
fragment(', gv) AS gr FROM ('),
|
|
604
|
+
source,
|
|
605
|
+
fragment(')'),
|
|
606
|
+
], '');
|
|
607
|
+
const columns = [
|
|
608
|
+
...keyed.map((_, index) => `k${index}, min(v${index}) AS v${index}, i${index}`),
|
|
609
|
+
'sum(n) AS n',
|
|
610
|
+
...(sized ? ['sum(dn) AS dn', 'sum(ds) AS ds', 'sum(fn) AS fn', 'sum(fs) AS fs'] : []),
|
|
611
|
+
...(dated ? ['max(u) AS u'] : []),
|
|
612
|
+
...(greatest === null
|
|
613
|
+
? []
|
|
614
|
+
: ['gk', 'gv', 'gi'].map((column) => `min(CASE WHEN gr = 1 THEN ${column} END) AS ${column}`)),
|
|
615
|
+
].join(', ');
|
|
616
|
+
const outer = (index, order) => ({
|
|
617
|
+
ordering: order,
|
|
692
618
|
kind: fragment(`k${index}`),
|
|
693
619
|
value: fragment(`v${index}`),
|
|
694
620
|
});
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
621
|
+
const termOf = (by, order) => {
|
|
622
|
+
if (by === 'count')
|
|
623
|
+
return { ordering: order, kind: fragment("'number'"), value: fragment('n') };
|
|
624
|
+
if (by === 'size') {
|
|
625
|
+
return { ordering: order, kind: fragment("'number'"), value: fragment('(ds + fs)') };
|
|
626
|
+
}
|
|
627
|
+
if (by === 'updated') {
|
|
628
|
+
return {
|
|
629
|
+
ordering: order,
|
|
630
|
+
kind: fragment("(CASE WHEN u IS NULL THEN 'null' ELSE 'string' END)"),
|
|
631
|
+
value: fragment("substr(u, instr(u, ' ') + 1)"),
|
|
632
|
+
instant: fragment("substr(u, 1, instr(u, ' ') - 1)"),
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
if (by === 'greatest')
|
|
636
|
+
return greatestTerm(order);
|
|
637
|
+
return { ...outer(by, order), instant: fragment(`i${by}`) };
|
|
638
|
+
};
|
|
639
|
+
const valueOf = (row, by) => {
|
|
640
|
+
if (by === 'count')
|
|
641
|
+
return Number(row.n);
|
|
642
|
+
if (by === 'size')
|
|
643
|
+
return Number(row.ds) + Number(row.fs);
|
|
644
|
+
if (by === 'updated') {
|
|
645
|
+
const text = latestOf(row.u);
|
|
646
|
+
return text === null ? null : sql.sortValue('string', text, row.u.split(' ')[0]);
|
|
647
|
+
}
|
|
648
|
+
if (by === 'greatest')
|
|
649
|
+
return sortedOf(row.gk, row.gv, row.gi);
|
|
650
|
+
return sortedValue(row, by);
|
|
651
|
+
};
|
|
652
|
+
const ordered = shape.orderBy.map(({ by, direction, nulls }) => termOf(by, { ...GROUP_ORDER, direction, nulls: nulls ?? GROUP_ORDER.nulls }));
|
|
698
653
|
const ties = keyed.flatMap((_, index) => [
|
|
699
654
|
{ ...outer(index, GROUP_TIES), total: true },
|
|
700
655
|
{ ordering: GROUP_TIES, kind: fragment("'string'"), value: fragment(`k${index}`) },
|
|
701
656
|
]);
|
|
702
657
|
const cursor = plan.after?.values ?? null;
|
|
703
|
-
const
|
|
704
|
-
? null
|
|
705
|
-
: cursor[shape.orderBy.length].flatMap((value) => [
|
|
706
|
-
value,
|
|
707
|
-
kindOf(value ?? null),
|
|
708
|
-
]);
|
|
709
|
-
const after = cursor === null || tieValues === null
|
|
658
|
+
const after = cursor === null
|
|
710
659
|
? null
|
|
711
|
-
:
|
|
712
|
-
|
|
660
|
+
: (() => {
|
|
661
|
+
const tieValues = cursor[shape.orderBy.length].flatMap((value) => [value, heldKind(value ?? null)]);
|
|
662
|
+
return sql.following(ordered.map((term, index) => sql.beyond(bound.collate, term, cursor[index])), sql.following(ties
|
|
663
|
+
.slice(0, -1)
|
|
664
|
+
.map((term, index) => sql.beyond(bound.collate, term, tieValues[index])), sql.beyond(bound.collate, ties.at(-1), tieValues.at(-1)).beyond));
|
|
665
|
+
})();
|
|
666
|
+
const grouping = joined([fragment(`SELECT ${columns} FROM (`), ranked, fragment(`) GROUP BY ${held}`)], '');
|
|
713
667
|
const windowed = joined([
|
|
714
668
|
fragment('SELECT * FROM ('),
|
|
715
669
|
grouping,
|
|
716
670
|
fragment(')'),
|
|
717
671
|
...(after === null ? [] : [fragment(' WHERE '), after]),
|
|
718
672
|
fragment(' ORDER BY '),
|
|
719
|
-
joined([...ordered, ...ties].map((term) => sorted(bound, term)), ', '),
|
|
673
|
+
joined([...ordered, ...ties].map((term) => sql.sorted(bound.collate, term)), ', '),
|
|
720
674
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
721
675
|
], '');
|
|
722
676
|
const rows = connection.rows(windowed.sql, windowed.bind);
|
|
723
677
|
const kept = plan.limit === null ? rows : rows.slice(0, plan.limit);
|
|
724
678
|
const keyOf = (row) => keyed.map((_, index) => decoded(row[`k${index}`], row[`v${index}`]));
|
|
725
|
-
const found = kept.map((row) => ({
|
|
679
|
+
const found = kept.map((row) => ({
|
|
680
|
+
key: keyOf(row),
|
|
681
|
+
count: Number(row.n),
|
|
682
|
+
...(shape.size
|
|
683
|
+
? {
|
|
684
|
+
documents: { count: Number(row.dn), size: Number(row.ds) },
|
|
685
|
+
files: { count: Number(row.fn), size: Number(row.fs) },
|
|
686
|
+
}
|
|
687
|
+
: {}),
|
|
688
|
+
...(shape.updated ? { updated: latestOf(row.u) } : {}),
|
|
689
|
+
...(shape.greatest === null ? {} : { greatest: decoded(row.gk, row.gv) }),
|
|
690
|
+
}));
|
|
726
691
|
const last = kept.at(-1);
|
|
727
692
|
const next = plan.limit === null || rows.length <= plan.limit
|
|
728
693
|
? undefined
|
|
729
694
|
: last === undefined
|
|
730
695
|
? (plan.after?.text ?? FROM_START)
|
|
731
|
-
: cursorOf([
|
|
732
|
-
...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) : decoded(last[`k${by}`], last[`v${by}`])),
|
|
733
|
-
keyOf(last),
|
|
734
|
-
]);
|
|
696
|
+
: cursorOf([...shape.orderBy.map(({ by }) => valueOf(last, by)), keyOf(last)]);
|
|
735
697
|
const total = plan.total
|
|
736
698
|
? Number(connection.rows(joined([fragment('SELECT count(*) AS held FROM ('), grouping, fragment(')')], '').sql, grouping.bind)[0].held)
|
|
737
699
|
: undefined;
|
|
@@ -749,9 +711,6 @@ function boundOf(connection, plan, rooms) {
|
|
|
749
711
|
collate: collating(connection),
|
|
750
712
|
};
|
|
751
713
|
}
|
|
752
|
-
export function selected(connection, plan, rooms) {
|
|
753
|
-
return filtered(boundOf(connection, plan, rooms), plan);
|
|
754
|
-
}
|
|
755
714
|
export function answerQuery(connection, plan, rooms, budget = null) {
|
|
756
715
|
const bound = boundOf(connection, plan, rooms);
|
|
757
716
|
if (rooms.length === 0) {
|