@bjornpagen/bumbledb 1.0.1 → 1.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 +176 -36
- package/README.md +70 -26
- package/dist/capacity.d.ts +15 -55
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +63 -12
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +28 -6
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +119 -71
- package/dist/changes.js.map +1 -1
- package/dist/closed.d.ts +23 -51
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +110 -168
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +8 -14
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +113 -188
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +17 -18
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +41 -20
- package/dist/compile.js.map +1 -1
- package/dist/db-native.d.ts +76 -41
- package/dist/db-native.d.ts.map +1 -1
- package/dist/db-native.js +8 -0
- package/dist/db-native.js.map +1 -1
- package/dist/db.d.ts +61 -26
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +152 -85
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +11 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +13 -1
- package/dist/errors.js.map +1 -1
- package/dist/face.d.ts +19 -25
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +30 -24
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +13 -26
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +89 -116
- package/dist/fields.js.map +1 -1
- package/dist/immutable.d.ts +10 -0
- package/dist/immutable.d.ts.map +1 -0
- package/dist/immutable.js +75 -0
- package/dist/immutable.js.map +1 -0
- package/dist/index.d.ts +16 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +6 -4
- package/dist/internal/log.d.ts.map +1 -1
- package/dist/internal/log.js +3 -1
- package/dist/internal/log.js.map +1 -1
- package/dist/law.d.ts +11 -11
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +1 -1
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +11 -6
- package/dist/lower.js.map +1 -1
- package/dist/migration.d.ts +2 -3
- package/dist/migration.d.ts.map +1 -1
- package/dist/migration.js +5 -5
- package/dist/migration.js.map +1 -1
- package/dist/native.d.ts +2 -2
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +1 -1
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/description.d.ts +33 -0
- package/dist/query/description.d.ts.map +1 -0
- package/dist/query/description.js +302 -0
- package/dist/query/description.js.map +1 -0
- package/dist/query/lower.d.ts +60 -4
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +126 -161
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +7 -2
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +240 -53
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +8 -10
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +3 -4
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +5 -10
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +22 -48
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +30 -82
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +10 -25
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +7 -12
- package/dist/result.js.map +1 -1
- package/dist/rows.d.ts +11 -14
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +49 -237
- package/dist/rows.js.map +1 -1
- package/dist/runtime-codes.d.ts +1 -1
- package/dist/runtime-codes.d.ts.map +1 -1
- package/dist/runtime-codes.js +1 -2
- package/dist/runtime-codes.js.map +1 -1
- package/dist/runtime-errors.d.ts +27 -15
- package/dist/runtime-errors.d.ts.map +1 -1
- package/dist/runtime-errors.js +44 -8
- package/dist/runtime-errors.js.map +1 -1
- package/dist/runtime-native.d.ts +16 -34
- package/dist/runtime-native.d.ts.map +1 -1
- package/dist/runtime-native.js.map +1 -1
- package/dist/runtime.d.ts +11 -28
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +14 -54
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +9 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +85 -24
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +27 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +104 -0
- package/dist/selection.js.map +1 -0
- package/dist/shape.d.ts +6 -9
- package/dist/shape.d.ts.map +1 -1
- package/dist/statements.d.ts +17 -52
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +92 -111
- package/dist/statements.js.map +1 -1
- package/dist/values.d.ts +25 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +203 -0
- package/dist/values.js.map +1 -0
- package/pack-provenance.json +3 -3
- package/package.json +4 -4
- package/src/capacity.ts +86 -76
- package/src/changes.ts +225 -94
- package/src/closed.ts +161 -314
- package/src/codec.ts +123 -213
- package/src/compile.ts +61 -38
- package/src/db-native.ts +73 -68
- package/src/db.ts +268 -165
- package/src/errors.ts +16 -1
- package/src/face.ts +44 -74
- package/src/fields.ts +100 -146
- package/src/immutable.ts +76 -0
- package/src/index.ts +38 -31
- package/src/internal/log.ts +4 -5
- package/src/law.ts +14 -14
- package/src/lower.ts +13 -10
- package/src/migration.ts +5 -12
- package/src/native.ts +2 -1
- package/src/query/atom.ts +1 -0
- package/src/query/description.ts +394 -0
- package/src/query/lower.ts +139 -173
- package/src/query/parse-ir.ts +247 -54
- package/src/query/run.ts +15 -9
- package/src/query/scope.ts +10 -18
- package/src/relation.ts +58 -142
- package/src/result.ts +16 -45
- package/src/rows.ts +57 -250
- package/src/runtime-codes.ts +1 -2
- package/src/runtime-errors.ts +48 -8
- package/src/runtime-native.ts +13 -46
- package/src/runtime.ts +51 -119
- package/src/schema.ts +99 -29
- package/src/selection.ts +124 -0
- package/src/shape.ts +9 -9
- package/src/statements.ts +138 -189
- package/src/values.ts +223 -0
package/src/query/lower.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import { sealedFieldsOf } from "#closed.ts"
|
|
1
|
+
import { membersAgree, sealedFieldsOf } from "#closed.ts"
|
|
2
2
|
import { AuthoringError, SdkInvariantError } from "#errors.ts"
|
|
3
3
|
import type { AnyClosedRoster, AnyField } from "#fields.ts"
|
|
4
4
|
import {
|
|
5
5
|
assertDeclarationOrderKey,
|
|
6
6
|
f64 as f64Field,
|
|
7
|
+
i64 as i64Field,
|
|
7
8
|
isFloatIntervalValue,
|
|
8
9
|
isIntervalValue,
|
|
9
10
|
literalShapeError,
|
|
10
11
|
rosterOf,
|
|
12
|
+
rostersAgree,
|
|
11
13
|
u64 as u64Field
|
|
12
14
|
} from "#fields.ts"
|
|
15
|
+
import { snapshotData } from "#immutable.ts"
|
|
13
16
|
import type { ClassRecordOf, SchemaClasses } from "#law.ts"
|
|
14
17
|
import type {
|
|
15
18
|
AtomIr,
|
|
@@ -86,7 +89,8 @@ import {
|
|
|
86
89
|
} from "#query/scope.ts"
|
|
87
90
|
import { literalWireOf } from "#scalar.ts"
|
|
88
91
|
import type { AnySchema, Schema, SchemaRelations } from "#schema.ts"
|
|
89
|
-
import {
|
|
92
|
+
import { schemaDescriptor, schemasAgree } from "#schema.ts"
|
|
93
|
+
import { arrayValue, recordValue, taggedValueOf } from "#values.ts"
|
|
90
94
|
|
|
91
95
|
type QueryRelation<Rels extends SchemaRelations> = Extract<Rels[keyof Rels], MatchOwner>
|
|
92
96
|
|
|
@@ -481,8 +485,8 @@ interface ResolvedBindings {
|
|
|
481
485
|
|
|
482
486
|
/**
|
|
483
487
|
* The MINT slot of a variable, the runtime twin of {@link MintSlotOf}: (i)
|
|
484
|
-
* verifies the mint owner
|
|
485
|
-
*
|
|
488
|
+
* verifies the mint owner agrees with the schema's ordered declaration — a
|
|
489
|
+
* conflicting declaration is refused, naming its label — and (ii)
|
|
486
490
|
* returns the descriptor it was minted at plus the law-computed class read
|
|
487
491
|
* off the schema's frozen class map. Because {@link fieldJoins} is an
|
|
488
492
|
* equality, judging every binding position against this one slot makes all
|
|
@@ -495,16 +499,16 @@ function mintSlotOf(context: ChainContext, ref: AnyVar): ClassedField {
|
|
|
495
499
|
// the descriptor it projected and the carrier class that survived
|
|
496
500
|
// the projection (aggregate outputs are bare derived scalars).
|
|
497
501
|
const column = facade.columns.get(ref.column)
|
|
498
|
-
if (column === undefined || (facade.source.schema as AnySchema
|
|
502
|
+
if (column === undefined || !schemasAgree(facade.source.schema as AnySchema, context.theory)) {
|
|
499
503
|
throw new AuthoringError({
|
|
500
504
|
message: `the variable ${ref.label} was minted from a query template schema ${context.theory.name} does not own`
|
|
501
505
|
})
|
|
502
506
|
}
|
|
503
507
|
return column.slot ?? { field: ref.field, class: undefined }
|
|
504
508
|
}
|
|
505
|
-
if (context.theory.relations[ref.owner.name]
|
|
509
|
+
if (!membersAgree(context.theory.relations[ref.owner.name], ref.owner)) {
|
|
506
510
|
throw new AuthoringError({
|
|
507
|
-
message: `the variable ${ref.label} was minted from a relation schema ${context.theory.name} does not declare —
|
|
511
|
+
message: `the variable ${ref.label} was minted from a relation schema ${context.theory.name} does not declare — use an equivalent ordered declaration`
|
|
508
512
|
})
|
|
509
513
|
}
|
|
510
514
|
return { field: ref.field, class: context.classes[ref.owner.name]?.[ref.column] }
|
|
@@ -532,7 +536,7 @@ function membershipSet(
|
|
|
532
536
|
})
|
|
533
537
|
}
|
|
534
538
|
const seen = new Set<string>()
|
|
535
|
-
const members = value
|
|
539
|
+
const members = arrayValue(context, value, function memberName(context, member) {
|
|
536
540
|
if (typeof member !== "string") {
|
|
537
541
|
throw literalShapeError(context, `a ${roster.name} handle name (string)`, member)
|
|
538
542
|
}
|
|
@@ -548,22 +552,30 @@ function membershipSet(
|
|
|
548
552
|
return { name: `∈ ${roster.name} ${JSON.stringify(key)}`, members: Object.freeze(members) }
|
|
549
553
|
}
|
|
550
554
|
|
|
555
|
+
function queryRecord(context: string, input: unknown): Readonly<Record<string, unknown>> {
|
|
556
|
+
return recordValue(context, input, typeof input === "object" && input !== null ? Object.keys(input) : [])
|
|
557
|
+
}
|
|
558
|
+
|
|
551
559
|
function resolveBindings(
|
|
552
560
|
context: ChainContext,
|
|
553
561
|
label: string,
|
|
554
|
-
|
|
555
|
-
bindings:
|
|
562
|
+
input: MatchOwner,
|
|
563
|
+
bindings: unknown,
|
|
556
564
|
joins: (a: ClassedField, b: ClassedField) => boolean = fieldJoins
|
|
557
565
|
): ResolvedBindings {
|
|
566
|
+
const relation = context.theory.relations[input.name]
|
|
567
|
+
if (relation === undefined || !membersAgree(relation, input)) {
|
|
568
|
+
throw new AuthoringError({
|
|
569
|
+
message: `${label}: schema ${context.theory.name} does not declare this ordered relation`
|
|
570
|
+
})
|
|
571
|
+
}
|
|
572
|
+
// Retain the schema-owned declaration, not mutable caller metadata.
|
|
558
573
|
const entries: BindingEntry[] = []
|
|
559
574
|
const vars: AnyVar[] = []
|
|
560
575
|
const uses: ParamUse[] = []
|
|
561
576
|
const relationClasses = context.classes[relation.name]
|
|
562
577
|
const ordered = sealedFieldsOf(relation)
|
|
563
|
-
for (const [fieldName, value] of Object.entries(bindings)) {
|
|
564
|
-
if (value === undefined) {
|
|
565
|
-
continue
|
|
566
|
-
}
|
|
578
|
+
for (const [fieldName, value] of Object.entries(queryRecord(label, bindings))) {
|
|
567
579
|
const declared = ordered.find(function byName(candidate) {
|
|
568
580
|
return candidate.name === fieldName
|
|
569
581
|
})
|
|
@@ -627,7 +639,10 @@ function resolveBindings(
|
|
|
627
639
|
})
|
|
628
640
|
)
|
|
629
641
|
} else {
|
|
630
|
-
bound = Object.freeze({
|
|
642
|
+
bound = Object.freeze({
|
|
643
|
+
kind: "literal" as const,
|
|
644
|
+
value: ownLiteral(`${label}.${fieldName}`, declared.field, value, "binding")
|
|
645
|
+
})
|
|
631
646
|
}
|
|
632
647
|
entries.push(Object.freeze({ field: fieldName, data: declared.field, class: fieldClass, term: bound }))
|
|
633
648
|
}
|
|
@@ -683,9 +698,22 @@ function sideUses(op: CmpKind, side: CmpTermData, sibling: CmpTermData, uses: Pa
|
|
|
683
698
|
}
|
|
684
699
|
|
|
685
700
|
function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
|
|
701
|
+
queryRecord("query condition", cond)
|
|
686
702
|
if (cond.cond === "cmp") {
|
|
687
|
-
|
|
688
|
-
|
|
703
|
+
let lhs = cmpTermDataOf(cond.lhs)
|
|
704
|
+
let rhs = cmpTermDataOf(cond.rhs)
|
|
705
|
+
if (lhs.kind === "literal" && rhs.kind === "var") {
|
|
706
|
+
lhs = Object.freeze({
|
|
707
|
+
kind: "literal",
|
|
708
|
+
value: ownLiteral("comparison literal", rhs.ref.field, lhs.value, cond.op)
|
|
709
|
+
})
|
|
710
|
+
}
|
|
711
|
+
if (rhs.kind === "literal" && lhs.kind === "var") {
|
|
712
|
+
rhs = Object.freeze({
|
|
713
|
+
kind: "literal",
|
|
714
|
+
value: ownLiteral("comparison literal", lhs.ref.field, rhs.value, cond.op)
|
|
715
|
+
})
|
|
716
|
+
}
|
|
689
717
|
sideUses(cond.op, lhs, rhs, uses)
|
|
690
718
|
sideUses(cond.op, rhs, lhs, uses)
|
|
691
719
|
if (cond.op === "allen") {
|
|
@@ -705,8 +733,8 @@ function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
|
|
|
705
733
|
return Object.freeze({ kind: "cmp" as const, op: { kind: cond.op }, lhs, rhs })
|
|
706
734
|
}
|
|
707
735
|
if (cond.cond === "tree") {
|
|
708
|
-
const children = cond.children
|
|
709
|
-
return condDataOf(child, uses)
|
|
736
|
+
const children = arrayValue("query condition children", cond.children, function lowerChild(_context, child) {
|
|
737
|
+
return condDataOf(child as AnyCond, uses)
|
|
710
738
|
})
|
|
711
739
|
return Object.freeze({ kind: "tree" as const, op: cond.op, children: Object.freeze(children) })
|
|
712
740
|
}
|
|
@@ -716,25 +744,22 @@ function condDataOf(cond: AnyCond, uses: ParamUse[]): CondData {
|
|
|
716
744
|
}
|
|
717
745
|
|
|
718
746
|
function advanceWhere(context: ChainContext, state: RuleBuildState, cond: AnyCond): RuleBuildState {
|
|
747
|
+
queryRecord("query condition", cond)
|
|
719
748
|
if (typeof cond !== "object" || cond === null || !("cond" in cond)) {
|
|
720
749
|
throw new AuthoringError({ message: "where() takes a comparison, an and()/or() tree, or a negated atom" })
|
|
721
750
|
}
|
|
722
751
|
if (cond.cond === "notInterior") {
|
|
723
|
-
|
|
724
|
-
Object.entries(cond.bindings ?? {}).filter(function defined([, value]) {
|
|
725
|
-
return value !== undefined
|
|
726
|
-
})
|
|
727
|
-
)
|
|
728
|
-
return notInteriorAdvance(context, state, cond.name, bindings)
|
|
752
|
+
return notInteriorAdvance(context, state, cond.name, queryRecord("negated interior bindings", cond.bindings))
|
|
729
753
|
}
|
|
730
754
|
if (cond.cond === "not") {
|
|
731
755
|
const relation: MatchOwner = cond.relation
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
756
|
+
const resolved = resolveBindings(
|
|
757
|
+
context,
|
|
758
|
+
`negated relation ${relation.name}`,
|
|
759
|
+
relation,
|
|
760
|
+
cond.bindings,
|
|
761
|
+
fieldAntiJoins
|
|
736
762
|
)
|
|
737
|
-
const resolved = resolveBindings(context, `negated relation ${relation.name}`, relation, bindings, fieldAntiJoins)
|
|
738
763
|
return Object.freeze({
|
|
739
764
|
items: Object.freeze([...state.items, Object.freeze({ kind: "negated" as const, atom: resolved.atom })]),
|
|
740
765
|
bound: state.bound,
|
|
@@ -757,10 +782,7 @@ function advanceInterior(
|
|
|
757
782
|
kind: "interior" | "negatedInterior"
|
|
758
783
|
): RuleBuildState {
|
|
759
784
|
const resolved: Array<{ readonly key: string; readonly ref: AnyVar }> = []
|
|
760
|
-
for (const [key, value] of Object.entries(bindings)) {
|
|
761
|
-
if (value === undefined) {
|
|
762
|
-
continue
|
|
763
|
-
}
|
|
785
|
+
for (const [key, value] of Object.entries(queryRecord(`interior ${target.name} bindings`, bindings))) {
|
|
764
786
|
if (!isTerm(value) || value[term] !== "var") {
|
|
765
787
|
throw new AuthoringError({
|
|
766
788
|
message: `interior ${target.name}: position ${key} takes a variable — bind literals and params through where()/match()`
|
|
@@ -1177,28 +1199,43 @@ interface ImportEntry {
|
|
|
1177
1199
|
|
|
1178
1200
|
const importEntries = new WeakMap<object, ImportEntry>()
|
|
1179
1201
|
const importTables = new WeakSet<InteriorData>()
|
|
1202
|
+
const importOrigins = new WeakMap<InteriorData, InteriorData>()
|
|
1203
|
+
|
|
1204
|
+
function snapshotQueryData<A>(input: A): A {
|
|
1205
|
+
return snapshotData(input, (source, snapshot) => {
|
|
1206
|
+
// Detached imports retain their authoring identity for deduplication.
|
|
1207
|
+
if (importTables.has(source as InteriorData)) {
|
|
1208
|
+
const table = snapshot as InteriorData
|
|
1209
|
+
importTables.add(table)
|
|
1210
|
+
importOrigins.set(table, importOrigins.get(source as InteriorData) ?? (source as InteriorData))
|
|
1211
|
+
const inner = importInner.get(source as InteriorData)
|
|
1212
|
+
if (inner !== undefined) importInner.set(table, inner)
|
|
1213
|
+
}
|
|
1214
|
+
})
|
|
1215
|
+
}
|
|
1180
1216
|
|
|
1181
1217
|
function importEntryOf(context: ChainContext, imported: AnyQuery): ImportEntry {
|
|
1182
1218
|
const cached = importEntries.get(imported)
|
|
1183
1219
|
if (cached !== undefined) {
|
|
1184
|
-
if (imported.schema
|
|
1220
|
+
if (!schemasAgree(imported.schema, context.theory)) {
|
|
1185
1221
|
throw new AuthoringError({
|
|
1186
1222
|
message: `${contextLabel(context)}: the imported query belongs to schema ${imported.schema.name}, not ${context.theory.name} — templates compose within one schema`
|
|
1187
1223
|
})
|
|
1188
1224
|
}
|
|
1189
1225
|
return cached
|
|
1190
1226
|
}
|
|
1191
|
-
if (imported.schema
|
|
1227
|
+
if (!schemasAgree(imported.schema, context.theory)) {
|
|
1192
1228
|
throw new AuthoringError({
|
|
1193
1229
|
message: `${contextLabel(context)}: the imported query belongs to schema ${imported.schema.name}, not ${context.theory.name} — templates compose within one schema`
|
|
1194
1230
|
})
|
|
1195
1231
|
}
|
|
1196
|
-
|
|
1232
|
+
const data = imported.data
|
|
1233
|
+
if (data.kind === "reach") {
|
|
1197
1234
|
throw new AuthoringError({
|
|
1198
1235
|
message: `${contextLabel(context)}: a recursive query is not importable as a relation expression yet — declare the recursion on the consuming query (C05 boundary, recorded)`
|
|
1199
1236
|
})
|
|
1200
1237
|
}
|
|
1201
|
-
const userParams =
|
|
1238
|
+
const userParams = data.params.filter(function userSupplied(entry) {
|
|
1202
1239
|
return entry.membership === undefined
|
|
1203
1240
|
})
|
|
1204
1241
|
if (userParams.length > 0) {
|
|
@@ -1211,10 +1248,10 @@ function importEntryOf(context: ChainContext, imported: AnyQuery): ImportEntry {
|
|
|
1211
1248
|
const facade = importFacadeOf(imported)
|
|
1212
1249
|
const table: InteriorData = Object.freeze({
|
|
1213
1250
|
name: facade.owner.name,
|
|
1214
|
-
finds:
|
|
1215
|
-
rules:
|
|
1251
|
+
finds: data.finds,
|
|
1252
|
+
rules: data.rules
|
|
1216
1253
|
})
|
|
1217
|
-
const entry: ImportEntry = Object.freeze({ table, inner:
|
|
1254
|
+
const entry: ImportEntry = Object.freeze({ table, inner: data.interiors })
|
|
1218
1255
|
importEntries.set(imported, entry)
|
|
1219
1256
|
importTables.add(table)
|
|
1220
1257
|
importInner.set(table, entry.inner)
|
|
@@ -1242,8 +1279,9 @@ function referencedImports(
|
|
|
1242
1279
|
continue
|
|
1243
1280
|
}
|
|
1244
1281
|
const target = item.target as InteriorData
|
|
1245
|
-
|
|
1246
|
-
|
|
1282
|
+
const origin = importOrigins.get(target) ?? target
|
|
1283
|
+
if (importTables.has(target) && !seen.has(origin)) {
|
|
1284
|
+
seen.add(origin)
|
|
1247
1285
|
out.push(target)
|
|
1248
1286
|
}
|
|
1249
1287
|
}
|
|
@@ -1280,7 +1318,8 @@ function spliceImports(
|
|
|
1280
1318
|
const names = new Set<string>()
|
|
1281
1319
|
const added = new Set<InteriorData>()
|
|
1282
1320
|
function add(table: InteriorData): void {
|
|
1283
|
-
|
|
1321
|
+
const origin = importOrigins.get(table) ?? table
|
|
1322
|
+
if (added.has(origin)) {
|
|
1284
1323
|
return
|
|
1285
1324
|
}
|
|
1286
1325
|
if (names.has(table.name)) {
|
|
@@ -1288,7 +1327,7 @@ function spliceImports(
|
|
|
1288
1327
|
message: `query: the derived-table name ${table.name} appears twice after import splicing — rename the colliding interior (names are unique)`
|
|
1289
1328
|
})
|
|
1290
1329
|
}
|
|
1291
|
-
added.add(
|
|
1330
|
+
added.add(origin)
|
|
1292
1331
|
names.add(table.name)
|
|
1293
1332
|
spliced.push(table)
|
|
1294
1333
|
}
|
|
@@ -1330,15 +1369,13 @@ function notInteriorAdvance(
|
|
|
1330
1369
|
|
|
1331
1370
|
function findColumns(context: ChainContext, entries: Readonly<Record<string, unknown>>): FindColumn[] {
|
|
1332
1371
|
const columns: FindColumn[] = []
|
|
1372
|
+
queryRecord("query find", entries)
|
|
1333
1373
|
// Nonrecursive derived stages (interiors) may emit aggregate/computed
|
|
1334
1374
|
// outputs (C05: "do not leave Interior.rules projection only"); ONLY the
|
|
1335
1375
|
// recursive feedback cycle stays projection-only — no aggregate,
|
|
1336
1376
|
// arithmetic-created value or negation flows through it.
|
|
1337
1377
|
const recName = context.kind === "rec-base" || context.kind === "rec-arm" ? context.self.name : undefined
|
|
1338
1378
|
for (const [name, entry] of Object.entries(entries)) {
|
|
1339
|
-
if (entry === undefined) {
|
|
1340
|
-
continue
|
|
1341
|
-
}
|
|
1342
1379
|
if (recName !== undefined && !(isTerm(entry) && entry[term] === "var")) {
|
|
1343
1380
|
throw new AuthoringError({
|
|
1344
1381
|
message: `rec ${recName}: a rec head projects bound variables only — no aggregate or arithmetic-created value flows through the feedback cycle (compute over the finished set in the main rules)`
|
|
@@ -1540,7 +1577,7 @@ function paramRegistryOf(
|
|
|
1540
1577
|
if (existing.anchor !== undefined && use.anchor !== undefined) {
|
|
1541
1578
|
const registered = rosterOf(existing.anchor)
|
|
1542
1579
|
const anchored = rosterOf(use.anchor)
|
|
1543
|
-
if (registered
|
|
1580
|
+
if (!rostersAgree(registered, anchored)) {
|
|
1544
1581
|
throw new AuthoringError({
|
|
1545
1582
|
message: `query param ${use.name} is anchored at ${renderParamAnchor(registered)} and at ${renderParamAnchor(anchored)} — a closed-anchored param translates handle names through ONE roster (one name, one domain); name the params differently`
|
|
1546
1583
|
})
|
|
@@ -1627,7 +1664,7 @@ function alignedHeadOf(label: string, rules: readonly RuleData[]): readonly Find
|
|
|
1627
1664
|
}
|
|
1628
1665
|
rule.finds.forEach(function verifyClosedSlice(column, position) {
|
|
1629
1666
|
const lead = first.finds[position]
|
|
1630
|
-
if (lead !== undefined && column.closed
|
|
1667
|
+
if (lead !== undefined && !rostersAgree(column.closed, lead.closed)) {
|
|
1631
1668
|
throw new AuthoringError({
|
|
1632
1669
|
message: `every rule of ${label} derives the same head — the head column ${lead.name} is ${renderClosedSlice(lead.closed)} in rule 0 but ${renderClosedSlice(column.closed)} in rule ${index} (one column decodes through one roster)`
|
|
1633
1670
|
})
|
|
@@ -1671,11 +1708,17 @@ function afterMainError(what: string): Error {
|
|
|
1671
1708
|
}
|
|
1672
1709
|
|
|
1673
1710
|
function makeRawQuery(
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1711
|
+
inputTheory: AnySchema,
|
|
1712
|
+
inputInteriors: readonly InteriorData[],
|
|
1713
|
+
inputRec: RecData | undefined,
|
|
1714
|
+
inputRules: readonly RuleData[]
|
|
1678
1715
|
): RawQuery {
|
|
1716
|
+
const { theory, interiors, rec, rules } = snapshotQueryData({
|
|
1717
|
+
theory: inputTheory,
|
|
1718
|
+
interiors: inputInteriors,
|
|
1719
|
+
rec: inputRec,
|
|
1720
|
+
rules: inputRules
|
|
1721
|
+
})
|
|
1679
1722
|
const mergedFinds = alignedHeadOf("a query", rules)
|
|
1680
1723
|
const first = rules[0]
|
|
1681
1724
|
if (first === undefined) {
|
|
@@ -1704,8 +1747,12 @@ function makeRawQuery(
|
|
|
1704
1747
|
params
|
|
1705
1748
|
})
|
|
1706
1749
|
const value: RawQuery = {
|
|
1707
|
-
schema
|
|
1708
|
-
|
|
1750
|
+
get schema() {
|
|
1751
|
+
return snapshotData(theory)
|
|
1752
|
+
},
|
|
1753
|
+
get data() {
|
|
1754
|
+
return snapshotQueryData(data)
|
|
1755
|
+
},
|
|
1709
1756
|
rule(build) {
|
|
1710
1757
|
const built = build(makeRawScope({ kind: "query", classes: theory.classes, theory, ...env }))
|
|
1711
1758
|
return makeRawQuery(theory, interiors, rec, [...rules, built.rule])
|
|
@@ -1799,9 +1846,9 @@ function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>
|
|
|
1799
1846
|
const sealedRec: RecData["rec"] = [firstRec, ...rec.slice(1)]
|
|
1800
1847
|
const recData: RecData = Object.freeze({
|
|
1801
1848
|
name,
|
|
1802
|
-
finds: sealedFinds,
|
|
1803
|
-
base: sealedBase,
|
|
1804
|
-
rec: sealedRec
|
|
1849
|
+
finds: Object.freeze(sealedFinds),
|
|
1850
|
+
base: Object.freeze(sealedBase),
|
|
1851
|
+
rec: Object.freeze(sealedRec)
|
|
1805
1852
|
})
|
|
1806
1853
|
return recData
|
|
1807
1854
|
}
|
|
@@ -1882,112 +1929,21 @@ function makeQueryReachStart<Rels extends SchemaRelations, Classes extends Schem
|
|
|
1882
1929
|
function query<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
1883
1930
|
theory: Schema<Rels, Classes>
|
|
1884
1931
|
): QueryStart<Rels, Classes> {
|
|
1885
|
-
return makeQueryStart<Rels, Classes, Record<never, never>>(theory, [])
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
function taggedHandleId(
|
|
1889
|
-
context: string,
|
|
1890
|
-
closed: { readonly name: string; readonly handles: readonly string[] },
|
|
1891
|
-
value: unknown
|
|
1892
|
-
): TaggedValue {
|
|
1893
|
-
if (typeof value !== "string") {
|
|
1894
|
-
throw literalShapeError(context, `a ${closed.name} handle name (string)`, value)
|
|
1895
|
-
}
|
|
1896
|
-
const id = closed.handles.indexOf(value)
|
|
1897
|
-
if (id < 0) {
|
|
1898
|
-
throw new AuthoringError({
|
|
1899
|
-
message: `${context}: "${value}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`
|
|
1900
|
-
})
|
|
1901
|
-
}
|
|
1902
|
-
return { kind: "u64", value: BigInt(id) }
|
|
1932
|
+
return makeQueryStart<Rels, Classes, Record<never, never>>(schemaDescriptor(theory), [])
|
|
1903
1933
|
}
|
|
1904
1934
|
|
|
1905
1935
|
function taggedAtElementDomain(context: string, element: "u64" | "i64" | "f64", value: unknown): TaggedValue {
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
if (Number.isNaN(value.start) || Number.isNaN(value.end) || !(value.start < value.end)) {
|
|
1912
|
-
throw literalShapeError(context, "a nonempty float interval with non-NaN endpoints", value)
|
|
1913
|
-
}
|
|
1914
|
-
return {
|
|
1915
|
-
kind: "intervalF64",
|
|
1916
|
-
start: Object.is(value.start, -0) ? 0 : value.start,
|
|
1917
|
-
end: Object.is(value.end, -0) ? 0 : value.end
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
throw literalShapeError(context, "number (point) or { start, end } numbers (interval)", value)
|
|
1921
|
-
}
|
|
1922
|
-
if (typeof value === "bigint") {
|
|
1923
|
-
if (element === "u64") {
|
|
1924
|
-
return { kind: "u64", value }
|
|
1925
|
-
}
|
|
1926
|
-
return { kind: "i64", value }
|
|
1927
|
-
}
|
|
1928
|
-
if (isIntervalValue(value)) {
|
|
1929
|
-
if (element === "u64") {
|
|
1930
|
-
return { kind: "intervalU64", start: value.start, end: value.end }
|
|
1931
|
-
}
|
|
1932
|
-
return { kind: "intervalI64", start: value.start, end: value.end }
|
|
1933
|
-
}
|
|
1934
|
-
throw literalShapeError(context, "bigint (point) or { start, end } (interval)", value)
|
|
1936
|
+
const field: AnyField =
|
|
1937
|
+
typeof value === "number" || typeof value === "bigint"
|
|
1938
|
+
? { u64: u64Field, i64: i64Field, f64: f64Field }[element]
|
|
1939
|
+
: { kind: "interval", element, width: undefined }
|
|
1940
|
+
return taggedValueOf(context, field, value)
|
|
1935
1941
|
}
|
|
1936
1942
|
|
|
1937
1943
|
function taggedLiteral(context: string, field: AnyField, value: unknown): TaggedValue {
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
}
|
|
1942
|
-
switch (field.kind) {
|
|
1943
|
-
case "bool": {
|
|
1944
|
-
if (typeof value !== "boolean") {
|
|
1945
|
-
throw literalShapeError(context, "boolean", value)
|
|
1946
|
-
}
|
|
1947
|
-
return { kind: "bool", value }
|
|
1948
|
-
}
|
|
1949
|
-
case "u64": {
|
|
1950
|
-
if (typeof value !== "bigint") {
|
|
1951
|
-
throw literalShapeError(context, "bigint", value)
|
|
1952
|
-
}
|
|
1953
|
-
return { kind: "u64", value }
|
|
1954
|
-
}
|
|
1955
|
-
case "i64": {
|
|
1956
|
-
if (typeof value !== "bigint") {
|
|
1957
|
-
throw literalShapeError(context, "bigint", value)
|
|
1958
|
-
}
|
|
1959
|
-
return { kind: "i64", value }
|
|
1960
|
-
}
|
|
1961
|
-
case "f64": {
|
|
1962
|
-
if (typeof value !== "number") {
|
|
1963
|
-
throw literalShapeError(context, "number", value)
|
|
1964
|
-
}
|
|
1965
|
-
return { kind: "f64", value }
|
|
1966
|
-
}
|
|
1967
|
-
case "uuid": {
|
|
1968
|
-
if (!Uuid.isUuid(value)) {
|
|
1969
|
-
throw literalShapeError(context, "a UUID (canonical UUID text)", value)
|
|
1970
|
-
}
|
|
1971
|
-
return { kind: "uuid", value }
|
|
1972
|
-
}
|
|
1973
|
-
case "str": {
|
|
1974
|
-
if (typeof value !== "string") {
|
|
1975
|
-
throw literalShapeError(context, "string", value)
|
|
1976
|
-
}
|
|
1977
|
-
if (!value.isWellFormed()) {
|
|
1978
|
-
throw literalShapeError(context, "well-formed string", value)
|
|
1979
|
-
}
|
|
1980
|
-
return { kind: "string", value }
|
|
1981
|
-
}
|
|
1982
|
-
case "bytes": {
|
|
1983
|
-
if (!(value instanceof Uint8Array)) {
|
|
1984
|
-
throw literalShapeError(context, "Uint8Array", value)
|
|
1985
|
-
}
|
|
1986
|
-
return { kind: "fixedBytes", value }
|
|
1987
|
-
}
|
|
1988
|
-
case "interval":
|
|
1989
|
-
return taggedAtElementDomain(context, field.element, value)
|
|
1990
|
-
}
|
|
1944
|
+
return field.kind === "interval"
|
|
1945
|
+
? taggedAtElementDomain(context, field.element, value)
|
|
1946
|
+
: taggedValueOf(context, field, value)
|
|
1991
1947
|
}
|
|
1992
1948
|
|
|
1993
1949
|
/**
|
|
@@ -1996,7 +1952,7 @@ function taggedLiteral(context: string, field: AnyField, value: unknown): Tagged
|
|
|
1996
1952
|
* contributes its element domain, a scalar sibling its own type. At
|
|
1997
1953
|
* `pointIn` the operand order is interval-left, point-right, so an
|
|
1998
1954
|
* interval-shaped literal beside a scalar element-typed sibling is the LEGAL
|
|
1999
|
-
* interval operand of `pointIn(t,
|
|
1955
|
+
* interval operand of `pointIn(t, { start, end })`; under every other operator an
|
|
2000
1956
|
* interval shape against a scalar sibling stays refused.
|
|
2001
1957
|
*/
|
|
2002
1958
|
function taggedCmpLiteral(context: string, sibling: AnyField, value: unknown, op: CmpKind | "binding"): TaggedValue {
|
|
@@ -2017,6 +1973,14 @@ function taggedCmpLiteral(context: string, sibling: AnyField, value: unknown, op
|
|
|
2017
1973
|
return taggedLiteral(context, sibling, value)
|
|
2018
1974
|
}
|
|
2019
1975
|
|
|
1976
|
+
/** Validate and own a literal when the query is authored, before caller mutation. */
|
|
1977
|
+
function ownLiteral(context: string, field: AnyField, value: unknown, op: CmpKind | "binding"): unknown {
|
|
1978
|
+
const tagged = taggedCmpLiteral(context, field, value, op)
|
|
1979
|
+
if (rosterOf(field) !== undefined) return value
|
|
1980
|
+
if ("value" in tagged) return tagged.value
|
|
1981
|
+
return Object.freeze({ start: tagged.start, end: tagged.end })
|
|
1982
|
+
}
|
|
1983
|
+
|
|
2020
1984
|
interface LowerContext {
|
|
2021
1985
|
readonly theory: AnySchema
|
|
2022
1986
|
readonly relationIds: ReadonlyMap<string, number>
|
|
@@ -2054,7 +2018,7 @@ function paramIdOf(ctx: LowerContext, name: string): number {
|
|
|
2054
2018
|
|
|
2055
2019
|
function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds): AtomIr {
|
|
2056
2020
|
const member = ctx.theory.relations[atom.relation.name]
|
|
2057
|
-
if (member
|
|
2021
|
+
if (!membersAgree(member, atom.relation)) {
|
|
2058
2022
|
throw new AuthoringError({
|
|
2059
2023
|
message: `query lowering: relation ${atom.relation.name} is not the relation value schema ${ctx.theory.name} declares`
|
|
2060
2024
|
})
|
|
@@ -2306,20 +2270,21 @@ function lowerRule(ctx: LowerContext, rule: RuleData): RuleIr {
|
|
|
2306
2270
|
|
|
2307
2271
|
function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
2308
2272
|
const theory = q.schema
|
|
2273
|
+
const data = q.data
|
|
2309
2274
|
const relationIds = new Map<string, number>()
|
|
2310
2275
|
Object.keys(theory.relations).forEach(function assignOrdinal(name, index) {
|
|
2311
2276
|
relationIds.set(name, index)
|
|
2312
2277
|
})
|
|
2313
2278
|
const interiorIds = new Map<string, number>()
|
|
2314
|
-
|
|
2279
|
+
data.interiors.forEach(function assignInteriorId(interior, index) {
|
|
2315
2280
|
interiorIds.set(interior.name, index)
|
|
2316
2281
|
})
|
|
2317
|
-
if (
|
|
2318
|
-
interiorIds.set(
|
|
2282
|
+
if (data.kind === "reach") {
|
|
2283
|
+
interiorIds.set(data.rec.name, data.interiors.length)
|
|
2319
2284
|
}
|
|
2320
2285
|
const paramIds = new Map<string, number>()
|
|
2321
2286
|
const params = new Map<string, ParamEntry>()
|
|
2322
|
-
|
|
2287
|
+
data.params.forEach(function assignParamId(entry, index) {
|
|
2323
2288
|
if (entry.anchor === undefined) {
|
|
2324
2289
|
throw new AuthoringError({
|
|
2325
2290
|
message: `query param ${entry.name} has no field-anchored use — bind it in an atom or compare it against a bound variable`
|
|
@@ -2329,7 +2294,7 @@ function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
|
2329
2294
|
params.set(entry.name, entry)
|
|
2330
2295
|
})
|
|
2331
2296
|
const ctx: LowerContext = { theory, relationIds, interiorIds, paramIds, params }
|
|
2332
|
-
const interiors =
|
|
2297
|
+
const interiors = data.interiors.map(function lowerInterior(interior) {
|
|
2333
2298
|
return {
|
|
2334
2299
|
head: interior.finds.map(headTermOf),
|
|
2335
2300
|
rules: interior.rules.map(function lowerInteriorRule(rule) {
|
|
@@ -2337,22 +2302,22 @@ function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
|
2337
2302
|
})
|
|
2338
2303
|
}
|
|
2339
2304
|
})
|
|
2340
|
-
const head =
|
|
2341
|
-
const rules =
|
|
2305
|
+
const head = data.finds.map(headTermOf)
|
|
2306
|
+
const rules = data.rules.map(function lowerMainRule(rule) {
|
|
2342
2307
|
return lowerRule(ctx, rule)
|
|
2343
2308
|
})
|
|
2344
|
-
if (
|
|
2309
|
+
if (data.kind === "cq") {
|
|
2345
2310
|
return parseQueryIr({ kind: "cq", interiors, head, rules })
|
|
2346
2311
|
}
|
|
2347
2312
|
return parseQueryIr({
|
|
2348
2313
|
kind: "reach",
|
|
2349
2314
|
interiors,
|
|
2350
2315
|
rec: {
|
|
2351
|
-
head:
|
|
2352
|
-
base:
|
|
2316
|
+
head: data.rec.finds.map(headTermOf),
|
|
2317
|
+
base: data.rec.base.map(function lowerBase(rule) {
|
|
2353
2318
|
return lowerRule(ctx, rule)
|
|
2354
2319
|
}),
|
|
2355
|
-
rec:
|
|
2320
|
+
rec: data.rec.rec.map(function lowerRecArm(rule) {
|
|
2356
2321
|
return lowerRule(ctx, rule)
|
|
2357
2322
|
})
|
|
2358
2323
|
},
|
|
@@ -2364,6 +2329,7 @@ function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
|
2364
2329
|
export type {
|
|
2365
2330
|
AnyQuery,
|
|
2366
2331
|
AnyRuleValue,
|
|
2332
|
+
ChainContext,
|
|
2367
2333
|
Query,
|
|
2368
2334
|
QueryData,
|
|
2369
2335
|
QueryParams,
|
|
@@ -2378,4 +2344,4 @@ export type {
|
|
|
2378
2344
|
RuleValue,
|
|
2379
2345
|
TermOps
|
|
2380
2346
|
}
|
|
2381
|
-
export { lowerQuery, query, taggedCmpLiteral }
|
|
2347
|
+
export { alignedHeadOf, EMPTY_RULE, lowerQuery, makeRawChain, makeRawQuery, query, taggedCmpLiteral }
|