import type { CheckedQuery, QuerySelection } from "../query/types.js"; import type { QueryPredicate, QueryPathStep } from "../query/relational.js"; /** Supported by the fresh coordinator/replica interpreters. The language parser * can describe more operators; that does not make them executable in this world. */ export function unsupportedQueryOperator(query: CheckedQuery): string | undefined { const path = (steps: QueryPathStep[]) => steps.some((step) => step.kind !== "relation") ? "expanded source/target paths" : undefined; const predicate = (value: QueryPredicate): string | undefined => { if (value.kind === "reduce") return "aggregate predicates"; if (value.kind === "and" || value.kind === "or") return value.children.map(predicate).find(Boolean); if (value.kind === "not") return predicate(value.child); if (value.kind === "compare") return ( path(value.expression.path) ?? (value.expression.leaf.kind === "mapKey" ? "keyed relationship operands" : undefined) ); if (value.kind === "relation") return path(value.path) ?? (value.predicate && predicate(value.predicate)); return undefined; }; const conditional = (selections: readonly QuerySelection[]): boolean => selections.some((selection) => selection.conditions.length > 0 || conditional(selection.selection)); const walk = (selections: readonly QuerySelection[]): string | undefined => { for (const selection of selections) { const after = selection.arguments.after; if (after && !(after.kind === "literal" && after.value === null)) return "cursor continuation"; if (selection.predicate) { const reason = predicate(selection.predicate); if (reason) return reason; } if (selection.relational) { if (query.declaration.placement?.kind === "replica") return "aggregate selections in canonical-input scopes"; const { stages, terminals } = selection.relational; if (stages.length !== 1 || stages[0]!.operation.kind !== "source") return "relational expansion, filtering or distinct stages"; if (terminals.length !== 1) return "multiple aggregate terminals"; const terminal = terminals[0]!; if ( terminal.kind !== "aggregate" || terminal.stage !== stages[0]!.id || terminal.keys.length || terminal.order.length || terminal.residual || terminal.where || terminal.having || terminal.first || terminal.all || terminal.after || terminal.reductions.some( (reduction) => reduction.reduction.operator !== "count" || reduction.reduction.operand, ) ) return "grouping or reductions other than a direct relationship count"; if (conditional(selection.selection)) return "conditional aggregate projections"; } const reason = walk(selection.selection); if (reason) return reason; } return undefined; }; return walk(query.selection); }