@bjornpagen/bumbledb 1.3.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COOKBOOK.md +153 -77
- package/README.md +107 -41
- package/dist/alternatives.d.ts +5 -6
- package/dist/alternatives.d.ts.map +1 -1
- package/dist/alternatives.js +5 -6
- package/dist/alternatives.js.map +1 -1
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +85 -0
- package/dist/bin.js.map +1 -0
- package/dist/capacity.d.ts +14 -17
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +7 -7
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +32 -40
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +86 -104
- package/dist/changes.js.map +1 -1
- package/dist/closed.d.ts +2 -2
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +5 -5
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +13 -27
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +20 -21
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +26 -50
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +75 -64
- package/dist/compile.js.map +1 -1
- package/dist/database/database.d.ts +98 -0
- package/dist/database/database.d.ts.map +1 -0
- package/dist/database/database.js +249 -0
- package/dist/database/database.js.map +1 -0
- package/dist/database/driver.d.ts +40 -0
- package/dist/database/driver.d.ts.map +1 -0
- package/dist/database/driver.js +68 -0
- package/dist/database/driver.js.map +1 -0
- package/dist/database/fs.d.ts +12 -0
- package/dist/database/fs.d.ts.map +1 -0
- package/dist/database/fs.js +120 -0
- package/dist/database/fs.js.map +1 -0
- package/dist/database/generate.d.ts +26 -0
- package/dist/database/generate.d.ts.map +1 -0
- package/dist/database/generate.js +102 -0
- package/dist/database/generate.js.map +1 -0
- package/dist/database/hosted.d.ts +32 -0
- package/dist/database/hosted.d.ts.map +1 -0
- package/dist/database/hosted.js +44 -0
- package/dist/database/hosted.js.map +1 -0
- package/dist/database/io.d.ts +94 -0
- package/dist/database/io.d.ts.map +1 -0
- package/dist/database/io.js +35 -0
- package/dist/database/io.js.map +1 -0
- package/dist/database/mem.d.ts +26 -0
- package/dist/database/mem.d.ts.map +1 -0
- package/dist/database/mem.js +67 -0
- package/dist/database/mem.js.map +1 -0
- package/dist/database/migration.d.ts +55 -0
- package/dist/database/migration.d.ts.map +1 -0
- package/dist/database/migration.js +46 -0
- package/dist/database/migration.js.map +1 -0
- package/dist/database/s3.d.ts +36 -0
- package/dist/database/s3.d.ts.map +1 -0
- package/dist/database/s3.js +141 -0
- package/dist/database/s3.js.map +1 -0
- package/dist/db.d.ts +44 -128
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +163 -287
- package/dist/db.js.map +1 -1
- package/dist/engine.d.ts +7 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +2 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.d.ts +152 -46
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +107 -19
- package/dist/errors.js.map +1 -1
- package/dist/face.d.ts +5 -5
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +6 -6
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +4 -5
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +3 -3
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +60 -57
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -28
- package/dist/index.js.map +1 -1
- package/dist/judgment.d.ts +1 -1
- package/dist/judgment.js +1 -1
- package/dist/law.d.ts +16 -68
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +3 -3
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts +3 -3
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +35 -39
- package/dist/lower.js.map +1 -1
- package/dist/native/addon.d.ts +23 -0
- package/dist/native/addon.d.ts.map +1 -0
- package/dist/native/addon.js +15 -0
- package/dist/native/addon.js.map +1 -0
- package/dist/native/binding.d.ts +996 -0
- package/dist/native/json.d.ts +14 -0
- package/dist/native/json.d.ts.map +1 -0
- package/dist/native/json.js +73 -0
- package/dist/native/json.js.map +1 -0
- package/dist/native/load.d.ts +9 -0
- package/dist/native/load.d.ts.map +1 -0
- package/dist/native/load.js +30 -0
- package/dist/native/load.js.map +1 -0
- package/dist/native/op.d.ts +29 -0
- package/dist/native/op.d.ts.map +1 -0
- package/dist/native/op.js +50 -0
- package/dist/native/op.js.map +1 -0
- package/dist/query/atom.d.ts +13 -14
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +2 -2
- package/dist/query/atom.js.map +1 -1
- package/dist/query/compute.d.ts +4 -4
- package/dist/query/compute.d.ts.map +1 -1
- package/dist/query/compute.js +4 -4
- package/dist/query/compute.js.map +1 -1
- package/dist/query/find.d.ts +8 -8
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +1 -1
- package/dist/query/lower.d.ts +22 -17
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +230 -343
- package/dist/query/lower.js.map +1 -1
- package/dist/query/run.d.ts +5 -5
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +32 -15
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +20 -28
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +7 -7
- package/dist/query/scope.js.map +1 -1
- package/dist/query/segments.d.ts +2 -2
- package/dist/query/segments.d.ts.map +1 -1
- package/dist/query/segments.js +3 -3
- package/dist/query/segments.js.map +1 -1
- package/dist/relation.d.ts +1 -1
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +4 -4
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +15 -22
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +30 -39
- package/dist/result.js.map +1 -1
- package/dist/rows.d.ts +9 -33
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +8 -12
- package/dist/rows.js.map +1 -1
- package/dist/runtime.d.ts +17 -20
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +41 -205
- package/dist/runtime.js.map +1 -1
- package/dist/scalar.d.ts +4 -4
- package/dist/scalar.d.ts.map +1 -1
- package/dist/scalar.js +24 -8
- package/dist/scalar.js.map +1 -1
- package/dist/schema.d.ts +7 -18
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +18 -228
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +4 -4
- package/dist/selection.d.ts.map +1 -1
- package/dist/selection.js +5 -5
- package/dist/selection.js.map +1 -1
- package/dist/shape.d.ts +5 -5
- package/dist/shape.d.ts.map +1 -1
- package/dist/spec.d.ts +7 -79
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +3 -3
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +10 -10
- package/dist/statements.js.map +1 -1
- package/dist/uuid.d.ts +1 -1
- package/dist/uuid.d.ts.map +1 -1
- package/dist/uuid.js +1 -1
- package/dist/uuid.js.map +1 -1
- package/dist/values.d.ts +2 -2
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +2 -2
- package/dist/values.js.map +1 -1
- package/package.json +18 -18
- package/src/alternatives.ts +7 -8
- package/src/bin.ts +82 -0
- package/src/capacity.ts +20 -23
- package/src/changes.ts +134 -191
- package/src/closed.ts +5 -5
- package/src/codec.ts +35 -49
- package/src/compile.ts +91 -113
- package/src/database/database.ts +413 -0
- package/src/database/driver.ts +134 -0
- package/src/database/fs.ts +119 -0
- package/src/database/generate.ts +116 -0
- package/src/database/hosted.ts +102 -0
- package/src/database/io.ts +134 -0
- package/src/database/mem.ts +113 -0
- package/src/database/migration.ts +107 -0
- package/src/database/s3.ts +231 -0
- package/src/db.ts +290 -547
- package/src/engine.ts +14 -0
- package/src/errors.ts +118 -39
- package/src/face.ts +9 -9
- package/src/fields.ts +6 -7
- package/src/index.ts +92 -105
- package/src/judgment.ts +1 -1
- package/src/law.ts +18 -70
- package/src/lower.ts +61 -72
- package/src/native/addon.ts +54 -0
- package/src/native/binding.d.ts +996 -0
- package/src/native/json.ts +100 -0
- package/src/native/load.ts +31 -0
- package/src/native/op.ts +73 -0
- package/src/query/atom.ts +15 -16
- package/src/query/compute.ts +7 -7
- package/src/query/find.ts +8 -8
- package/src/query/lower.ts +270 -392
- package/src/query/run.ts +42 -21
- package/src/query/scope.ts +29 -37
- package/src/query/segments.ts +4 -4
- package/src/relation.ts +4 -4
- package/src/result.ts +53 -80
- package/src/rows.ts +18 -46
- package/src/runtime.ts +50 -277
- package/src/scalar.ts +32 -12
- package/src/schema.ts +23 -284
- package/src/selection.ts +6 -6
- package/src/shape.ts +5 -5
- package/src/spec.ts +7 -85
- package/src/statements.ts +11 -11
- package/src/uuid.ts +1 -1
- package/src/values.ts +4 -4
- package/dist/close.d.ts +0 -24
- package/dist/close.d.ts.map +0 -1
- package/dist/close.js +0 -34
- package/dist/close.js.map +0 -1
- package/dist/db-native.d.ts +0 -189
- package/dist/db-native.d.ts.map +0 -1
- package/dist/db-native.js +0 -13
- package/dist/db-native.js.map +0 -1
- package/dist/internal/log.d.ts +0 -26
- package/dist/internal/log.d.ts.map +0 -1
- package/dist/internal/log.js +0 -12
- package/dist/internal/log.js.map +0 -1
- package/dist/native.d.ts +0 -562
- package/dist/native.d.ts.map +0 -1
- package/dist/native.js +0 -123
- package/dist/native.js.map +0 -1
- package/dist/query/description.d.ts +0 -41
- package/dist/query/description.d.ts.map +0 -1
- package/dist/query/description.js +0 -311
- package/dist/query/description.js.map +0 -1
- package/dist/query/parse-ir.d.ts +0 -10
- package/dist/query/parse-ir.d.ts.map +0 -1
- package/dist/query/parse-ir.js +0 -280
- package/dist/query/parse-ir.js.map +0 -1
- package/dist/runtime-codes.d.ts +0 -2
- package/dist/runtime-codes.d.ts.map +0 -1
- package/dist/runtime-codes.js +0 -19
- package/dist/runtime-codes.js.map +0 -1
- package/dist/runtime-errors.d.ts +0 -138
- package/dist/runtime-errors.d.ts.map +0 -1
- package/dist/runtime-errors.js +0 -111
- package/dist/runtime-errors.js.map +0 -1
- package/dist/runtime-native.d.ts +0 -136
- package/dist/runtime-native.d.ts.map +0 -1
- package/dist/runtime-native.js +0 -5
- package/dist/runtime-native.js.map +0 -1
- package/dist/schema-file.d.ts +0 -7
- package/dist/schema-file.d.ts.map +0 -1
- package/dist/schema-file.js +0 -24
- package/dist/schema-file.js.map +0 -1
- package/pack-provenance.json +0 -7
- package/src/close.ts +0 -53
- package/src/db-native.ts +0 -235
- package/src/internal/log.ts +0 -46
- package/src/native.ts +0 -736
- package/src/query/description.ts +0 -424
- package/src/query/parse-ir.ts +0 -293
- package/src/runtime-codes.ts +0 -18
- package/src/runtime-errors.ts +0 -122
- package/src/runtime-native.ts +0 -181
- package/src/schema-file.ts +0 -38
package/src/query/lower.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { membersAgree, sealedFieldsOf } from "
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import { membersAgree, sealedFieldsOf } from "../closed.ts"
|
|
2
|
+
import { compiledOf } from "../compile.ts"
|
|
3
|
+
import { AuthoringError, internalError } from "../errors.ts"
|
|
4
|
+
import type { AnyClosedRoster, AnyField, IntervalField } from "../fields.ts"
|
|
4
5
|
import {
|
|
5
6
|
assertDeclarationOrderKey,
|
|
6
7
|
f64 as f64Field,
|
|
@@ -11,24 +12,30 @@ import {
|
|
|
11
12
|
rosterOf,
|
|
12
13
|
rostersAgree,
|
|
13
14
|
u64 as u64Field
|
|
14
|
-
} from "
|
|
15
|
-
import { snapshotData } from "
|
|
16
|
-
import type { Same } from "
|
|
17
|
-
import type { ClassRecordOf, SchemaClasses } from "
|
|
15
|
+
} from "../fields.ts"
|
|
16
|
+
import { snapshotData } from "../immutable.ts"
|
|
17
|
+
import type { Same } from "../judgment.ts"
|
|
18
|
+
import type { ClassRecordOf, SchemaClasses } from "../law.ts"
|
|
19
|
+
import type { QueryRef } from "../native/addon.ts"
|
|
20
|
+
import { addon } from "../native/addon.ts"
|
|
18
21
|
import type {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} from "
|
|
22
|
+
AtomIn,
|
|
23
|
+
BindingIn,
|
|
24
|
+
ConditionIn,
|
|
25
|
+
FindTermIn,
|
|
26
|
+
HeadTermIn,
|
|
27
|
+
QueryDiagnostic,
|
|
28
|
+
QueryIn,
|
|
29
|
+
RecStepIn,
|
|
30
|
+
RuleIn,
|
|
31
|
+
TermIn
|
|
32
|
+
} from "../native/binding.d.ts"
|
|
33
|
+
import { valueIn } from "../native/json.ts"
|
|
34
|
+
import { scalarWire } from "../scalar.ts"
|
|
35
|
+
import type { AnySchema, Schema, SchemaRelations } from "../schema.ts"
|
|
36
|
+
import { schemaDescriptor, schemasAgree } from "../schema.ts"
|
|
37
|
+
import type { QueryParam, TaggedValue } from "../spec.ts"
|
|
38
|
+
import { arrayValue, recordValue, taggedValueOf } from "../values.ts"
|
|
32
39
|
import type {
|
|
33
40
|
AggData,
|
|
34
41
|
AnyCond,
|
|
@@ -37,7 +44,6 @@ import type {
|
|
|
37
44
|
BindParamsShape,
|
|
38
45
|
CheckBindings,
|
|
39
46
|
CheckCond,
|
|
40
|
-
CmpData,
|
|
41
47
|
CmpKind,
|
|
42
48
|
CmpTermData,
|
|
43
49
|
CondData,
|
|
@@ -55,13 +61,11 @@ import type {
|
|
|
55
61
|
RecHead,
|
|
56
62
|
RuleData,
|
|
57
63
|
RuleItem
|
|
58
|
-
} from "
|
|
59
|
-
import { allen, and, eq, ge, gt, le, lt, ne, not, or, pointIn } from "
|
|
60
|
-
import
|
|
61
|
-
import {
|
|
62
|
-
import
|
|
63
|
-
import { count, max, mean, min, pack, sum } from "#query/find.ts"
|
|
64
|
-
import { parseQueryIr } from "#query/parse-ir.ts"
|
|
64
|
+
} from "./atom.ts"
|
|
65
|
+
import { allen, and, eq, ge, gt, le, lt, ne, not, or, pointIn } from "./atom.ts"
|
|
66
|
+
import { computeFieldOf, isComputeExpr, MAX_COMPUTE_DEPTH } from "./compute.ts"
|
|
67
|
+
import type { CheckFind, CheckRecFind, FindShape, HeadRecordOf, RowOfFind } from "./find.ts"
|
|
68
|
+
import { count, max, mean, min, pack, sum } from "./find.ts"
|
|
65
69
|
import type {
|
|
66
70
|
AnyVar,
|
|
67
71
|
ClassedField,
|
|
@@ -72,7 +76,7 @@ import type {
|
|
|
72
76
|
ParamsRecord,
|
|
73
77
|
ShapeOf,
|
|
74
78
|
VarsOf
|
|
75
|
-
} from "
|
|
79
|
+
} from "./scope.ts"
|
|
76
80
|
import {
|
|
77
81
|
fieldAntiJoins,
|
|
78
82
|
fieldJoins,
|
|
@@ -87,12 +91,8 @@ import {
|
|
|
87
91
|
makeSetParam,
|
|
88
92
|
renderFieldKind,
|
|
89
93
|
term
|
|
90
|
-
} from "
|
|
91
|
-
import { difference, intersection, isSegments, segmentField } from "
|
|
92
|
-
import { scalarWire } from "#scalar.ts"
|
|
93
|
-
import type { AnySchema, Schema, SchemaRelations } from "#schema.ts"
|
|
94
|
-
import { schemaDescriptor, schemasAgree } from "#schema.ts"
|
|
95
|
-
import { arrayValue, recordValue, taggedValueOf } from "#values.ts"
|
|
94
|
+
} from "./scope.ts"
|
|
95
|
+
import { difference, intersection, isSegments, segmentField } from "./segments.ts"
|
|
96
96
|
|
|
97
97
|
type QueryRelation<Rels extends SchemaRelations> = Extract<Rels[keyof Rels], MatchOwner>
|
|
98
98
|
|
|
@@ -319,8 +319,8 @@ interface InteriorRuleChain<
|
|
|
319
319
|
bindings: B & CheckInteriorBindings<B>
|
|
320
320
|
): InteriorRuleChain<Rels, P, Classes>
|
|
321
321
|
/**
|
|
322
|
-
* Nonrecursive derived stages emit aggregate/computed outputs too
|
|
323
|
-
*
|
|
322
|
+
* Nonrecursive derived stages emit aggregate/computed outputs too; only
|
|
323
|
+
* the RECURSIVE head stays projection-only.
|
|
324
324
|
*/
|
|
325
325
|
find<const F extends Readonly<Record<string, AnyVar>>>(
|
|
326
326
|
entries: F
|
|
@@ -345,7 +345,7 @@ interface RecRuleScope<Rels extends SchemaRelations, Classes extends SchemaClass
|
|
|
345
345
|
relation: R,
|
|
346
346
|
bindings: B & CheckBindings<Classes, MatchFields<R>, ClassRecordOf<Classes, R["name"]>, B>
|
|
347
347
|
): RecRuleChain<Rels, BindParamsShape<MatchFields<R>, B>, Classes>
|
|
348
|
-
/** Frozen finite nonrecursive imports may feed base/step
|
|
348
|
+
/** Frozen finite nonrecursive imports may feed base/step. */
|
|
349
349
|
match<Q extends AnyQuery, const B extends ImportMatchShape<Q>>(
|
|
350
350
|
imported: Q,
|
|
351
351
|
bindings: B
|
|
@@ -913,51 +913,6 @@ function findColumnOf(name: string, entry: unknown): FindColumn {
|
|
|
913
913
|
})
|
|
914
914
|
}
|
|
915
915
|
|
|
916
|
-
/**
|
|
917
|
-
* The orderable ban's pointed refusal
|
|
918
|
-
* § orderability): a closed reference is equality-and-membership only.
|
|
919
|
-
*/
|
|
920
|
-
function closedOrderError(context: string, position: string, vocabulary: string): Error {
|
|
921
|
-
return new AuthoringError({
|
|
922
|
-
message: `${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (equality, membership, and counting remain)`
|
|
923
|
-
})
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
function isOrderOp(op: CmpKind | "binding"): op is "lt" | "le" | "gt" | "ge" | "pointIn" {
|
|
927
|
-
return op === "lt" || op === "le" || op === "gt" || op === "ge" || op === "pointIn"
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
function assertBound(where: string, bound: ReadonlySet<AnyVar>, ref: AnyVar): void {
|
|
931
|
-
if (!bound.has(ref)) {
|
|
932
|
-
throw new AuthoringError({
|
|
933
|
-
message: `${where}: the variable ${ref.label} is not bound by a relation atom of the rule`
|
|
934
|
-
})
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
function assertInterval(where: string, ref: AnyVar): void {
|
|
939
|
-
if (ref.field.kind !== "interval") {
|
|
940
|
-
throw new AuthoringError({
|
|
941
|
-
message: `${where}: ${ref.label} is not interval-typed — the measure is defined over interval-typed variables only`
|
|
942
|
-
})
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
function assertNotClosed(where: string, position: string, ref: AnyVar): void {
|
|
947
|
-
const roster = rosterOf(ref.field)
|
|
948
|
-
if (roster !== undefined) {
|
|
949
|
-
throw closedOrderError(where, `${position} ${ref.label}`, roster.name)
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
function assertNumeric(where: string, position: string, ref: AnyVar): void {
|
|
954
|
-
if (ref.field.kind !== "u64" && ref.field.kind !== "i64" && ref.field.kind !== "f64") {
|
|
955
|
-
throw new AuthoringError({
|
|
956
|
-
message: `${where}: ${position} ${ref.label} is ${ref.field.kind}, not numeric — a fold reads u64/i64/f64 only`
|
|
957
|
-
})
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
|
|
961
916
|
/**
|
|
962
917
|
* The head column's slot: a projected variable keeps its mint slot; an
|
|
963
918
|
* aggregate output is a NEW derived scalar — its descriptor follows the
|
|
@@ -992,80 +947,24 @@ function findColumnSlotOf(context: ChainContext, column: FindColumn): ClassedFie
|
|
|
992
947
|
}
|
|
993
948
|
}
|
|
994
949
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
assertBound(where, bound, entry.left)
|
|
1000
|
-
assertBound(where, bound, entry.right)
|
|
1001
|
-
return
|
|
1002
|
-
}
|
|
1003
|
-
if (entry.kind === "var") {
|
|
1004
|
-
assertBound(where, bound, entry.over)
|
|
1005
|
-
return
|
|
1006
|
-
}
|
|
1007
|
-
if (entry.kind === "compute") {
|
|
1008
|
-
// Kind agreement, closed-reference and depth walls hold at
|
|
1009
|
-
// construction (#query/compute.ts); boundness is a RULE property,
|
|
1010
|
-
// judged here like every other term position.
|
|
1011
|
-
for (const ref of computeVarsOf(entry.expr)) {
|
|
1012
|
-
assertBound(where, bound, ref)
|
|
1013
|
-
}
|
|
950
|
+
/** An equality between two variables joins them, so their slots must join under the class law. */
|
|
951
|
+
function validateCond(context: ChainContext, cond: CondData): void {
|
|
952
|
+
if (cond.kind !== "cmp") {
|
|
953
|
+
for (const child of cond.children) validateCond(context, child)
|
|
1014
954
|
return
|
|
1015
955
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
assertNumeric(where, `the ${agg.fold} input`, agg.over)
|
|
1024
|
-
if (agg.fold === "mean" && agg.over.field.kind !== "f64") {
|
|
1025
|
-
throw new AuthoringError({ message: `${where}: mean requires an f64 input; cast integers explicitly` })
|
|
1026
|
-
}
|
|
1027
|
-
return
|
|
1028
|
-
}
|
|
1029
|
-
case "pack":
|
|
1030
|
-
assertBound(where, bound, agg.over)
|
|
1031
|
-
assertInterval(where, agg.over)
|
|
1032
|
-
return
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
function validateCond(context: ChainContext, bound: ReadonlySet<AnyVar>, cond: CondData): void {
|
|
1037
|
-
const label = contextLabel(context)
|
|
1038
|
-
if (cond.kind === "cmp") {
|
|
1039
|
-
for (const side of [cond.lhs, cond.rhs]) {
|
|
1040
|
-
if (side.kind === "var") {
|
|
1041
|
-
assertBound(label, bound, side.ref)
|
|
1042
|
-
const roster = rosterOf(side.ref.field)
|
|
1043
|
-
if (isOrderOp(cond.op.kind) && roster !== undefined) {
|
|
1044
|
-
throw closedOrderError(label, `the ${cond.op.kind} side ${side.ref.label}`, roster.name)
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
if ((cond.op.kind === "eq" || cond.op.kind === "ne") && cond.lhs.kind === "var" && cond.rhs.kind === "var") {
|
|
1049
|
-
assertBound(label, bound, cond.lhs.ref)
|
|
1050
|
-
assertBound(label, bound, cond.rhs.ref)
|
|
1051
|
-
const lhs = mintSlotOf(context, cond.lhs.ref)
|
|
1052
|
-
const rhs = mintSlotOf(context, cond.rhs.ref)
|
|
1053
|
-
if (!fieldJoins(lhs, rhs)) {
|
|
1054
|
-
throw new AuthoringError({
|
|
1055
|
-
message: `${label}: ${cond.op.kind}(${cond.lhs.ref.label}, ${cond.rhs.ref.label}) unifies domain-unequal fields — ${cond.lhs.ref.label} bound at ${renderFieldKind(lhs)}, ${cond.rhs.ref.label} at ${renderFieldKind(rhs)} (a var joins only class-equal slots; bare pairs only with bare)`
|
|
1056
|
-
})
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
return
|
|
1060
|
-
}
|
|
1061
|
-
for (const child of cond.children) {
|
|
1062
|
-
validateCond(context, bound, child)
|
|
956
|
+
if ((cond.op.kind !== "eq" && cond.op.kind !== "ne") || cond.lhs.kind !== "var" || cond.rhs.kind !== "var") return
|
|
957
|
+
const lhs = mintSlotOf(context, cond.lhs.ref)
|
|
958
|
+
const rhs = mintSlotOf(context, cond.rhs.ref)
|
|
959
|
+
if (!fieldJoins(lhs, rhs)) {
|
|
960
|
+
throw new AuthoringError({
|
|
961
|
+
message: `${contextLabel(context)}: ${cond.op.kind}(${cond.lhs.ref.label}, ${cond.rhs.ref.label}) unifies domain-unequal fields — ${cond.lhs.ref.label} bound at ${renderFieldKind(lhs)}, ${cond.rhs.ref.label} at ${renderFieldKind(rhs)} (a var joins only class-equal slots; bare pairs only with bare)`
|
|
962
|
+
})
|
|
1063
963
|
}
|
|
1064
964
|
}
|
|
1065
965
|
|
|
1066
966
|
function validateInterior(
|
|
1067
967
|
context: ChainContext,
|
|
1068
|
-
bound: ReadonlySet<AnyVar>,
|
|
1069
968
|
item: {
|
|
1070
969
|
readonly kind: "interior" | "negatedInterior"
|
|
1071
970
|
readonly target: DerivedTable
|
|
@@ -1096,11 +995,6 @@ function validateInterior(
|
|
|
1096
995
|
}
|
|
1097
996
|
}
|
|
1098
997
|
for (const binding of item.bindings) {
|
|
1099
|
-
if (item.kind === "negatedInterior" && !bound.has(binding.ref)) {
|
|
1100
|
-
throw new AuthoringError({
|
|
1101
|
-
message: `${label}: negated interior ${item.target.name} names the variable ${binding.ref.label}, but no positive atom of the rule binds it — a negated atom binds nothing, only rejects (the safety rule)`
|
|
1102
|
-
})
|
|
1103
|
-
}
|
|
1104
998
|
const headColumn = headColumns.find(function byName(column) {
|
|
1105
999
|
return column.name === binding.key
|
|
1106
1000
|
})
|
|
@@ -1118,38 +1012,14 @@ function validateInterior(
|
|
|
1118
1012
|
|
|
1119
1013
|
function completeRule(context: ChainContext, state: RuleBuildState, rawColumns: readonly FindColumn[]): RuleData {
|
|
1120
1014
|
const label = contextLabel(context)
|
|
1121
|
-
if (rawColumns.length === 0) {
|
|
1122
|
-
throw new AuthoringError({ message: `${label}: a find needs at least one entry` })
|
|
1123
|
-
}
|
|
1124
|
-
const aggregates = rawColumns.flatMap((column) => (column.entry.kind === "aggregate" ? [column.entry.agg] : []))
|
|
1125
|
-
const packs = aggregates.filter((agg) => agg.op === "pack").length
|
|
1126
|
-
if (packs > 1) throw new AuthoringError({ message: "a query stage can pack one interval column" })
|
|
1127
|
-
if (packs !== 0 && aggregates.length !== packs)
|
|
1128
|
-
throw new AuthoringError({ message: "pack intervals and compute numeric aggregates in separate query stages" })
|
|
1129
|
-
if (rawColumns.some((c) => c.entry.kind === "segments") && aggregates.length !== 0)
|
|
1130
|
-
throw new AuthoringError({ message: "aggregate generated segments in a following query stage" })
|
|
1131
1015
|
const columns = rawColumns.map(function enrichColumn(column): FindColumn {
|
|
1132
1016
|
assertDeclarationOrderKey(`${label} find column`, column.name)
|
|
1133
|
-
validateColumn(context, state.bound, column)
|
|
1134
1017
|
const slot = findColumnSlotOf(context, column)
|
|
1135
1018
|
return Object.freeze({ name: column.name, entry: column.entry, slot, closed: rosterOf(slot?.field) })
|
|
1136
1019
|
})
|
|
1137
1020
|
for (const item of state.items) {
|
|
1138
|
-
if (item.kind === "
|
|
1139
|
-
|
|
1140
|
-
if (binding.term.kind === "var" && !state.bound.has(binding.term.ref)) {
|
|
1141
|
-
throw new AuthoringError({
|
|
1142
|
-
message: `${label}: negated ${item.atom.relation.name} atom binds the variable ${binding.term.ref.label} at position ${binding.field}, but no positive atom of the rule binds it — a negated atom binds nothing, only rejects (the safety rule)`
|
|
1143
|
-
})
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
if (item.kind === "interior" || item.kind === "negatedInterior") {
|
|
1148
|
-
validateInterior(context, state.bound, item, columns)
|
|
1149
|
-
}
|
|
1150
|
-
if (item.kind === "cond") {
|
|
1151
|
-
validateCond(context, state.bound, item.cond)
|
|
1152
|
-
}
|
|
1021
|
+
if (item.kind === "interior" || item.kind === "negatedInterior") validateInterior(context, item, columns)
|
|
1022
|
+
if (item.kind === "cond") validateCond(context, item.cond)
|
|
1153
1023
|
}
|
|
1154
1024
|
return Object.freeze({ items: state.items, finds: Object.freeze(columns), paramUses: state.paramUses })
|
|
1155
1025
|
}
|
|
@@ -1241,8 +1111,8 @@ function interiorAdvance(
|
|
|
1241
1111
|
}
|
|
1242
1112
|
|
|
1243
1113
|
/**
|
|
1244
|
-
* Imported query templates as relation-expression sources (
|
|
1245
|
-
*
|
|
1114
|
+
* Imported query templates as relation-expression sources (nonrecursive
|
|
1115
|
+
* composition). The imported CQ's whole body — its inner
|
|
1246
1116
|
* interiors plus its main rules as one derived table — is SPLICED into the
|
|
1247
1117
|
* consuming query's interior roster at assembly ({@link makeRawQuery});
|
|
1248
1118
|
* naming does not materialize anything, and the imported template keeps its
|
|
@@ -1289,7 +1159,7 @@ function importEntryOf(context: ChainContext, imported: AnyQuery): ImportEntry {
|
|
|
1289
1159
|
const data = imported.data
|
|
1290
1160
|
if (data.kind === "reach") {
|
|
1291
1161
|
throw new AuthoringError({
|
|
1292
|
-
message: `${contextLabel(context)}: a recursive query is not importable as a relation expression
|
|
1162
|
+
message: `${contextLabel(context)}: a recursive query is not importable as a relation expression — declare the recursion on the consuming query`
|
|
1293
1163
|
})
|
|
1294
1164
|
}
|
|
1295
1165
|
const userParams = data.params.filter(function userSupplied(entry) {
|
|
@@ -1428,8 +1298,7 @@ function findColumns(context: ChainContext, entries: Readonly<Record<string, unk
|
|
|
1428
1298
|
const columns: FindColumn[] = []
|
|
1429
1299
|
queryRecord("query find", entries)
|
|
1430
1300
|
// Nonrecursive derived stages (interiors) may emit aggregate/computed
|
|
1431
|
-
// outputs
|
|
1432
|
-
// recursive feedback cycle stays projection-only — no aggregate,
|
|
1301
|
+
// outputs; ONLY the recursive feedback cycle stays projection-only — no aggregate,
|
|
1433
1302
|
// arithmetic-created value or negation flows through it.
|
|
1434
1303
|
const recName = context.kind === "rec-base" || context.kind === "rec-arm" ? context.self.name : undefined
|
|
1435
1304
|
for (const [name, entry] of Object.entries(entries)) {
|
|
@@ -1501,7 +1370,7 @@ function makeQueryRuleScope<Rels extends SchemaRelations, Classes extends Schema
|
|
|
1501
1370
|
): QueryRuleScope<Rels, Classes> {
|
|
1502
1371
|
const raw = makeRawScope({ kind: "query", classes: theory.classes, theory, ...env })
|
|
1503
1372
|
if (!isTypedScope<QueryRuleScope<Rels, Classes>>(raw)) {
|
|
1504
|
-
throw
|
|
1373
|
+
throw internalError("query rule builder construction incomplete")
|
|
1505
1374
|
}
|
|
1506
1375
|
return raw
|
|
1507
1376
|
}
|
|
@@ -1513,7 +1382,7 @@ function makeInteriorRuleScope<Rels extends SchemaRelations, Classes extends Sch
|
|
|
1513
1382
|
): InteriorRuleScope<Rels, Classes> {
|
|
1514
1383
|
const raw = makeRawScope({ kind: "interior", self, classes: theory.classes, theory, ...env })
|
|
1515
1384
|
if (!isTypedScope<InteriorRuleScope<Rels, Classes>>(raw)) {
|
|
1516
|
-
throw
|
|
1385
|
+
throw internalError("interior rule builder construction incomplete")
|
|
1517
1386
|
}
|
|
1518
1387
|
return raw
|
|
1519
1388
|
}
|
|
@@ -1553,7 +1422,7 @@ function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaCl
|
|
|
1553
1422
|
...env
|
|
1554
1423
|
})
|
|
1555
1424
|
if (!isTypedScope<RecRuleScope<Rels, Classes>>(raw)) {
|
|
1556
|
-
throw
|
|
1425
|
+
throw internalError("rec rule builder construction incomplete")
|
|
1557
1426
|
}
|
|
1558
1427
|
return raw
|
|
1559
1428
|
}
|
|
@@ -1562,8 +1431,10 @@ function renderClosedSlice(closed: AnyClosedRoster | undefined): string {
|
|
|
1562
1431
|
return closed === undefined ? "a bare value" : `a ${closed.name} reference`
|
|
1563
1432
|
}
|
|
1564
1433
|
|
|
1565
|
-
function headOperation(column: FindColumn):
|
|
1566
|
-
|
|
1434
|
+
function headOperation(column: FindColumn): string | undefined {
|
|
1435
|
+
if (column.entry.kind !== "aggregate") return undefined
|
|
1436
|
+
const agg = column.entry.agg
|
|
1437
|
+
return agg.op === "fold" ? agg.fold : agg.op
|
|
1567
1438
|
}
|
|
1568
1439
|
|
|
1569
1440
|
function renderParamAnchor(roster: AnyClosedRoster | undefined): string {
|
|
@@ -1571,16 +1442,10 @@ function renderParamAnchor(roster: AnyClosedRoster | undefined): string {
|
|
|
1571
1442
|
}
|
|
1572
1443
|
|
|
1573
1444
|
/**
|
|
1574
|
-
* Folds every rule's param uses
|
|
1575
|
-
*
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1578
|
-
* closedness. The orderable ban needs no registry arm: an order use always
|
|
1579
|
-
* anchors its SIBLING's domain (the no-variable-side spelling is refused
|
|
1580
|
-
* at the comparison constructor), so a closed-anchored param under an
|
|
1581
|
-
* order op dies at the one-domain wall here — and an order use whose
|
|
1582
|
-
* sibling is itself closed-bound dies at the comparison's own var-side
|
|
1583
|
-
* wall first.
|
|
1445
|
+
* Folds every rule's param uses, in lowering order, into the query's registry: the first use mints
|
|
1446
|
+
* the dense `ParamId`, the first field-anchored use types it, and one name keeps one shape and one
|
|
1447
|
+
* closedness. An order use always anchors its sibling's domain, so a closed-anchored param under an
|
|
1448
|
+
* order op fails the one-domain check here.
|
|
1584
1449
|
*/
|
|
1585
1450
|
function paramRegistryOf(
|
|
1586
1451
|
interiors: readonly InteriorData[],
|
|
@@ -1777,6 +1642,53 @@ function afterMainError(what: string): Error {
|
|
|
1777
1642
|
})
|
|
1778
1643
|
}
|
|
1779
1644
|
|
|
1645
|
+
/** Names the parts of `data` an engine query diagnostic cites. */
|
|
1646
|
+
function explainQuery(data: QueryData, labels: RuleLabels, diagnostic: QueryDiagnostic): string {
|
|
1647
|
+
const tables = [...data.interiors, ...(data.kind === "reach" ? [data.rec] : [])]
|
|
1648
|
+
const table = diagnostic.interior === undefined ? undefined : tables[diagnostic.interior]
|
|
1649
|
+
const ruleLabels =
|
|
1650
|
+
diagnostic.rule === undefined
|
|
1651
|
+
? undefined
|
|
1652
|
+
: (diagnostic.interior === undefined ? labels.main : labels.interiors[diagnostic.interior])?.[diagnostic.rule]
|
|
1653
|
+
const finds = table?.finds ?? data.finds
|
|
1654
|
+
const cited = [
|
|
1655
|
+
table === undefined ? undefined : `in ${table.name}`,
|
|
1656
|
+
diagnostic.rule === undefined ? undefined : `rule ${diagnostic.rule}`,
|
|
1657
|
+
diagnostic.find === undefined ? undefined : `find ${finds[diagnostic.find]?.name ?? diagnostic.find}`,
|
|
1658
|
+
diagnostic.atom === undefined ? undefined : `atom ${diagnostic.atom}`,
|
|
1659
|
+
diagnostic.var === undefined ? undefined : `variable ${ruleLabels?.[diagnostic.var] ?? diagnostic.var}`,
|
|
1660
|
+
diagnostic.param === undefined ? undefined : `param ${data.params[diagnostic.param]?.name ?? diagnostic.param}`,
|
|
1661
|
+
diagnostic.comparison === undefined ? undefined : `comparison ${diagnostic.comparison}`
|
|
1662
|
+
].filter((part) => part !== undefined)
|
|
1663
|
+
return cited.length === 0 ? diagnostic.message : `${diagnostic.message} (${cited.join(", ")})`
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
/** Validates a complete query with the engine and returns its compiled handle. */
|
|
1667
|
+
function validateQueryData(theory: AnySchema, data: QueryData): QueryRef {
|
|
1668
|
+
const lowered = lowerQueryLabelled({ schema: theory, data })
|
|
1669
|
+
const validated = addon.validateQuery(compiledOf(schemaDescriptor(theory)).handle, JSON.stringify(lowered.query))
|
|
1670
|
+
switch (validated._tag) {
|
|
1671
|
+
case "Valid":
|
|
1672
|
+
return validated.query
|
|
1673
|
+
case "Invalid":
|
|
1674
|
+
throw new AuthoringError({
|
|
1675
|
+
message: `query: ${explainQuery(data, lowered.labels, validated.diagnostic)}`,
|
|
1676
|
+
diagnostic: { code: "InvalidQuery", context: validated.diagnostic.code, expected: validated.diagnostic.message }
|
|
1677
|
+
})
|
|
1678
|
+
case "Malformed":
|
|
1679
|
+
throw internalError(`validateQuery: ${validated.path}: ${validated.message}`)
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
const validatedQueries = new WeakMap<object, QueryRef>()
|
|
1684
|
+
|
|
1685
|
+
/** The engine's validated form of a query value `query()` built. */
|
|
1686
|
+
function queryHandleOf(query: AnyQuery): QueryRef {
|
|
1687
|
+
const handle = validatedQueries.get(query)
|
|
1688
|
+
if (handle === undefined) throw internalError("queryHandleOf: the query was not built by query()")
|
|
1689
|
+
return handle
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1780
1692
|
function makeRawQuery(
|
|
1781
1693
|
inputTheory: AnySchema,
|
|
1782
1694
|
inputInteriors: readonly InteriorData[],
|
|
@@ -1816,6 +1728,7 @@ function makeRawQuery(
|
|
|
1816
1728
|
finds: mergedFinds,
|
|
1817
1729
|
params
|
|
1818
1730
|
})
|
|
1731
|
+
const handle = validateQueryData(theory, data)
|
|
1819
1732
|
const value: RawQuery = {
|
|
1820
1733
|
get schema() {
|
|
1821
1734
|
return snapshotData(theory)
|
|
@@ -1841,6 +1754,7 @@ function makeRawQuery(
|
|
|
1841
1754
|
}
|
|
1842
1755
|
}
|
|
1843
1756
|
Object.freeze(value)
|
|
1757
|
+
validatedQueries.set(value, handle)
|
|
1844
1758
|
return value
|
|
1845
1759
|
}
|
|
1846
1760
|
|
|
@@ -2071,6 +1985,8 @@ interface LowerContext {
|
|
|
2071
1985
|
|
|
2072
1986
|
interface VarIds {
|
|
2073
1987
|
of(ref: AnyVar): number
|
|
1988
|
+
/** Each variable's label, by variable id. */
|
|
1989
|
+
labels(): readonly string[]
|
|
2074
1990
|
}
|
|
2075
1991
|
|
|
2076
1992
|
function freshVarIds(): VarIds {
|
|
@@ -2084,6 +2000,9 @@ function freshVarIds(): VarIds {
|
|
|
2084
2000
|
const id = assigned.size
|
|
2085
2001
|
assigned.set(ref, id)
|
|
2086
2002
|
return id
|
|
2003
|
+
},
|
|
2004
|
+
labels() {
|
|
2005
|
+
return [...assigned.keys()].map((ref) => ref.label)
|
|
2087
2006
|
}
|
|
2088
2007
|
}
|
|
2089
2008
|
}
|
|
@@ -2096,256 +2015,221 @@ function paramIdOf(ctx: LowerContext, name: string): number {
|
|
|
2096
2015
|
return id
|
|
2097
2016
|
}
|
|
2098
2017
|
|
|
2099
|
-
function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds):
|
|
2100
|
-
const
|
|
2101
|
-
if (!membersAgree(
|
|
2018
|
+
function lowerAtom(ctx: LowerContext, atom: AtomData, ids: VarIds): AtomIn {
|
|
2019
|
+
const relationId = ctx.relationIds.get(atom.relation.name)
|
|
2020
|
+
if (!membersAgree(ctx.theory.relations[atom.relation.name], atom.relation) || relationId === undefined) {
|
|
2102
2021
|
throw new AuthoringError({
|
|
2103
|
-
message: `query
|
|
2022
|
+
message: `query: relation ${atom.relation.name} is not the relation value schema ${ctx.theory.name} declares`
|
|
2104
2023
|
})
|
|
2105
2024
|
}
|
|
2106
|
-
const relationId = ctx.relationIds.get(atom.relation.name)
|
|
2107
|
-
if (relationId === undefined) {
|
|
2108
|
-
throw new AuthoringError({ message: `query lowering: relation ${atom.relation.name} has no ordinal` })
|
|
2109
|
-
}
|
|
2110
2025
|
const ordered = sealedFieldsOf(atom.relation)
|
|
2111
|
-
const bindings
|
|
2112
|
-
const
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
throw new AuthoringError({
|
|
2117
|
-
message: `query lowering: relation ${atom.relation.name} has no field ${binding.field}`
|
|
2118
|
-
})
|
|
2119
|
-
}
|
|
2120
|
-
return [ordinal, lowerBindingTerm(ctx, `${atom.relation.name}.${binding.field}`, binding, ids)] as const
|
|
2026
|
+
const bindings = atom.bindings.map((binding): BindingIn => {
|
|
2027
|
+
const field = ordered.findIndex((candidate) => candidate.name === binding.field)
|
|
2028
|
+
if (field < 0)
|
|
2029
|
+
throw new AuthoringError({ message: `query: relation ${atom.relation.name} has no field ${binding.field}` })
|
|
2030
|
+
return { field, term: lowerBindingTerm(ctx, `${atom.relation.name}.${binding.field}`, binding, ids) }
|
|
2121
2031
|
})
|
|
2122
|
-
return { source: { kind: "
|
|
2032
|
+
return { source: { kind: "Edb", relation: relationId }, bindings }
|
|
2123
2033
|
}
|
|
2124
2034
|
|
|
2125
|
-
function lowerBindingTerm(ctx: LowerContext, context: string, binding: BindingEntry, ids: VarIds):
|
|
2035
|
+
function lowerBindingTerm(ctx: LowerContext, context: string, binding: BindingEntry, ids: VarIds): TermIn {
|
|
2126
2036
|
const bound = binding.term
|
|
2127
2037
|
switch (bound.kind) {
|
|
2128
2038
|
case "var":
|
|
2129
|
-
return { kind: "
|
|
2039
|
+
return { kind: "Var", var: ids.of(bound.ref) }
|
|
2130
2040
|
case "param":
|
|
2131
|
-
return { kind: "
|
|
2041
|
+
return { kind: "Param", param: paramIdOf(ctx, bound.name) }
|
|
2132
2042
|
case "setParam":
|
|
2133
|
-
return { kind: "paramSet", param: paramIdOf(ctx, bound.name) }
|
|
2134
2043
|
case "literalSet":
|
|
2135
|
-
return { kind: "
|
|
2044
|
+
return { kind: "ParamSet", param: paramIdOf(ctx, bound.name) }
|
|
2136
2045
|
case "literal":
|
|
2137
|
-
return { kind: "
|
|
2046
|
+
return { kind: "Literal", value: valueIn(taggedLiteral(context, binding.data, bound.value)) }
|
|
2138
2047
|
}
|
|
2139
2048
|
}
|
|
2140
2049
|
|
|
2141
|
-
/**
|
|
2142
|
-
* Lowers one interior atom: named bindings placed by HEAD order, `FieldId(i)` =
|
|
2143
|
-
* head position i. Every head column of the table must be bound (a missing key
|
|
2144
|
-
* is refused pointed); the var-id assignment order is head order, so the
|
|
2145
|
-
* first-use numbering matches the name-keyed edition exactly.
|
|
2146
|
-
*/
|
|
2050
|
+
/** An interior atom binds every head column of the table, by head position. */
|
|
2147
2051
|
function lowerInteriorAtom(
|
|
2148
2052
|
ctx: LowerContext,
|
|
2149
2053
|
target: DerivedTable,
|
|
2150
2054
|
bindings: ReadonlyArray<{ readonly key: string; readonly ref: AnyVar }>,
|
|
2151
2055
|
ids: VarIds
|
|
2152
|
-
):
|
|
2056
|
+
): AtomIn {
|
|
2153
2057
|
const interior = ctx.interiorIds.get(target.name)
|
|
2154
2058
|
if (interior === undefined) {
|
|
2155
|
-
throw new AuthoringError({ message: `query
|
|
2156
|
-
}
|
|
2157
|
-
if (target.finds.length === 0) {
|
|
2158
|
-
throw new AuthoringError({ message: `query lowering: derived table ${target.name} has no head` })
|
|
2059
|
+
throw new AuthoringError({ message: `query: derived table ${target.name} was not declared on this query` })
|
|
2159
2060
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2061
|
+
return {
|
|
2062
|
+
source: { kind: "Interior", interior },
|
|
2063
|
+
bindings: target.finds.map((column, field): BindingIn => {
|
|
2064
|
+
const binding = bindings.find((candidate) => candidate.key === column.name)
|
|
2065
|
+
if (binding === undefined) {
|
|
2066
|
+
throw new AuthoringError({ message: `query: interior ${target.name} omits head column ${column.name}` })
|
|
2067
|
+
}
|
|
2068
|
+
return { field, term: { kind: "Var", var: ids.of(binding.ref) } }
|
|
2163
2069
|
})
|
|
2164
|
-
|
|
2165
|
-
throw new AuthoringError({ message: `query lowering: interior ${target.name} omits head column ${column.name}` })
|
|
2166
|
-
}
|
|
2167
|
-
return [position, { kind: "var", var: ids.of(binding.ref) } as const] as const
|
|
2168
|
-
})
|
|
2169
|
-
return { source: { kind: "interior", interior }, bindings: irBindings }
|
|
2070
|
+
}
|
|
2170
2071
|
}
|
|
2171
2072
|
|
|
2172
|
-
function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData, ids: VarIds, op: CmpKind):
|
|
2073
|
+
function lowerCmpTerm(ctx: LowerContext, side: CmpTermData, sibling: CmpTermData, ids: VarIds, op: CmpKind): TermIn {
|
|
2173
2074
|
switch (side.kind) {
|
|
2174
2075
|
case "var":
|
|
2175
|
-
return { kind: "
|
|
2076
|
+
return { kind: "Var", var: ids.of(side.ref) }
|
|
2176
2077
|
case "param":
|
|
2177
|
-
return { kind: "
|
|
2078
|
+
return { kind: "Param", param: paramIdOf(ctx, side.name) }
|
|
2178
2079
|
case "setParam":
|
|
2179
|
-
return { kind: "
|
|
2080
|
+
return { kind: "ParamSet", param: paramIdOf(ctx, side.name) }
|
|
2180
2081
|
case "literal": {
|
|
2181
2082
|
const anchor = cmpAnchorOf(ctx, sibling)
|
|
2182
2083
|
if (anchor === undefined) {
|
|
2183
2084
|
throw new AuthoringError({
|
|
2184
|
-
message: "query
|
|
2085
|
+
message: "query: a comparison literal needs a bound-variable or anchored-param sibling to type it"
|
|
2185
2086
|
})
|
|
2186
2087
|
}
|
|
2187
|
-
return { kind: "
|
|
2088
|
+
return { kind: "Literal", value: valueIn(taggedCmpLiteral("comparison literal", anchor, side.value, op)) }
|
|
2188
2089
|
}
|
|
2189
2090
|
}
|
|
2190
2091
|
}
|
|
2191
2092
|
|
|
2192
2093
|
function cmpAnchorOf(ctx: LowerContext, sibling: CmpTermData): AnyField | undefined {
|
|
2193
|
-
if (sibling.kind === "var")
|
|
2194
|
-
|
|
2195
|
-
}
|
|
2196
|
-
if (sibling.kind === "param" || sibling.kind === "setParam") {
|
|
2197
|
-
return ctx.params.get(sibling.name)?.anchor
|
|
2198
|
-
}
|
|
2094
|
+
if (sibling.kind === "var") return sibling.ref.field
|
|
2095
|
+
if (sibling.kind === "param" || sibling.kind === "setParam") return ctx.params.get(sibling.name)?.anchor
|
|
2199
2096
|
return undefined
|
|
2200
2097
|
}
|
|
2201
2098
|
|
|
2202
|
-
|
|
2203
|
-
|
|
2099
|
+
const CMP = { eq: "Eq", ne: "Ne", lt: "Lt", le: "Le", gt: "Gt", ge: "Ge", pointIn: "PointIn" } as const
|
|
2100
|
+
|
|
2101
|
+
function lowerCondition(ctx: LowerContext, cond: CondData, ids: VarIds): ConditionIn {
|
|
2102
|
+
if (cond.kind !== "cmp") {
|
|
2204
2103
|
return {
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
rhs: lowerCmpTerm(ctx, cmp.rhs, cmp.lhs, ids, "allen")
|
|
2104
|
+
kind: cond.op === "and" ? "And" : "Or",
|
|
2105
|
+
children: cond.children.map((child) => lowerCondition(ctx, child, ids))
|
|
2208
2106
|
}
|
|
2209
2107
|
}
|
|
2108
|
+
const kind = cond.op.kind
|
|
2210
2109
|
return {
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
function lowerCondition(ctx: LowerContext, cond: CondData, ids: VarIds): ConditionTreeIr {
|
|
2218
|
-
if (cond.kind === "cmp") {
|
|
2219
|
-
return { kind: "leaf", cmp: lowerComparison(ctx, cond, ids) }
|
|
2220
|
-
}
|
|
2221
|
-
return {
|
|
2222
|
-
kind: cond.op,
|
|
2223
|
-
children: cond.children.map(function lowerChild(child) {
|
|
2224
|
-
return lowerCondition(ctx, child, ids)
|
|
2225
|
-
})
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
/**
|
|
2230
|
-
* Lowers one computed scalar node to the C05 wire (`#native.ts`'s
|
|
2231
|
-
* `ScalarExprIr`): the shared grammar arms pass through 1:1. Cached
|
|
2232
|
-
* construction depth is the bound; this walk emits wire only and does
|
|
2233
|
-
* not re-judge kinds (L14/native compile owns binding).
|
|
2234
|
-
*/
|
|
2235
|
-
function lowerComputeNode(node: QueryNode, ids: VarIds): ScalarExprIr {
|
|
2236
|
-
if (node.depth > MAX_COMPUTE_DEPTH) {
|
|
2237
|
-
throw new AuthoringError({
|
|
2238
|
-
message: `query lowering: a compute expression is deeper than ${MAX_COMPUTE_DEPTH} nodes (the engine's scalar depth bound)`
|
|
2239
|
-
})
|
|
2110
|
+
kind: "Leaf",
|
|
2111
|
+
op: cond.op.kind === "allen" ? { kind: "Allen", mask: cond.op.mask } : { kind: CMP[cond.op.kind] },
|
|
2112
|
+
lhs: lowerCmpTerm(ctx, cond.lhs, cond.rhs, ids, kind),
|
|
2113
|
+
rhs: lowerCmpTerm(ctx, cond.rhs, cond.lhs, ids, kind)
|
|
2240
2114
|
}
|
|
2241
|
-
return lowerComputeGrammar(node, ids)
|
|
2242
2115
|
}
|
|
2243
2116
|
|
|
2244
|
-
|
|
2245
|
-
return scalarWire(node, (leaf) => {
|
|
2246
|
-
return ids.of(leaf.ref as AnyVar)
|
|
2247
|
-
})
|
|
2248
|
-
}
|
|
2117
|
+
const FOLD = { sum: "Sum", mean: "Mean", min: "Min", max: "Max" } as const
|
|
2249
2118
|
|
|
2250
|
-
function lowerFind(entry: FindEntryData, ids: VarIds):
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2119
|
+
function lowerFind(entry: FindEntryData, ids: VarIds): FindTermIn {
|
|
2120
|
+
switch (entry.kind) {
|
|
2121
|
+
case "segments":
|
|
2122
|
+
return {
|
|
2123
|
+
kind: "Segments",
|
|
2124
|
+
op: entry.op === "intersection" ? "Intersection" : "Difference",
|
|
2125
|
+
left: ids.of(entry.left),
|
|
2126
|
+
right: ids.of(entry.right)
|
|
2127
|
+
}
|
|
2128
|
+
case "var":
|
|
2129
|
+
return { kind: "Var", var: ids.of(entry.over) }
|
|
2130
|
+
case "compute":
|
|
2131
|
+
if (entry.expr.depth > MAX_COMPUTE_DEPTH) {
|
|
2132
|
+
throw new AuthoringError({
|
|
2133
|
+
message: `query: a compute expression is deeper than ${MAX_COMPUTE_DEPTH} nodes`
|
|
2134
|
+
})
|
|
2135
|
+
}
|
|
2136
|
+
return { kind: "Compute", expr: scalarWire(entry.expr, (leaf) => ids.of(leaf.ref as AnyVar)) }
|
|
2258
2137
|
}
|
|
2259
2138
|
const agg = entry.agg
|
|
2260
2139
|
switch (agg.op) {
|
|
2261
2140
|
case "count":
|
|
2262
|
-
return { kind: "
|
|
2263
|
-
case "fold":
|
|
2264
|
-
return { kind: "aggregate", op: { kind: agg.fold }, over: ids.of(agg.over) }
|
|
2265
|
-
case "pack":
|
|
2266
|
-
return { kind: "pack", over: ids.of(agg.over) }
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2269
|
-
|
|
2270
|
-
function headOpOf(agg: AggData): HeadOpIr {
|
|
2271
|
-
switch (agg.op) {
|
|
2272
|
-
case "count":
|
|
2273
|
-
return "count"
|
|
2141
|
+
return { kind: "Count" }
|
|
2274
2142
|
case "fold":
|
|
2275
|
-
return agg.fold
|
|
2143
|
+
return { kind: "Aggregate", op: FOLD[agg.fold], over: ids.of(agg.over) }
|
|
2276
2144
|
case "pack":
|
|
2277
|
-
return "
|
|
2145
|
+
return { kind: "Pack", over: ids.of(agg.over) }
|
|
2278
2146
|
}
|
|
2279
2147
|
}
|
|
2280
2148
|
|
|
2281
|
-
function headTermOf(column: FindColumn):
|
|
2149
|
+
function headTermOf(column: FindColumn): HeadTermIn {
|
|
2282
2150
|
const entry = column.entry
|
|
2283
|
-
if (entry.kind === "segments") return { kind: "
|
|
2284
|
-
if (entry.kind === "var") {
|
|
2285
|
-
|
|
2286
|
-
}
|
|
2287
|
-
if (
|
|
2288
|
-
|
|
2289
|
-
}
|
|
2290
|
-
return { kind: "aggregate", op: headOpOf(entry.agg) }
|
|
2151
|
+
if (entry.kind === "segments" || entry.kind === "compute") return { kind: "Compute" }
|
|
2152
|
+
if (entry.kind === "var") return { kind: "Var" }
|
|
2153
|
+
const agg = entry.agg
|
|
2154
|
+
if (agg.op === "count") return { kind: "Aggregate", op: "Count" }
|
|
2155
|
+
if (agg.op === "pack") return { kind: "Aggregate", op: "Pack" }
|
|
2156
|
+
return { kind: "Aggregate", op: FOLD[agg.fold] }
|
|
2291
2157
|
}
|
|
2292
2158
|
|
|
2293
|
-
function lowerRule(ctx: LowerContext, rule: RuleData):
|
|
2159
|
+
function lowerRule(ctx: LowerContext, rule: RuleData, labels?: (readonly string[])[]): RuleIn {
|
|
2294
2160
|
const ids = freshVarIds()
|
|
2295
|
-
const atoms:
|
|
2296
|
-
const negated:
|
|
2297
|
-
const conditions:
|
|
2161
|
+
const atoms: AtomIn[] = []
|
|
2162
|
+
const negated: AtomIn[] = []
|
|
2163
|
+
const conditions: ConditionIn[] = []
|
|
2298
2164
|
for (const item of rule.items) {
|
|
2299
2165
|
switch (item.kind) {
|
|
2300
|
-
case "atom":
|
|
2166
|
+
case "atom":
|
|
2301
2167
|
atoms.push(lowerAtom(ctx, item.atom, ids))
|
|
2302
2168
|
break
|
|
2303
|
-
|
|
2304
|
-
case "negated": {
|
|
2169
|
+
case "negated":
|
|
2305
2170
|
negated.push(lowerAtom(ctx, item.atom, ids))
|
|
2306
2171
|
break
|
|
2307
|
-
|
|
2308
|
-
case "interior": {
|
|
2172
|
+
case "interior":
|
|
2309
2173
|
atoms.push(lowerInteriorAtom(ctx, item.target, item.bindings, ids))
|
|
2310
2174
|
break
|
|
2311
|
-
|
|
2312
|
-
case "negatedInterior": {
|
|
2175
|
+
case "negatedInterior":
|
|
2313
2176
|
negated.push(lowerInteriorAtom(ctx, item.target, item.bindings, ids))
|
|
2314
2177
|
break
|
|
2315
|
-
|
|
2316
|
-
case "cond": {
|
|
2178
|
+
case "cond":
|
|
2317
2179
|
conditions.push(lowerCondition(ctx, item.cond, ids))
|
|
2318
2180
|
break
|
|
2319
|
-
}
|
|
2320
2181
|
}
|
|
2321
2182
|
}
|
|
2183
|
+
const finds = rule.finds.map((column) => lowerFind(column.entry, ids))
|
|
2184
|
+
labels?.push(ids.labels())
|
|
2185
|
+
return { finds, atoms, negated, conditions }
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
/** A recursive rule projects variables only and negates nothing. */
|
|
2189
|
+
function recFinds(label: string, rule: RuleIn): number[] {
|
|
2190
|
+
if (rule.negated.length > 0) throw new AuthoringError({ message: `query ${label}: a recursive rule cannot negate` })
|
|
2191
|
+
return rule.finds.map((find) => {
|
|
2192
|
+
if (find.kind !== "Var")
|
|
2193
|
+
throw new AuthoringError({ message: `query ${label}: a recursive rule finds variables only` })
|
|
2194
|
+
return find.var
|
|
2195
|
+
})
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
/** A recursive step reads its own previous output through exactly one self-atom. */
|
|
2199
|
+
function recStep(label: string, rule: RuleIn, self: number): RecStepIn {
|
|
2200
|
+
const selfAtoms = rule.atoms.filter((atom) => atom.source.kind === "Interior" && atom.source.interior === self)
|
|
2201
|
+
const [selfAtom] = selfAtoms
|
|
2202
|
+
if (selfAtom === undefined || selfAtoms.length > 1) {
|
|
2203
|
+
throw new AuthoringError({ message: `query ${label}: a recursive step reads the recursion exactly once` })
|
|
2204
|
+
}
|
|
2322
2205
|
return {
|
|
2323
|
-
finds:
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
negated,
|
|
2328
|
-
conditions
|
|
2206
|
+
finds: recFinds(label, rule),
|
|
2207
|
+
selfBindings: selfAtom.bindings,
|
|
2208
|
+
atoms: rule.atoms.filter((atom) => atom !== selfAtom),
|
|
2209
|
+
conditions: rule.conditions
|
|
2329
2210
|
}
|
|
2330
2211
|
}
|
|
2331
2212
|
|
|
2332
|
-
|
|
2213
|
+
/** Variable labels by rule: main rules, then each interior's rules. */
|
|
2214
|
+
interface RuleLabels {
|
|
2215
|
+
readonly main: (readonly string[])[]
|
|
2216
|
+
readonly interiors: (readonly string[])[][]
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
/** Lowers a query to the addon's `QueryIn`. The engine validates it; lowering judges nothing. */
|
|
2220
|
+
function lowerQuery(q: AnyQuery): QueryIn {
|
|
2221
|
+
return lowerQueryLabelled(q).query
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
function lowerQueryLabelled(q: AnyQuery): { readonly query: QueryIn; readonly labels: RuleLabels } {
|
|
2333
2225
|
const theory = q.schema
|
|
2334
2226
|
const data = q.data
|
|
2335
|
-
const relationIds = new Map
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
})
|
|
2339
|
-
const interiorIds = new Map<string, number>()
|
|
2340
|
-
data.interiors.forEach(function assignInteriorId(interior, index) {
|
|
2341
|
-
interiorIds.set(interior.name, index)
|
|
2342
|
-
})
|
|
2343
|
-
if (data.kind === "reach") {
|
|
2344
|
-
interiorIds.set(data.rec.name, data.interiors.length)
|
|
2345
|
-
}
|
|
2227
|
+
const relationIds = new Map(Object.keys(theory.relations).map((name, index) => [name, index] as const))
|
|
2228
|
+
const interiorIds = new Map(data.interiors.map((interior, index) => [interior.name, index] as const))
|
|
2229
|
+
if (data.kind === "reach") interiorIds.set(data.rec.name, data.interiors.length)
|
|
2346
2230
|
const paramIds = new Map<string, number>()
|
|
2347
2231
|
const params = new Map<string, ParamEntry>()
|
|
2348
|
-
data.params.forEach(
|
|
2232
|
+
data.params.forEach((entry, index) => {
|
|
2349
2233
|
if (entry.anchor === undefined) {
|
|
2350
2234
|
throw new AuthoringError({
|
|
2351
2235
|
message: `query param ${entry.name} has no field-anchored use — bind it in an atom or compare it against a bound variable`
|
|
@@ -2355,36 +2239,30 @@ function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
|
2355
2239
|
params.set(entry.name, entry)
|
|
2356
2240
|
})
|
|
2357
2241
|
const ctx: LowerContext = { theory, relationIds, interiorIds, paramIds, params }
|
|
2358
|
-
const
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
rules: interior.rules.map(
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
|
-
const
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
return lowerRule(ctx, rule)
|
|
2380
|
-
}),
|
|
2381
|
-
rec: data.rec.rec.map(function lowerRecArm(rule) {
|
|
2382
|
-
return lowerRule(ctx, rule)
|
|
2383
|
-
})
|
|
2242
|
+
const labels: RuleLabels = { main: [], interiors: data.interiors.map(() => []) }
|
|
2243
|
+
const query: QueryIn = {
|
|
2244
|
+
interiors: data.interiors.map((interior, index) => ({
|
|
2245
|
+
rules: interior.rules.map((rule) => lowerRule(ctx, rule, labels.interiors[index]))
|
|
2246
|
+
})),
|
|
2247
|
+
head: data.finds.map(headTermOf),
|
|
2248
|
+
rules: data.rules.map((rule) => lowerRule(ctx, rule, labels.main))
|
|
2249
|
+
}
|
|
2250
|
+
if (data.kind === "cq") return { query, labels }
|
|
2251
|
+
const label = `recursion ${data.rec.name}`
|
|
2252
|
+
const self = data.interiors.length
|
|
2253
|
+
return {
|
|
2254
|
+
query: {
|
|
2255
|
+
...query,
|
|
2256
|
+
rec: {
|
|
2257
|
+
base: data.rec.base.map((rule) => {
|
|
2258
|
+
const lowered = lowerRule(ctx, rule)
|
|
2259
|
+
return { finds: recFinds(label, lowered), atoms: lowered.atoms, conditions: lowered.conditions }
|
|
2260
|
+
}),
|
|
2261
|
+
rec: data.rec.rec.map((rule) => recStep(label, lowerRule(ctx, rule), self))
|
|
2262
|
+
}
|
|
2384
2263
|
},
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
})
|
|
2264
|
+
labels
|
|
2265
|
+
}
|
|
2388
2266
|
}
|
|
2389
2267
|
|
|
2390
2268
|
export type {
|
|
@@ -2405,4 +2283,4 @@ export type {
|
|
|
2405
2283
|
RuleValue,
|
|
2406
2284
|
TermOps
|
|
2407
2285
|
}
|
|
2408
|
-
export { alignedHeadOf, EMPTY_RULE, lowerQuery, makeRawChain, makeRawQuery, query, taggedCmpLiteral }
|
|
2286
|
+
export { alignedHeadOf, EMPTY_RULE, lowerQuery, makeRawChain, makeRawQuery, query, queryHandleOf, taggedCmpLiteral }
|