@bjornpagen/bumbledb 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COOKBOOK.md +1394 -0
- package/README.md +4 -0
- package/dist/closed.d.ts +60 -37
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +159 -40
- package/dist/closed.js.map +1 -1
- package/dist/count.d.ts +64 -17
- package/dist/count.d.ts.map +1 -1
- package/dist/count.js +37 -14
- package/dist/count.js.map +1 -1
- package/dist/db.d.ts +15 -7
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +13 -5
- package/dist/db.js.map +1 -1
- package/dist/exhume.d.ts +12 -11
- package/dist/exhume.d.ts.map +1 -1
- package/dist/exhume.js +7 -6
- package/dist/exhume.js.map +1 -1
- package/dist/face.d.ts +79 -19
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +14 -15
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +158 -129
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +80 -81
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +21 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -12
- package/dist/index.js.map +1 -1
- package/dist/lower.d.ts +6 -3
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +48 -17
- package/dist/lower.js.map +1 -1
- package/dist/marshal.d.ts +36 -21
- package/dist/marshal.d.ts.map +1 -1
- package/dist/marshal.js +41 -25
- package/dist/marshal.js.map +1 -1
- package/dist/native.js +2 -2
- package/dist/query/atom.d.ts +332 -174
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +72 -172
- package/dist/query/atom.js.map +1 -1
- package/dist/query/lower.d.ts +295 -107
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +860 -388
- package/dist/query/lower.js.map +1 -1
- package/dist/query/predicate.d.ts +69 -88
- package/dist/query/predicate.d.ts.map +1 -1
- package/dist/query/predicate.js +118 -72
- package/dist/query/predicate.js.map +1 -1
- package/dist/query/run.d.ts +19 -16
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +31 -24
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +139 -123
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +71 -115
- package/dist/query/scope.js.map +1 -1
- package/dist/query/select.d.ts +102 -80
- package/dist/query/select.d.ts.map +1 -1
- package/dist/query/select.js +39 -34
- package/dist/query/select.js.map +1 -1
- package/dist/relation.d.ts +33 -36
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +15 -16
- package/dist/relation.js.map +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +19 -8
- package/dist/schema.js.map +1 -1
- package/dist/spec.d.ts +6 -5
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +56 -31
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +38 -13
- package/dist/statements.js.map +1 -1
- package/package.json +4 -3
- package/src/closed.ts +271 -93
- package/src/count.ts +112 -18
- package/src/db.ts +22 -12
- package/src/exhume.ts +12 -11
- package/src/face.ts +114 -22
- package/src/fields.ts +261 -217
- package/src/index.ts +50 -59
- package/src/lower.ts +62 -20
- package/src/marshal.ts +48 -30
- package/src/native.ts +2 -2
- package/src/query/atom.ts +480 -376
- package/src/query/lower.ts +1341 -542
- package/src/query/predicate.ts +198 -161
- package/src/query/run.ts +35 -25
- package/src/query/scope.ts +188 -218
- package/src/query/select.ts +168 -93
- package/src/relation.ts +38 -44
- package/src/schema.ts +22 -12
- package/src/spec.ts +6 -5
- package/src/statements.ts +69 -26
- package/dist/brand.d.ts +0 -59
- package/dist/brand.d.ts.map +0 -1
- package/dist/brand.js +0 -47
- package/dist/brand.js.map +0 -1
- package/src/brand.ts +0 -82
package/src/query/predicate.ts
CHANGED
|
@@ -1,195 +1,232 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Stratified recursion — engine recursion as values, mirroring the IR's
|
|
3
3
|
* cut exactly (`ir::Program`/`PredicateDef`/`AtomSource::Idb`;
|
|
4
|
-
* `docs/architecture/20-query-ir.md` § engine recursion):
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
4
|
+
* `docs/architecture/20-query-ir.md` § engine recursion):
|
|
5
|
+
*
|
|
6
|
+
* program(S, (p) => {
|
|
7
|
+
* const reach = p.rec("reach")
|
|
8
|
+
* reach.rule((r) => r.match(Node, { id: r.var("c") })
|
|
9
|
+
* .where(r.eq(r.var("c"), r.param("root"))).select("c"))
|
|
10
|
+
* reach.rule((r) => r.match(Parent, { child: r.var("c"), parent: r.var("m") })
|
|
11
|
+
* .idb(reach, r.var("m")).select("c"))
|
|
12
|
+
* return p.output((r) => r.match(Posting, { account: r.var("a"), minor: r.var("m") })
|
|
13
|
+
* .idb(reach, r.var("a")).select(r.sum("m")))
|
|
14
|
+
* })
|
|
15
|
+
*
|
|
16
|
+
* `p.rec(name)` declares one recursive predicate (declaration order = its
|
|
17
|
+
* dense `PredId`); `rec.rule(...)` attaches one clause — its builder's
|
|
18
|
+
* `idb` accepts ONLY the rec itself (the self-recursion cut as a
|
|
19
|
+
* type-level boundary: mutual recursion is unwritable) and its head
|
|
20
|
+
* projects bound variable NAMES only (aggregation/measure through a cycle
|
|
21
|
+
* is unrepresentable — the strata judge's roster, made unwritable);
|
|
22
|
+
* `p.output(...)` seals the recs and builds the output rules, whose `idb`
|
|
23
|
+
* folds any FINISHED stratum (recipe 25's form). The rec value `.rule`
|
|
24
|
+
* returns carries the params its rules used — thread it into the output's
|
|
25
|
+
* `idb` and the program's inferred `Params` stays exactly the params the
|
|
26
|
+
* rules use. Everything deeper — strata legality, signature sealing, the
|
|
27
|
+
* three oracles — is the ENGINE's judge, surfacing typed at prepare.
|
|
14
28
|
*/
|
|
15
29
|
|
|
16
30
|
import * as errors from "@superbuilders/errors"
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
import type { RecData } from "#query/atom.ts"
|
|
32
|
+
import type {
|
|
33
|
+
AnyRuleValue,
|
|
34
|
+
HeadOf,
|
|
35
|
+
HeadShape,
|
|
36
|
+
OutputRuleScope,
|
|
37
|
+
ParamsOf,
|
|
38
|
+
ProgramState,
|
|
39
|
+
Query,
|
|
40
|
+
RawScope,
|
|
41
|
+
RecRef,
|
|
42
|
+
RecRuleScope,
|
|
43
|
+
RowOf,
|
|
44
|
+
RuleValue
|
|
45
|
+
} from "#query/lower.ts"
|
|
46
|
+
import { makeOutputRuleScope, makeQuery, makeRawScope } from "#query/lower.ts"
|
|
47
|
+
import type { Flatten, ParamsRecord, ShapeOf } from "#query/scope.ts"
|
|
48
|
+
import { inferred } from "#query/scope.ts"
|
|
49
|
+
import type { Schema, SchemaRelations } from "#schema.ts"
|
|
24
50
|
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
/**
|
|
52
|
+
* One recursive predicate HANDLE: `rec.rule(...)` attaches a clause and
|
|
53
|
+
* returns the SAME rec under a widened params type (the runtime data is
|
|
54
|
+
* shared — either handle is the self-reference; the returned one carries
|
|
55
|
+
* the rules' params for the output to thread) and under the head signature
|
|
56
|
+
* the FIRST rule sealed (every later rule derives the same head — the
|
|
57
|
+
* runtime alignment check's law), so a threaded handle's `idb` joins are
|
|
58
|
+
* arity- and domain-checked against the head.
|
|
59
|
+
*/
|
|
60
|
+
interface Rec<
|
|
61
|
+
Rels extends SchemaRelations,
|
|
62
|
+
Name extends string,
|
|
63
|
+
P extends ParamsRecord,
|
|
64
|
+
Head extends HeadShape = undefined
|
|
65
|
+
> extends RecRef<Name, P, Head> {
|
|
66
|
+
rule<RV extends AnyRuleValue>(
|
|
67
|
+
build: (r: RecRuleScope<Rels, Name>) => RV
|
|
68
|
+
): Rec<Rels, Name, Flatten<P & ParamsOf<RV>>, Head extends undefined ? HeadOf<RV> : Head>
|
|
69
|
+
readonly [inferred]?: { readonly params: P; readonly head: Head }
|
|
42
70
|
}
|
|
43
71
|
|
|
44
|
-
/**
|
|
45
|
-
type
|
|
72
|
+
/** One output-rule builder function. */
|
|
73
|
+
type OutputBuild<Rels extends SchemaRelations> = (r: OutputRuleScope<Rels>) => AnyRuleValue
|
|
46
74
|
|
|
47
|
-
/**
|
|
48
|
-
type
|
|
75
|
+
/** A build function's rule value. */
|
|
76
|
+
type BuiltRule<F> = F extends (r: never) => infer RV ? RV : never
|
|
49
77
|
|
|
50
|
-
/**
|
|
51
|
-
type
|
|
78
|
+
/** The union row of a tuple of output builds. */
|
|
79
|
+
type OutputRow<Builds extends readonly OutputBuild<SchemaRelations>[]> = RowOf<BuiltRule<Builds[number]>>
|
|
52
80
|
|
|
53
|
-
/** The
|
|
54
|
-
type
|
|
81
|
+
/** The intersected params record of a tuple of output builds. */
|
|
82
|
+
type OutputParams<Builds extends readonly OutputBuild<SchemaRelations>[]> = ShapeOf<ParamsOf<BuiltRule<Builds[number]>>>
|
|
55
83
|
|
|
56
84
|
/**
|
|
57
|
-
* The
|
|
58
|
-
* the
|
|
59
|
-
*
|
|
85
|
+
* The program scope: declare recs, attach their rules, then declare the
|
|
86
|
+
* output — which seals the recs (a later `rec`/`rule` is a construction
|
|
87
|
+
* error) and returns the program as an ordinary query value.
|
|
60
88
|
*/
|
|
61
|
-
|
|
89
|
+
interface ProgramScope<Rels extends SchemaRelations> {
|
|
90
|
+
/** Declares one recursive predicate; declaration order = its dense `PredId`. */
|
|
91
|
+
rec<const Name extends string>(name: Name): Rec<Rels, Name, Record<never, never>>
|
|
92
|
+
/**
|
|
93
|
+
* Declares the output predicate (one rule per build; multiple rules =
|
|
94
|
+
* set union) and seals the program. Must be what the `program()`
|
|
95
|
+
* callback returns.
|
|
96
|
+
*/
|
|
97
|
+
output<const Builds extends readonly OutputBuild<Rels>[]>(
|
|
98
|
+
...builds: Builds
|
|
99
|
+
): Query<Rels, OutputRow<Builds>, OutputParams<Builds>>
|
|
100
|
+
}
|
|
62
101
|
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*/
|
|
69
|
-
interface PredicateRuleInput<ColsV> {
|
|
70
|
-
readonly finds: { readonly [K in keyof ColsV]: Var<ColsV[K]> }
|
|
71
|
-
readonly body: readonly AnyBodyItem[]
|
|
102
|
+
/** The runtime rec-handle shape beneath the typed `Rec` face. */
|
|
103
|
+
interface RawRec<Name extends string> {
|
|
104
|
+
readonly name: Name
|
|
105
|
+
readonly data: RecData
|
|
106
|
+
rule(build: (r: RawScope) => RuleValue<never, never>): RawRec<Name>
|
|
72
107
|
}
|
|
73
108
|
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
109
|
+
/** Builds the runtime rec handle over shared rec data. */
|
|
110
|
+
function makeRawRec<Name extends string>(state: ProgramState, name: Name, data: RecData): RawRec<Name> {
|
|
111
|
+
const rec: RawRec<Name> = {
|
|
112
|
+
name,
|
|
113
|
+
data,
|
|
114
|
+
rule(build) {
|
|
115
|
+
if (state.sealed) {
|
|
116
|
+
throw errors.new(
|
|
117
|
+
`rec ${name}: the program's output is already declared — recursive rules attach before p.output`
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
const built = build(makeRawScope({ kind: "rec", self: data }))
|
|
121
|
+
const head = data.rules[0]
|
|
122
|
+
if (head !== undefined) {
|
|
123
|
+
const declared = head.select.map(function columnName(column) {
|
|
124
|
+
return column.name
|
|
125
|
+
})
|
|
126
|
+
const candidate = built.rule.select.map(function columnName(column) {
|
|
127
|
+
return column.name
|
|
128
|
+
})
|
|
129
|
+
if (declared.join(", ") !== candidate.join(", ")) {
|
|
130
|
+
throw errors.new(
|
|
131
|
+
`rec ${name}: every rule derives the same head — rule 0 projects (${declared.join(", ")}), this rule projects (${candidate.join(", ")})`
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
data.rules.push(built.rule)
|
|
136
|
+
return makeRawRec<Name>(state, name, data)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
Object.freeze(rec)
|
|
140
|
+
return rec
|
|
82
141
|
}
|
|
83
142
|
|
|
84
143
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* type sees through predicates it only reaches transitively.
|
|
144
|
+
* The rec handles' trusted admission seam (the `refsComplete` pattern):
|
|
145
|
+
* the checkable fact — the handle owns exactly the rec data it names — is
|
|
146
|
+
* verified before the raw handle is admitted at its typed face.
|
|
89
147
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
148
|
+
function isRecHandle<Rels extends SchemaRelations, Name extends string, P extends ParamsRecord>(
|
|
149
|
+
data: RecData,
|
|
150
|
+
rec: RawRec<Name>
|
|
151
|
+
): rec is RawRec<Name> & Rec<Rels, Name, P> {
|
|
152
|
+
return rec.data === data
|
|
93
153
|
}
|
|
94
154
|
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
bindings: resolveBindings(
|
|
107
|
-
`predicate ${data.name}`,
|
|
108
|
-
data.columns.map(function asField(column) {
|
|
109
|
-
return { name: column.name, field: column.data }
|
|
110
|
-
}),
|
|
111
|
-
bindings
|
|
112
|
-
)
|
|
113
|
-
})
|
|
155
|
+
/** Builds one typed rec handle over shared rec data. */
|
|
156
|
+
function makeRec<Rels extends SchemaRelations, Name extends string, P extends ParamsRecord>(
|
|
157
|
+
state: ProgramState,
|
|
158
|
+
name: Name,
|
|
159
|
+
data: RecData
|
|
160
|
+
): Rec<Rels, Name, P> {
|
|
161
|
+
const raw = makeRawRec<Name>(state, name, data)
|
|
162
|
+
if (!isRecHandle<Rels, Name, P>(data, raw)) {
|
|
163
|
+
throw errors.new(`rec ${name}: handle construction incomplete`)
|
|
164
|
+
}
|
|
165
|
+
return raw
|
|
114
166
|
}
|
|
115
167
|
|
|
116
168
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
169
|
+
* Builds a stratified program over a schema. The callback declares recs
|
|
170
|
+
* and their rules through the scope and MUST return `p.output(...)` — the
|
|
171
|
+
* sealed program is an ordinary query value: `db.prepare` lowers it to
|
|
172
|
+
* the one `ProgramIr` shape the engine executes under the per-stratum
|
|
173
|
+
* fixpoint driver.
|
|
122
174
|
*/
|
|
123
|
-
function
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
):
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Object.freeze({
|
|
137
|
-
name: columnName,
|
|
138
|
-
data: resolveFieldData(registry.theory, ref.relation, ref.field)
|
|
139
|
-
})
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
if (ordered.length === 0) {
|
|
143
|
-
throw errors.new(`predicate ${name}: a predicate head needs at least one column`)
|
|
144
|
-
}
|
|
145
|
-
const ruleSlots: PredicateRuleData[] = []
|
|
146
|
-
const data: PredicateData = Object.freeze({
|
|
147
|
-
name,
|
|
148
|
-
columns: Object.freeze(ordered),
|
|
149
|
-
rules: ruleSlots
|
|
150
|
-
})
|
|
151
|
-
const self: PredicateSelf<ColumnValues<Cols>> = Object.freeze({
|
|
152
|
-
data,
|
|
153
|
-
match(bindings: Readonly<Record<string, unknown>>) {
|
|
154
|
-
return predicateAtom(data, bindings)
|
|
155
|
-
}
|
|
156
|
-
})
|
|
157
|
-
const declared = rules(self)
|
|
158
|
-
for (const rule of declared) {
|
|
159
|
-
const finds: AnyVar[] = []
|
|
160
|
-
const record: Readonly<Record<string, unknown>> = Object.fromEntries(Object.entries(rule.finds))
|
|
161
|
-
for (const column of ordered) {
|
|
162
|
-
const found = record[column.name]
|
|
163
|
-
if (!isTerm(found) || found[term] !== "var") {
|
|
175
|
+
function program<Rels extends SchemaRelations, Q extends Query<Rels, unknown, ParamsRecord>>(
|
|
176
|
+
theory: Schema<Rels>,
|
|
177
|
+
build: (p: ProgramScope<Rels>) => Q
|
|
178
|
+
): Q {
|
|
179
|
+
const state: ProgramState = { recs: [], sealed: false }
|
|
180
|
+
const names = new Set<string>()
|
|
181
|
+
const made: { query: unknown } = { query: undefined }
|
|
182
|
+
const scope: ProgramScope<Rels> = {
|
|
183
|
+
rec<const Name extends string>(name: Name): Rec<Rels, Name, Record<never, never>> {
|
|
184
|
+
if (state.sealed) {
|
|
185
|
+
throw errors.new(`program: the output is already declared — rec ${name} would be unreachable`)
|
|
186
|
+
}
|
|
187
|
+
if (names.has(name)) {
|
|
164
188
|
throw errors.new(
|
|
165
|
-
`
|
|
189
|
+
`program: a rec named ${name} is already declared — rec names are the self-recursion cut's identity`
|
|
166
190
|
)
|
|
167
191
|
}
|
|
168
|
-
|
|
192
|
+
names.add(name)
|
|
193
|
+
const data: RecData = { name, rules: [] }
|
|
194
|
+
state.recs.push(data)
|
|
195
|
+
return makeRec<Rels, Name, Record<never, never>>(state, name, data)
|
|
196
|
+
},
|
|
197
|
+
output<const Builds extends readonly OutputBuild<Rels>[]>(
|
|
198
|
+
...builds: Builds
|
|
199
|
+
): Query<Rels, OutputRow<Builds>, OutputParams<Builds>> {
|
|
200
|
+
if (state.sealed) {
|
|
201
|
+
throw errors.new("program: output is declared once — multiple rules are multiple builds of the one output")
|
|
202
|
+
}
|
|
203
|
+
state.sealed = true
|
|
204
|
+
for (const rec of state.recs) {
|
|
205
|
+
if (rec.rules.length === 0) {
|
|
206
|
+
throw errors.new(
|
|
207
|
+
`program: rec ${rec.name} has no rules — a predicate with no defining clause seals no signature`
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
Object.freeze(rec.rules)
|
|
211
|
+
}
|
|
212
|
+
if (builds.length === 0) {
|
|
213
|
+
throw errors.new("program: the output needs at least one rule")
|
|
214
|
+
}
|
|
215
|
+
const rules = builds.map(function buildRule(buildOne) {
|
|
216
|
+
return buildOne(makeOutputRuleScope<Rels>(state)).rule
|
|
217
|
+
})
|
|
218
|
+
const q = makeQuery<Rels, OutputRow<Builds>, OutputParams<Builds>>(theory, state.recs, rules)
|
|
219
|
+
made.query = q
|
|
220
|
+
return q
|
|
169
221
|
}
|
|
170
|
-
ruleSlots.push(Object.freeze({ finds: Object.freeze(finds), body: Object.freeze([...rule.body]) }))
|
|
171
222
|
}
|
|
172
|
-
Object.freeze(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
})
|
|
223
|
+
Object.freeze(scope)
|
|
224
|
+
const result = build(scope)
|
|
225
|
+
if (made.query !== result) {
|
|
226
|
+
throw errors.new("program: the build callback must return p.output(...) — the sealed program IS the query value")
|
|
227
|
+
}
|
|
228
|
+
return result
|
|
180
229
|
}
|
|
181
230
|
|
|
182
|
-
export type {
|
|
183
|
-
|
|
184
|
-
ColumnValues,
|
|
185
|
-
Predicate,
|
|
186
|
-
PredicateBindings,
|
|
187
|
-
PredicateColumn,
|
|
188
|
-
PredicateColumnsInput,
|
|
189
|
-
PredicateData,
|
|
190
|
-
PredicateParams,
|
|
191
|
-
PredicateRuleData,
|
|
192
|
-
PredicateRuleInput,
|
|
193
|
-
PredicateSelf
|
|
194
|
-
}
|
|
195
|
-
export { makePredicate }
|
|
231
|
+
export type { OutputBuild, ProgramScope, Rec }
|
|
232
|
+
export { program }
|
package/src/query/run.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prepared-query marshaling seams
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the
|
|
9
|
-
*
|
|
10
|
-
*
|
|
2
|
+
* Prepared-query marshaling seams, the two rides every execution takes —
|
|
3
|
+
* the typed params object down to the bridge's positional `QueryParam[]`
|
|
4
|
+
* (registry order = dense `ParamId`s, values tagged by each param's
|
|
5
|
+
* ANCHORING use: the field position or comparison sibling that typed it,
|
|
6
|
+
* op-aware exactly as comparison literals tag), and answer rows
|
|
7
|
+
* (positional, head order) back up to plain objects of BARE structural
|
|
8
|
+
* values — the marshal boundary is pure both ways: the engine computed
|
|
9
|
+
* the answer under the prepared head, so a decoded row that carries every
|
|
10
|
+
* select column IS a row (the trusted read seam), and nothing is asserted
|
|
11
|
+
* on any value. Answers are SETS — no order or limit exists anywhere;
|
|
12
|
+
* hosts sort. The `Prepared` VALUE itself (no lifecycle, GC-reclaimed
|
|
13
|
+
* plan) lives in `#db.ts`.
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
import * as errors from "@superbuilders/errors"
|
|
14
17
|
import type { FactValue, QueryParam, TaggedValue } from "#native.ts"
|
|
15
|
-
import type { SelectColumn } from "#query/
|
|
16
|
-
import {
|
|
18
|
+
import type { SelectColumn } from "#query/atom.ts"
|
|
19
|
+
import { taggedCmpLiteral } from "#query/lower.ts"
|
|
17
20
|
import type { ParamEntry } from "#query/scope.ts"
|
|
18
21
|
|
|
19
22
|
/** The 13-bit Allen mask ceiling (`bumbledb/crates/bumbledb/src/allen.rs`: bits above the low 13 are unrepresentable). */
|
|
@@ -27,12 +30,22 @@ function wireMask(name: string, value: unknown): TaggedValue {
|
|
|
27
30
|
return { kind: "allenMask", mask: value }
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
/** Tags one supplied value-param cell by its anchoring use. */
|
|
34
|
+
function wireValue(entry: ParamEntry, context: string, value: unknown): TaggedValue {
|
|
35
|
+
if (entry.anchor === undefined) {
|
|
36
|
+
throw errors.new(
|
|
37
|
+
`param ${entry.name} has no field-anchored use — bind it in an atom or compare it against a bound variable`
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
return taggedCmpLiteral(context, entry.anchor, value, entry.op)
|
|
41
|
+
}
|
|
42
|
+
|
|
30
43
|
/**
|
|
31
44
|
* Marshals the typed params object to the bridge's positional arguments,
|
|
32
|
-
* in
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
45
|
+
* in registry order (= the lowering's dense `ParamId`s). A missing entry
|
|
46
|
+
* is a typed error naming the param; values tag by the anchoring use's
|
|
47
|
+
* structural type; a set param takes a readonly array (the empty set is
|
|
48
|
+
* legal and matches nothing — the engine's rule).
|
|
36
49
|
*/
|
|
37
50
|
function wireParams(entries: readonly ParamEntry[], supplied: Readonly<Record<string, unknown>>): QueryParam[] {
|
|
38
51
|
return entries.map(function wireOne(entry): QueryParam {
|
|
@@ -43,30 +56,27 @@ function wireParams(entries: readonly ParamEntry[], supplied: Readonly<Record<st
|
|
|
43
56
|
if (entry.shape === "mask") {
|
|
44
57
|
return wireMask(entry.name, value)
|
|
45
58
|
}
|
|
46
|
-
if (entry.data === undefined) {
|
|
47
|
-
throw errors.new(`param ${entry.name}: registry entry carries no declaring field`)
|
|
48
|
-
}
|
|
49
59
|
if (entry.shape === "set") {
|
|
50
60
|
if (!Array.isArray(value)) {
|
|
51
61
|
throw errors.new(`param ${entry.name}: a set param binds a readonly array of values`)
|
|
52
62
|
}
|
|
53
|
-
const data = entry.data
|
|
54
63
|
return {
|
|
55
64
|
kind: "set",
|
|
56
65
|
values: value.map(function wireElement(element, index) {
|
|
57
|
-
return
|
|
66
|
+
return wireValue(entry, `param ${entry.name}[${index}]`, element)
|
|
58
67
|
})
|
|
59
68
|
}
|
|
60
69
|
}
|
|
61
|
-
return
|
|
70
|
+
return wireValue(entry, `param ${entry.name}`, value)
|
|
62
71
|
})
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
/**
|
|
66
75
|
* The read-side trusted seam of answers: a decoded row carrying every
|
|
67
76
|
* select column IS a `Row` — the engine computed it under the prepared
|
|
68
|
-
* head,
|
|
69
|
-
*
|
|
77
|
+
* head, and the values are BARE structural values, so nothing is asserted
|
|
78
|
+
* beyond presence (the store is the proof carrier; no brand exists to
|
|
79
|
+
* re-derive).
|
|
70
80
|
*/
|
|
71
81
|
function isAnswerRow<Row>(
|
|
72
82
|
select: readonly SelectColumn[],
|
|
@@ -79,8 +89,8 @@ function isAnswerRow<Row>(
|
|
|
79
89
|
|
|
80
90
|
/**
|
|
81
91
|
* Decodes positional answer rows (column order = the program's head order
|
|
82
|
-
* = the select
|
|
83
|
-
*
|
|
92
|
+
* = the select's written order) to named, frozen row objects of bare
|
|
93
|
+
* structural values.
|
|
84
94
|
*/
|
|
85
95
|
function decodeAnswers<Row>(select: readonly SelectColumn[], rows: FactValue[][]): Row[] {
|
|
86
96
|
return rows.map(function decodeRow(row) {
|