Evaluate coherent local queries over explicit replica scopes
This commit is contained in:
@@ -312,14 +312,19 @@ export function compileExecutionPlan(input: WorkspaceRevision): ExecutionPlan {
|
||||
"Local and replica queries must use the same root contract",
|
||||
);
|
||||
const traversals = (linked: LinkedQuery) => {
|
||||
const paths = new Set<string>();
|
||||
const paths = new Map<string, boolean>();
|
||||
const visit = (selections: LinkedQuery["checked"]["selection"], parents: string[]) => {
|
||||
for (const selection of selections) {
|
||||
const member = selection.interfaceRevisionId
|
||||
? interfaces.get(selection.interfaceRevisionId)?.members.find((m) => m.id === selection.memberId)
|
||||
: undefined;
|
||||
const path = member?.kind === "relationship" ? [...parents, member.id] : parents;
|
||||
if (member?.kind === "relationship") paths.add(JSON.stringify(path));
|
||||
if (member?.kind === "relationship") {
|
||||
const many = member.cardinality === "many" || member.cardinality === "many-unique";
|
||||
const complete = !selection.predicate && (!many || !!selection.arguments.all);
|
||||
const key = JSON.stringify(path);
|
||||
paths.set(key, complete || paths.get(key) === true);
|
||||
}
|
||||
visit(selection.selection, path);
|
||||
}
|
||||
};
|
||||
@@ -327,13 +332,20 @@ export function compileExecutionPlan(input: WorkspaceRevision): ExecutionPlan {
|
||||
return paths;
|
||||
};
|
||||
const admittedTraversals = traversals(source.query);
|
||||
for (const traversal of traversals(query.query))
|
||||
for (const traversal of traversals(query.query).keys()) {
|
||||
if (!admittedTraversals.has(traversal))
|
||||
throw new ExecutionPlanError(
|
||||
"REPLICA_TRAVERSAL_MISSING",
|
||||
query.id,
|
||||
`Replica source does not admit relationship path ${traversal}`,
|
||||
);
|
||||
if (!admittedTraversals.get(traversal))
|
||||
throw new ExecutionPlanError(
|
||||
"REPLICA_RELATION_INCOMPLETE",
|
||||
query.id,
|
||||
`Local relationship ${traversal} needs an unfiltered bounded all selection in its replica source; a filtered/windowed source cannot supply unseen matches`,
|
||||
);
|
||||
}
|
||||
// The admission executor follows these canonical dependencies on the admitted roots.
|
||||
// They do not change source candidate membership or make local take/filters authoritative.
|
||||
source.admittedOperations.push(...query.operations);
|
||||
|
||||
Reference in New Issue
Block a user