@agentshouse/mdruntime 0.1.0-c2e8a6f99866d → 0.1.0-c2ef9303a058d
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 +11 -9
- package/dist/adapter.d.ts +95 -67
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +5 -11
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +36 -47
- package/dist/binary.d.ts +0 -5
- package/dist/binary.d.ts.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/bootstrap.d.ts +17 -9
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +143 -94
- package/dist/chain.d.ts +2 -1
- package/dist/chain.d.ts.map +1 -1
- package/dist/change-set.d.ts +1 -7
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +1 -4
- package/dist/client.d.ts +15 -12
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +56 -8
- package/dist/collection.d.ts +7 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +24 -19
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +117 -243
- package/dist/connection.d.ts +14 -12
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1466 -1431
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/{chat/context.js → context.js} +1 -4
- package/dist/conversation.d.ts +18 -15
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +273 -185
- package/dist/durability.d.ts +6 -4
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +3 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +17 -8
- package/dist/file.d.ts +2 -11
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +6 -4
- package/dist/host.d.ts +3 -7
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +55 -38
- package/dist/identity.d.ts +0 -2
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -6
- package/dist/index.d.ts +15 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/job.d.ts +14 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +28 -0
- package/dist/live.d.ts +2 -1
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +9 -5
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +12 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +209 -101
- package/dist/messages.d.ts +28 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +89 -0
- package/dist/model.d.ts +8 -8
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +7 -13
- package/dist/observation.d.ts +3 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +8 -3
- package/dist/outcome.d.ts +13 -6
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +16 -5
- package/dist/overlay.js +1 -1
- package/dist/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -0
- package/dist/query.d.ts +10 -206
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -494
- package/dist/reach.d.ts +14 -9
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +84 -72
- package/dist/react.d.ts +3 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/readiness.d.ts +14 -17
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +2 -8
- package/dist/recovery.d.ts +1 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +26 -0
- package/dist/refresh.d.ts +10 -4
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +88 -30
- package/dist/replica.d.ts +11 -8
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +30 -15
- package/dist/room-draft.d.ts +22 -5
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +131 -51
- package/dist/rows.d.ts +18 -16
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +45 -153
- package/dist/save.d.ts +2 -0
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +13 -7
- package/dist/search.d.ts +12 -95
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +10 -349
- package/dist/shared.d.ts +0 -1
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +0 -10
- package/dist/status.d.ts +4 -3
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -1
- package/dist/store.d.ts +25 -62
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +43 -282
- package/dist/stored.d.ts +12 -26
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +189 -226
- package/dist/tables.d.ts +55 -48
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +107 -57
- 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 +39 -97
- 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 +14 -9
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +5 -3
- package/package.json +14 -18
- package/AGENTS.md +0 -312
- package/dist/chat/acts.d.ts +0 -33
- package/dist/chat/acts.d.ts.map +0 -1
- package/dist/chat/acts.js +0 -36
- package/dist/chat/context.d.ts +0 -3
- package/dist/chat/context.d.ts.map +0 -1
- package/dist/chat/earlier.d.ts +0 -10
- package/dist/chat/earlier.d.ts.map +0 -1
- package/dist/chat/earlier.js +0 -54
- package/dist/chat/house.d.ts +0 -25
- package/dist/chat/house.d.ts.map +0 -1
- package/dist/chat/house.js +0 -38
- package/dist/chat/input.d.ts +0 -11
- package/dist/chat/input.d.ts.map +0 -1
- package/dist/chat/input.js +0 -92
- package/dist/chat/kit.d.ts +0 -61
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -28
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -103
- package/dist/chat/markdown.d.ts +0 -21
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -246
- package/dist/chat/message.d.ts +0 -7
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -244
- package/dist/chat/messages.d.ts +0 -30
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -198
- package/dist/chat/name.d.ts +0 -7
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -12
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -19
- package/dist/chat/overlay.d.ts +0 -32
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -103
- package/dist/chat/parts.d.ts +0 -58
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -120
- package/dist/chat/pieces.d.ts +0 -62
- package/dist/chat/pieces.d.ts.map +0 -1
- package/dist/chat/pieces.js +0 -1110
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -70
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -635
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -69
- package/dist/chat/questions.d.ts +0 -31
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/questions.js +0 -111
- package/dist/chat/read.d.ts +0 -5
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/read.js +0 -13
- package/dist/chat/ready.d.ts +0 -37
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -93
- package/dist/chat/room-file.d.ts +0 -8
- package/dist/chat/room-file.d.ts.map +0 -1
- package/dist/chat/room-file.js +0 -72
- package/dist/chat/steady.d.ts +0 -3
- package/dist/chat/steady.d.ts.map +0 -1
- package/dist/chat/steady.js +0 -26
- package/dist/chat/style.d.ts +0 -4
- package/dist/chat/style.d.ts.map +0 -1
- package/dist/chat/style.js +0 -306
- package/dist/chat.d.ts +0 -15
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js +0 -8
- package/dist/reads.d.ts +0 -11
- package/dist/reads.d.ts.map +0 -1
- package/dist/reads.js +0 -98
package/dist/collection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAErF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAIpD,eAAO,MAAM,QAAQ,EAAE,eAAwE,CAAC;AAEhG,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE;QAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,GAAG,IAAI,CAAC;IAClE,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAc3F,wBAAgB,UAAU,CAAC,WAAW,EAAE,WAAW,GAAG,QAAQ,CAS7D;AAED,wBAAgB,QAAQ,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAKxE;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAI5E;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACtC,WAAW,EAAE,WAAW,GACvB,UAAU,CAcZ;AAED,wBAAiB,WAAW,CAC1B,UAAU,EAAE,UAAU,GACrB,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAQhE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,MAAM,CAEvD;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAE3D"}
|
package/dist/collection.js
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import { parse, VERSION } from '@agentshouse/mdmodel';
|
|
2
2
|
import { beneath, normalize } from './paths.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { hashed } from './observation.js';
|
|
4
|
+
import { sortedKeys } from './shared.js';
|
|
5
5
|
export const MD_MODEL = { identity: '@agentshouse/mdmodel', version: VERSION };
|
|
6
|
+
const typesPrinted = new WeakMap();
|
|
7
|
+
function typesPrint(model) {
|
|
8
|
+
if (model.status === 'unavailable')
|
|
9
|
+
return '';
|
|
10
|
+
let print = typesPrinted.get(model.types);
|
|
11
|
+
if (print === undefined) {
|
|
12
|
+
print = hashed(JSON.stringify(model.types)).toString(36);
|
|
13
|
+
typesPrinted.set(model.types, print);
|
|
14
|
+
}
|
|
15
|
+
return print;
|
|
16
|
+
}
|
|
17
|
+
export function composedOf(composition) {
|
|
18
|
+
const { model, selection } = composition;
|
|
19
|
+
return {
|
|
20
|
+
composition,
|
|
21
|
+
fingerprint: JSON.stringify([
|
|
22
|
+
typesPrint(model),
|
|
23
|
+
selection === null ? null : [selection.roots, typesPrint(selection.model)],
|
|
24
|
+
]),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
6
27
|
export function selected(composition, path) {
|
|
7
28
|
return (composition.selection !== null &&
|
|
8
29
|
composition.selection.roots.some((root) => beneath(root, path)));
|
|
@@ -39,25 +60,9 @@ export function* documentsOf(collection) {
|
|
|
39
60
|
}
|
|
40
61
|
})();
|
|
41
62
|
}
|
|
42
|
-
export function* unreadable(text, paths = null) {
|
|
43
|
-
const named = paths ?? (yield* keysIn(text));
|
|
44
|
-
let least = null;
|
|
45
|
-
for (const path of named) {
|
|
46
|
-
const content = Object.hasOwn(text, path) ? text[path] : undefined;
|
|
47
|
-
if (content === undefined)
|
|
48
|
-
continue;
|
|
49
|
-
const read = parse(content, path);
|
|
50
|
-
if (!read.ok && (least === null || path < least.path)) {
|
|
51
|
-
least = { path, refused: { kind: 'refused', refusal: read.refusal } };
|
|
52
|
-
}
|
|
53
|
-
yield ROW_WEIGHT + content.length;
|
|
54
|
-
}
|
|
55
|
-
return least?.refused ?? null;
|
|
56
|
-
}
|
|
57
63
|
export function validRoot(root) {
|
|
58
64
|
return typeof root === 'string' && normalize(root) === root;
|
|
59
65
|
}
|
|
60
66
|
export function available(composition) {
|
|
61
|
-
return
|
|
62
|
-
(composition.selection === null || composition.selection.model.status !== 'unavailable'));
|
|
67
|
+
return composition.model.status !== 'unavailable';
|
|
63
68
|
}
|
package/dist/compile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,IAAI,EAAuC,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAG1D,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AA+a3E,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAkUjE,wBAAgB,WAAW,CACzB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,MAAM,GAAE,MAAM,GAAG,IAAW,GAC3B,WAAW,CAUb"}
|
package/dist/compile.js
CHANGED
|
@@ -1,107 +1,36 @@
|
|
|
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 { documentKey,
|
|
4
|
-
import {
|
|
3
|
+
import { documentKey, pathOfKey, roomOfKey } from './rows.js';
|
|
4
|
+
import { FROM_START, MAXIMUM_WINDOW } from './windows.js';
|
|
5
|
+
const { all, any, exists, fragment, grouped, joined, listed } = sql;
|
|
5
6
|
const VISIBLE = (alias) => `${alias}.visible = 1 AND ${alias}.present = 1 AND ${alias}.queried = 1`;
|
|
6
|
-
function
|
|
7
|
-
return
|
|
7
|
+
function located(alias) {
|
|
8
|
+
return fragment(`(SELECT k.locator FROM replica_keys k WHERE k.key = ${alias}.key)`);
|
|
8
9
|
}
|
|
9
|
-
function
|
|
10
|
-
return {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function all(parts) {
|
|
16
|
-
return parts.length === 0 ? fragment('1') : joined(parts.map(grouped), ' AND ');
|
|
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 heldValues(doc, field, alias, where) {
|
|
44
|
-
return fragment(`SELECT 1 FROM replica_values ${alias} WHERE ${alias}.doc = ${doc}.id AND ${alias}.form = ? AND (${where.sql})`, [field, ...where.bind]);
|
|
45
|
-
}
|
|
46
|
-
function exists(part) {
|
|
47
|
-
return { sql: `EXISTS (${part.sql})`, bind: part.bind };
|
|
48
|
-
}
|
|
49
|
-
function matching(held, doc, field, values) {
|
|
50
|
-
return any(values.map((value) => any([
|
|
51
|
-
same(held, value),
|
|
52
|
-
all([
|
|
53
|
-
fragment(`${held}.kind = 'array'`),
|
|
54
|
-
exists(heldValues(doc, field, 'i', all([fragment(`i.held = ${held}.held AND i.item >= 0`), same('i', value)]))),
|
|
55
|
-
]),
|
|
56
|
-
])));
|
|
57
|
-
}
|
|
58
|
-
function tested(doc, field, test) {
|
|
59
|
-
const reached = exists(heldValues(doc, field, 'r', fragment('r.item = -1')));
|
|
60
|
-
if (test.op === 'exists')
|
|
61
|
-
return test.exists ? reached : fragment(`NOT ${reached.sql}`, reached.bind);
|
|
62
|
-
if (test.op === 'eq' || test.op === 'in') {
|
|
63
|
-
const values = test.op === 'eq' ? [test.value] : test.values;
|
|
64
|
-
return exists(heldValues(doc, field, 'v', any(values.map((value) => same('v', value)))));
|
|
65
|
-
}
|
|
66
|
-
if (test.op === 'ne' || test.op === 'nin') {
|
|
67
|
-
const values = test.op === 'ne' ? [test.value] : test.values;
|
|
68
|
-
const unmatched = matching('h', doc, field, values);
|
|
69
|
-
return any([
|
|
70
|
-
fragment(`NOT ${reached.sql}`, reached.bind),
|
|
71
|
-
exists(heldValues(doc, field, 'h', all([fragment('h.item = -1'), fragment(`NOT (${unmatched.sql})`, unmatched.bind)]))),
|
|
72
|
-
]);
|
|
73
|
-
}
|
|
74
|
-
if (test.op === 'range') {
|
|
75
|
-
return exists(heldValues(doc, field, 'v', all([
|
|
76
|
-
fragment('v.item = -1'),
|
|
77
|
-
...test.bounds.map(([operator, bound]) => {
|
|
78
|
-
const held = sqlValue(bound);
|
|
79
|
-
return fragment(`v.kind = ? AND v.value ${BOUNDS[operator]} ?`, [
|
|
80
|
-
held.kind,
|
|
81
|
-
held.value,
|
|
82
|
-
]);
|
|
83
|
-
}),
|
|
84
|
-
])));
|
|
85
|
-
}
|
|
86
|
-
if (test.op === 'all') {
|
|
87
|
-
return exists(heldValues(doc, field, 'h', all([
|
|
88
|
-
fragment("h.item = -1 AND h.kind = 'array'"),
|
|
89
|
-
...test.values.map((value) => exists(heldValues(doc, field, 'i', all([fragment('i.held = h.held AND i.item >= 0'), same('i', value)])))),
|
|
90
|
-
])));
|
|
91
|
-
}
|
|
92
|
-
return exists(heldValues(doc, field, 'v', fragment("v.item = -1 AND v.kind = 'array' AND v.size = ?", [test.size])));
|
|
10
|
+
function within(alias, path) {
|
|
11
|
+
return fragment(`(${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [
|
|
12
|
+
path,
|
|
13
|
+
`${path}/`,
|
|
14
|
+
`${path}0`,
|
|
15
|
+
]);
|
|
93
16
|
}
|
|
94
|
-
function
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return
|
|
99
|
-
|
|
17
|
+
function ancestors(area) {
|
|
18
|
+
const depth = area.path.split('/').length;
|
|
19
|
+
return area.open.flatMap((open) => {
|
|
20
|
+
const segments = open.split('/');
|
|
21
|
+
return segments.slice(depth - 1, -1).map((_, at) => segments.slice(0, depth + at).join('/'));
|
|
22
|
+
});
|
|
100
23
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
24
|
+
function fenced(alias, key, area, folders) {
|
|
25
|
+
const opened = any([
|
|
26
|
+
...area.open.map((open) => within(alias, open)),
|
|
27
|
+
...(folders ? ancestors(area).map((path) => fragment(`${alias}.path = ?`, [path])) : []),
|
|
28
|
+
]);
|
|
29
|
+
return all([
|
|
30
|
+
fragment(`${alias}.key = ?`, [key]),
|
|
31
|
+
within(alias, area.path),
|
|
32
|
+
fragment(`NOT ${grouped(opened).sql}`, opened.bind),
|
|
33
|
+
]);
|
|
105
34
|
}
|
|
106
35
|
function reached(bound, alias) {
|
|
107
36
|
const reach = bound.reach;
|
|
@@ -115,44 +44,38 @@ function reached(bound, alias) {
|
|
|
115
44
|
const key = bound.keys.get(roomOfKey(held));
|
|
116
45
|
return key === undefined ? [] : [[key, pathOfKey(held)]];
|
|
117
46
|
});
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
: [
|
|
123
|
-
fragment(`EXISTS (SELECT 1 FROM json_each(?) j WHERE j.value ->> 0 = ${alias}.key AND j.value ->> 1 = ${alias}.path)`, [JSON.stringify(documents)]),
|
|
124
|
-
]),
|
|
125
|
-
]);
|
|
126
|
-
}
|
|
127
|
-
function scoped(alias, scope) {
|
|
47
|
+
const outside = any(reach.areas.flatMap((area) => {
|
|
48
|
+
const key = bound.keys.get(area.room);
|
|
49
|
+
return key === undefined ? [] : [fenced(alias, key, area, false)];
|
|
50
|
+
}));
|
|
128
51
|
return all([
|
|
129
|
-
|
|
130
|
-
? []
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
fragment(`${alias}.path >= ? AND ${alias}.path < ?`, [
|
|
136
|
-
`${scope.pathPrefix}/`,
|
|
137
|
-
`${scope.pathPrefix}0`,
|
|
52
|
+
any([
|
|
53
|
+
...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${listed(rooms).sql}`, rooms)]),
|
|
54
|
+
...(documents.length === 0
|
|
55
|
+
? []
|
|
56
|
+
: [
|
|
57
|
+
fragment(`EXISTS (SELECT 1 FROM json_each(?) j WHERE j.value ->> 0 = ${alias}.key AND j.value ->> 1 = ${alias}.path)`, [JSON.stringify(documents)]),
|
|
138
58
|
]),
|
|
139
|
-
|
|
140
|
-
...(
|
|
59
|
+
]),
|
|
60
|
+
...(reach.areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
|
|
141
61
|
]);
|
|
142
62
|
}
|
|
143
63
|
function related(bound, plan) {
|
|
144
64
|
const parts = [];
|
|
145
65
|
if (plan.hop !== null) {
|
|
146
66
|
parts.push(exists(joined([
|
|
147
|
-
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
|
|
148
|
-
grouped(
|
|
67
|
+
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`),
|
|
68
|
+
grouped(sql.placed('l', plan.hop.field, 'link')),
|
|
69
|
+
fragment('AND'),
|
|
70
|
+
grouped(all([sql.scoped('m', plan.hop), reached(bound, 'm')])),
|
|
149
71
|
], ' ')));
|
|
150
72
|
}
|
|
151
73
|
if (plan.backlinks !== null) {
|
|
152
74
|
const field = plan.backlinks.field;
|
|
153
75
|
parts.push(exists(joined([
|
|
154
|
-
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'
|
|
155
|
-
grouped(
|
|
76
|
+
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`),
|
|
77
|
+
...(field === null ? [] : [grouped(sql.placed('l', field, 'link')), fragment('AND')]),
|
|
78
|
+
grouped(all([sql.scoped('s', plan.backlinks), reached(bound, 's')])),
|
|
156
79
|
], ' ')));
|
|
157
80
|
}
|
|
158
81
|
return parts;
|
|
@@ -160,11 +83,9 @@ function related(bound, plan) {
|
|
|
160
83
|
function filtered(bound, plan) {
|
|
161
84
|
const keys = bound.rooms.map(({ key }) => key);
|
|
162
85
|
return all([
|
|
163
|
-
fragment(`d.key IN ${
|
|
164
|
-
...(plan.paths === null ? [] : [fragment(`d.path IN ${list(plan.paths).sql}`, plan.paths)]),
|
|
165
|
-
...(plan.folder === null ? [] : [fragment('d.folder = ?', [plan.folder])]),
|
|
86
|
+
fragment(`d.key IN ${listed(keys).sql} AND ${VISIBLE('d')}`, keys),
|
|
166
87
|
reached(bound, 'd'),
|
|
167
|
-
|
|
88
|
+
sql.tests('d', plan),
|
|
168
89
|
...related(bound, plan),
|
|
169
90
|
]);
|
|
170
91
|
}
|
|
@@ -195,99 +116,25 @@ const GROUP_TIES = {
|
|
|
195
116
|
locale: undefined,
|
|
196
117
|
localeOptions: undefined,
|
|
197
118
|
};
|
|
198
|
-
function
|
|
199
|
-
if (value === null || value === undefined) {
|
|
200
|
-
return { placement: ordering.nulls === 'first' ? 0 : 2, klass: 0, scalar: null };
|
|
201
|
-
}
|
|
202
|
-
if (typeof value === 'boolean')
|
|
203
|
-
return { placement: 1, klass: 1, scalar: value ? 1 : 0 };
|
|
204
|
-
if (typeof value === 'number')
|
|
205
|
-
return { placement: 1, klass: 1, scalar: value };
|
|
206
|
-
if (typeof value === 'string')
|
|
207
|
-
return { placement: 1, klass: 2, scalar: value };
|
|
208
|
-
return { placement: 1, klass: 3, scalar: JSON.stringify(value) };
|
|
209
|
-
}
|
|
210
|
-
function placement(term) {
|
|
211
|
-
const nulls = term.ordering.nulls === 'first' ? 0 : 2;
|
|
212
|
-
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]);
|
|
213
|
-
}
|
|
214
|
-
function klass(term) {
|
|
215
|
-
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);
|
|
216
|
-
}
|
|
217
|
-
function classed(term, which) {
|
|
218
|
-
const kinds = which === 1 ? "('boolean', 'number')" : which === 2 ? "('string')" : "('array')";
|
|
219
|
-
return fragment(`(CASE WHEN ${term.kind.sql} IN ${kinds} THEN ${term.value.sql} END)`, [
|
|
220
|
-
...term.kind.bind,
|
|
221
|
-
...term.value.bind,
|
|
222
|
-
]);
|
|
223
|
-
}
|
|
224
|
-
function collated(bound, term, which) {
|
|
225
|
-
if (which === 1)
|
|
226
|
-
return '';
|
|
227
|
-
const name = bound.collate(term.ordering, which === 3, term.total === true);
|
|
228
|
-
return name === null ? '' : ` COLLATE ${name}`;
|
|
229
|
-
}
|
|
230
|
-
function sorted(bound, term) {
|
|
231
|
-
const direction = term.ordering.direction === 'desc' ? 'DESC' : 'ASC';
|
|
232
|
-
return joined([
|
|
233
|
-
placement(term),
|
|
234
|
-
fragment(`${klass(term).sql} ${direction}`, klass(term).bind),
|
|
235
|
-
...[1, 2, 3].map((which) => {
|
|
236
|
-
const value = classed(term, which);
|
|
237
|
-
return fragment(`${value.sql}${collated(bound, term, which)} ${direction}`, value.bind);
|
|
238
|
-
}),
|
|
239
|
-
], ', ');
|
|
240
|
-
}
|
|
241
|
-
function beyond(bound, term, value) {
|
|
242
|
-
const held = classOf(value, term.ordering);
|
|
243
|
-
const place = placement(term);
|
|
244
|
-
if (held.placement !== 1) {
|
|
245
|
-
return {
|
|
246
|
-
beyond: fragment(`${place.sql} > ${held.placement}`, place.bind),
|
|
247
|
-
equal: fragment(`${place.sql} = ${held.placement}`, place.bind),
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
const after = term.ordering.direction === 'desc' ? '<' : '>';
|
|
251
|
-
const kind = klass(term);
|
|
252
|
-
const scalar = classed(term, held.klass);
|
|
253
|
-
const collation = collated(bound, term, held.klass);
|
|
254
|
-
return {
|
|
255
|
-
beyond: any([
|
|
256
|
-
fragment(`${place.sql} > 1`, place.bind),
|
|
257
|
-
all([
|
|
258
|
-
fragment(`${place.sql} = 1`, place.bind),
|
|
259
|
-
any([
|
|
260
|
-
fragment(`${kind.sql} ${after} ${held.klass}`, kind.bind),
|
|
261
|
-
all([
|
|
262
|
-
fragment(`${kind.sql} = ${held.klass}`, kind.bind),
|
|
263
|
-
fragment(`${scalar.sql} ${after} ?${collation}`, [...scalar.bind, held.scalar]),
|
|
264
|
-
]),
|
|
265
|
-
]),
|
|
266
|
-
]),
|
|
267
|
-
]),
|
|
268
|
-
equal: all([
|
|
269
|
-
fragment(`${place.sql} = 1 AND ${kind.sql} = ${held.klass}`, [...place.bind, ...kind.bind]),
|
|
270
|
-
fragment(`${scalar.sql} = ?${collation}`, [...scalar.bind, held.scalar]),
|
|
271
|
-
]),
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
function following(parts, tie) {
|
|
275
|
-
let condition = tie;
|
|
276
|
-
for (const part of [...parts].reverse()) {
|
|
277
|
-
condition = any([part.beyond, all([part.equal, condition])]);
|
|
278
|
-
}
|
|
279
|
-
return condition;
|
|
280
|
-
}
|
|
281
|
-
function keyTerm(bound, key, ordering, index) {
|
|
119
|
+
function keyTerm(key, ordering, index) {
|
|
282
120
|
if (key.of === 'field') {
|
|
283
121
|
const alias = `o${index}`;
|
|
284
122
|
return {
|
|
285
|
-
term: {
|
|
286
|
-
|
|
123
|
+
term: {
|
|
124
|
+
ordering,
|
|
125
|
+
kind: fragment(`${alias}.kind`),
|
|
126
|
+
value: fragment(`${alias}.value`),
|
|
127
|
+
instant: fragment(`${alias}.instant`),
|
|
128
|
+
},
|
|
129
|
+
join: joined([
|
|
130
|
+
fragment(`LEFT JOIN replica_values ${alias} ON ${alias}.doc = d.id AND `),
|
|
131
|
+
grouped(sql.placed(alias, key.field, 'exact')),
|
|
132
|
+
fragment(` AND ${alias}.item = -1 AND ${alias}.kind NOT IN ('object', 'undefined')`),
|
|
133
|
+
], ''),
|
|
287
134
|
};
|
|
288
135
|
}
|
|
289
136
|
const expression = key.of === 'room'
|
|
290
|
-
? located(
|
|
137
|
+
? located('d')
|
|
291
138
|
: key.of === 'path'
|
|
292
139
|
? fragment('d.path')
|
|
293
140
|
: key.of === 'type'
|
|
@@ -313,13 +160,20 @@ function decoded(kind, value) {
|
|
|
313
160
|
return JSON.parse(value);
|
|
314
161
|
return null;
|
|
315
162
|
}
|
|
163
|
+
function sortedValue(row, index) {
|
|
164
|
+
const kind = row[`k${index}`];
|
|
165
|
+
return sql.sortValue(kind, decoded(kind, row[`v${index}`]), row[`i${index}`]);
|
|
166
|
+
}
|
|
316
167
|
function decodedKey(key, row, index) {
|
|
317
168
|
if (key.of === 'field') {
|
|
318
169
|
const kind = row[`k${index}`];
|
|
319
|
-
return kind === null || kind === undefined ? undefined :
|
|
170
|
+
return kind === null || kind === undefined ? undefined : sortedValue(row, index);
|
|
320
171
|
}
|
|
321
172
|
return row[`v${index}`] ?? null;
|
|
322
173
|
}
|
|
174
|
+
function instantOf(term) {
|
|
175
|
+
return term.instant ?? fragment('NULL');
|
|
176
|
+
}
|
|
323
177
|
const registered = new WeakMap();
|
|
324
178
|
function hashed(text) {
|
|
325
179
|
let hash = 0x811c9dc5;
|
|
@@ -369,9 +223,6 @@ function collating(connection) {
|
|
|
369
223
|
return name;
|
|
370
224
|
};
|
|
371
225
|
}
|
|
372
|
-
function bodied(shape) {
|
|
373
|
-
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
374
|
-
}
|
|
375
226
|
function masked(pointers) {
|
|
376
227
|
const root = { whole: false, members: new Map() };
|
|
377
228
|
for (const pointer of pointers) {
|
|
@@ -427,6 +278,7 @@ function documentOf(shape, room, row, linked) {
|
|
|
427
278
|
const path = row.path;
|
|
428
279
|
const type = row.type;
|
|
429
280
|
const fileId = row.file_id ?? null;
|
|
281
|
+
const revision = row.revision ?? null;
|
|
430
282
|
if (type === null) {
|
|
431
283
|
const binary = row.binary === null ? null : JSON.parse(row.binary);
|
|
432
284
|
const content = row.content ?? null;
|
|
@@ -436,12 +288,11 @@ function documentOf(shape, room, row, linked) {
|
|
|
436
288
|
...(binary === null
|
|
437
289
|
? {}
|
|
438
290
|
: {
|
|
439
|
-
binaryFile: binaryRecord(path, binary,
|
|
291
|
+
binaryFile: binaryRecord(path, binary, revision ?? undefined),
|
|
440
292
|
}),
|
|
441
|
-
...(shape.markdown && content !== null
|
|
442
|
-
? { markdown: content }
|
|
443
|
-
: {}),
|
|
293
|
+
...(shape.markdown && content !== null ? { markdown: content } : {}),
|
|
444
294
|
...(shape.fileId ? { fileId } : {}),
|
|
295
|
+
...(shape.revision ? { revision } : {}),
|
|
445
296
|
};
|
|
446
297
|
}
|
|
447
298
|
const read = bodied(shape) ? parse(row.content, path) : null;
|
|
@@ -462,7 +313,7 @@ function documentOf(shape, room, row, linked) {
|
|
|
462
313
|
? {}
|
|
463
314
|
: { sections: sectionsOf(body.sections, shape.sections) }),
|
|
464
315
|
...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
|
|
465
|
-
...(shape.markdown
|
|
316
|
+
...(shape.markdown ? { markdown: row.content } : {}),
|
|
466
317
|
...(shape.backlinks ? { backlinks: linked.get(documentKey(room, path)) ?? [] } : {}),
|
|
467
318
|
...(shape.change
|
|
468
319
|
? {
|
|
@@ -470,6 +321,7 @@ function documentOf(shape, room, row, linked) {
|
|
|
470
321
|
}
|
|
471
322
|
: {}),
|
|
472
323
|
...(shape.fileId ? { fileId } : {}),
|
|
324
|
+
...(shape.revision ? { revision } : {}),
|
|
473
325
|
};
|
|
474
326
|
}
|
|
475
327
|
function backlinksOf(connection, bound, rows) {
|
|
@@ -495,10 +347,10 @@ function backlinksOf(connection, bound, rows) {
|
|
|
495
347
|
return linked;
|
|
496
348
|
}
|
|
497
349
|
function documents(connection, bound, plan, shape, budget) {
|
|
498
|
-
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(
|
|
350
|
+
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(key, orderingOf(options), index));
|
|
499
351
|
const filter = filtered(bound, plan);
|
|
500
352
|
const cursor = plan.after?.values ?? null;
|
|
501
|
-
const room = located(
|
|
353
|
+
const room = located('d');
|
|
502
354
|
const tie = cursor === null
|
|
503
355
|
? null
|
|
504
356
|
: (() => {
|
|
@@ -514,19 +366,20 @@ function documents(connection, bound, plan, shape, budget) {
|
|
|
514
366
|
})();
|
|
515
367
|
const after = cursor === null || tie === null
|
|
516
368
|
? null
|
|
517
|
-
: following(terms.map(({ term }, index) => beyond(bound, term, cursor[index])), tie);
|
|
369
|
+
: sql.following(terms.map(({ term }, index) => sql.beyond(bound.collate, term, cursor[index])), tie);
|
|
518
370
|
const selected = joined([
|
|
519
371
|
fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id'),
|
|
520
372
|
...terms.map(({ term }, index) => joined([
|
|
521
373
|
fragment(`, ${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
522
374
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
375
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
523
376
|
], '')),
|
|
524
377
|
fragment(' FROM replica_entries d'),
|
|
525
378
|
...terms.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
|
|
526
379
|
fragment(' WHERE '),
|
|
527
380
|
after === null ? filter : all([filter, after]),
|
|
528
381
|
fragment(' ORDER BY '),
|
|
529
|
-
joined([...terms.map(({ term }) => sorted(bound, term)), room, fragment('d.path')], ', '),
|
|
382
|
+
joined([...terms.map(({ term }) => sql.sorted(bound.collate, term)), room, fragment('d.path')], ', '),
|
|
530
383
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
531
384
|
], '');
|
|
532
385
|
const rows = connection.rows(selected.sql, selected.bind);
|
|
@@ -565,7 +418,7 @@ function withContent(connection, rows, budget) {
|
|
|
565
418
|
return rows;
|
|
566
419
|
const ids = rows.map((row) => row.id);
|
|
567
420
|
const sizes = new Map(connection
|
|
568
|
-
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${
|
|
421
|
+
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${listed(ids).sql}`, ids)
|
|
569
422
|
.map((row) => [row.id, Number(row.size ?? 0)]));
|
|
570
423
|
let cut = rows.length;
|
|
571
424
|
let spent = 0;
|
|
@@ -580,20 +433,30 @@ function withContent(connection, rows, budget) {
|
|
|
580
433
|
const kept = rows.slice(0, cut);
|
|
581
434
|
const read = kept.map((row) => row.id);
|
|
582
435
|
const held = new Map(connection
|
|
583
|
-
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${
|
|
436
|
+
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${listed(read).sql}`, read)
|
|
584
437
|
.map((row) => [row.id, row.content]));
|
|
585
438
|
return kept.map((row) => ({ ...row, content: held.get(row.id) ?? null }));
|
|
586
439
|
}
|
|
587
440
|
function storedFolders(bound, plan) {
|
|
588
|
-
if (plan.paths !== null || plan.
|
|
441
|
+
if (plan.paths !== null || plan.fileIds !== null)
|
|
442
|
+
return null;
|
|
443
|
+
if (plan.hop !== null || plan.backlinks !== null)
|
|
589
444
|
return null;
|
|
590
445
|
const whole = bound.rooms.filter(({ locator }) => bound.reach === null || bound.reach.rooms.includes(locator));
|
|
591
446
|
if (whole.length === 0)
|
|
592
447
|
return null;
|
|
593
448
|
const keys = whole.map(({ key }) => key);
|
|
594
449
|
const prefix = plan.root.pathPrefix;
|
|
450
|
+
const outside = (bound.reach?.areas ?? []).flatMap((area) => {
|
|
451
|
+
const key = bound.keys.get(area.room);
|
|
452
|
+
if (key === undefined)
|
|
453
|
+
return [];
|
|
454
|
+
const fence = fenced('f', key, area, true);
|
|
455
|
+
return [fragment(`NOT ${grouped(fence).sql}`, fence.bind)];
|
|
456
|
+
});
|
|
595
457
|
return all([
|
|
596
|
-
fragment(`f.key IN ${
|
|
458
|
+
fragment(`f.key IN ${listed(keys).sql} AND f.layer = 0 AND f.present = 1`, keys),
|
|
459
|
+
...outside,
|
|
597
460
|
...(prefix === null || prefix === ''
|
|
598
461
|
? []
|
|
599
462
|
: [
|
|
@@ -608,7 +471,7 @@ function storedFolders(bound, plan) {
|
|
|
608
471
|
}
|
|
609
472
|
function groups(connection, bound, plan, shape) {
|
|
610
473
|
const keyed = shape.groupBy.map((key, index) => {
|
|
611
|
-
const { term, join } = keyTerm(
|
|
474
|
+
const { term, join } = keyTerm(key, GROUP_ORDER, index);
|
|
612
475
|
const kind = fragment(`COALESCE(${term.kind.sql}, 'null')`, term.kind.bind);
|
|
613
476
|
return { term: { ...term, kind }, join };
|
|
614
477
|
});
|
|
@@ -618,6 +481,7 @@ function groups(connection, bound, plan, shape) {
|
|
|
618
481
|
joined(keyed.map(({ term }, index) => joined([
|
|
619
482
|
fragment(`${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
620
483
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
484
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
621
485
|
], '')), ', '),
|
|
622
486
|
fragment(', 1 AS n FROM replica_entries d'),
|
|
623
487
|
...keyed.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
|
|
@@ -631,10 +495,15 @@ function groups(connection, bound, plan, shape) {
|
|
|
631
495
|
? inner
|
|
632
496
|
: joined([
|
|
633
497
|
inner,
|
|
634
|
-
fragment(" UNION ALL SELECT 'string' AS k0, f.path AS v0, 0 AS n FROM replica_folders f WHERE "),
|
|
498
|
+
fragment(" UNION ALL SELECT 'string' AS k0, f.path AS v0, NULL AS i0, 0 AS n FROM replica_folders f WHERE "),
|
|
635
499
|
folders,
|
|
636
500
|
], '');
|
|
637
|
-
const columns = keyed
|
|
501
|
+
const columns = keyed
|
|
502
|
+
.map((_, index) => `k${index}, min(v${index}) AS v${index}, i${index}`)
|
|
503
|
+
.join(', ');
|
|
504
|
+
const held = keyed
|
|
505
|
+
.map((_, index) => `k${index}, i${index}, CASE WHEN i${index} IS NULL THEN v${index} END`)
|
|
506
|
+
.join(', ');
|
|
638
507
|
const counted = {
|
|
639
508
|
ordering: { ...GROUP_ORDER },
|
|
640
509
|
kind: fragment("'number'"),
|
|
@@ -647,7 +516,7 @@ function groups(connection, bound, plan, shape) {
|
|
|
647
516
|
});
|
|
648
517
|
const ordered = shape.orderBy.map(({ by, direction }) => by === 'count'
|
|
649
518
|
? { ...counted, ordering: { ...GROUP_ORDER, direction } }
|
|
650
|
-
: outer(by, { ...GROUP_ORDER, direction }));
|
|
519
|
+
: { ...outer(by, { ...GROUP_ORDER, direction }), instant: fragment(`i${by}`) });
|
|
651
520
|
const ties = keyed.flatMap((_, index) => [
|
|
652
521
|
{ ...outer(index, GROUP_TIES), total: true },
|
|
653
522
|
{ ordering: GROUP_TIES, kind: fragment("'string'"), value: fragment(`k${index}`) },
|
|
@@ -657,19 +526,21 @@ function groups(connection, bound, plan, shape) {
|
|
|
657
526
|
? null
|
|
658
527
|
: cursor[shape.orderBy.length].flatMap((value) => [
|
|
659
528
|
value,
|
|
660
|
-
|
|
529
|
+
heldKind(value ?? null),
|
|
661
530
|
]);
|
|
662
531
|
const after = cursor === null || tieValues === null
|
|
663
532
|
? null
|
|
664
|
-
: following(ordered.map((term, index) => beyond(bound, term, cursor[index])), following(ties
|
|
665
|
-
|
|
533
|
+
: sql.following(ordered.map((term, index) => sql.beyond(bound.collate, term, cursor[index])), sql.following(ties
|
|
534
|
+
.slice(0, -1)
|
|
535
|
+
.map((term, index) => sql.beyond(bound.collate, term, tieValues[index])), sql.beyond(bound.collate, ties.at(-1), tieValues.at(-1)).beyond));
|
|
536
|
+
const grouping = joined([fragment(`SELECT ${columns}, sum(n) AS n FROM (`), source, fragment(`) GROUP BY ${held}`)], '');
|
|
666
537
|
const windowed = joined([
|
|
667
538
|
fragment('SELECT * FROM ('),
|
|
668
539
|
grouping,
|
|
669
540
|
fragment(')'),
|
|
670
541
|
...(after === null ? [] : [fragment(' WHERE '), after]),
|
|
671
542
|
fragment(' ORDER BY '),
|
|
672
|
-
joined([...ordered, ...ties].map((term) => sorted(bound, term)), ', '),
|
|
543
|
+
joined([...ordered, ...ties].map((term) => sql.sorted(bound.collate, term)), ', '),
|
|
673
544
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
674
545
|
], '');
|
|
675
546
|
const rows = connection.rows(windowed.sql, windowed.bind);
|
|
@@ -682,7 +553,7 @@ function groups(connection, bound, plan, shape) {
|
|
|
682
553
|
: last === undefined
|
|
683
554
|
? (plan.after?.text ?? FROM_START)
|
|
684
555
|
: cursorOf([
|
|
685
|
-
...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) :
|
|
556
|
+
...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) : sortedValue(last, by)),
|
|
686
557
|
keyOf(last),
|
|
687
558
|
]);
|
|
688
559
|
const total = plan.total
|
|
@@ -694,13 +565,16 @@ function groups(connection, bound, plan, shape) {
|
|
|
694
565
|
...(total === undefined ? {} : { total }),
|
|
695
566
|
};
|
|
696
567
|
}
|
|
697
|
-
|
|
698
|
-
|
|
568
|
+
function boundOf(connection, plan, rooms) {
|
|
569
|
+
return {
|
|
699
570
|
rooms: [...rooms].sort((left, right) => left.locator < right.locator ? -1 : left.locator > right.locator ? 1 : 0),
|
|
700
571
|
keys: new Map(rooms.map(({ locator, key }) => [locator, key])),
|
|
701
572
|
reach: plan.reach,
|
|
702
573
|
collate: collating(connection),
|
|
703
574
|
};
|
|
575
|
+
}
|
|
576
|
+
export function answerQuery(connection, plan, rooms, budget = null) {
|
|
577
|
+
const bound = boundOf(connection, plan, rooms);
|
|
704
578
|
if (rooms.length === 0) {
|
|
705
579
|
return plan.shape.kind === 'groups'
|
|
706
580
|
? { groups: [], ...(plan.total ? { total: 0 } : {}) }
|