import type { AggregateFunction } from "@metaobjectsdev/metadata"; /** One node in the JOIN tree. `alias` is an auto-generated unique short alias. */ export interface JoinNode { /** Relationship name on the parent object (e.g., "weeks"). */ readonly relationship: string; /** Entity name this join lands on (e.g., "Week"). */ readonly targetEntity: string; /** Auto-assigned SQL alias for this join (e.g., "w", "w0"). */ readonly alias: string; /** Cardinality of the relationship being traversed. */ readonly cardinality: "one" | "many"; /** FK physical column (strategy + @column resolved); lives on whichever side `referenceHolder` indicates. */ readonly fkColumn: string; /** PK physical column (strategy + @column resolved) on the side that does NOT hold the FK. */ readonly pkColumn: string; /** Which side of this hop physically holds the FK: the parent (source) or the child (target). */ readonly referenceHolder: "source" | "target"; /** #209 — `inner` when this is a belongs-to hop whose FK is NOT NULL (required): * the join can neither drop nor NULL-fill a base row, so it is semantically INNER * and matches the hand-written INNER-join view it stands in for. `left` otherwise — * a nullable belongs-to FK, or ANY has-many (inverse-FK) hop, where a base row with * no match must survive (aggregates COALESCE to 0, not drop the row). */ readonly joinType: "inner" | "left"; /** Child joins. */ readonly children: readonly JoinNode[]; } /** Tree of JOINs rooted at the projection's base entity. */ export interface JoinTree { /** Base entity name (e.g., "Program"). */ readonly baseEntity: string; /** SQL alias for the base entity (typically "p", "p0"). */ readonly baseAlias: string; /** Joined entities (could be empty for a flat projection). */ readonly joins: readonly JoinNode[]; } /** * A resolved filter clause. Column refs are already resolved to `alias.column` * (naming-strategy applied), so the emitter is a pure renderer. Mirrors the * canonical attr.filter shape. Used both for an aggregate's scoping `@filter` * (which only ever produces `cmp`/`and`/`or`) and for #207's projection-level * row `@filter` (which may additionally compare an inlined computed expression — * the `exprCmp` node — when a filter ref names an `origin.computed` field). */ export type ViewFilterClause = { readonly kind: "cmp"; readonly ref: string; readonly op: string; readonly value: unknown; } | { readonly kind: "exprCmp"; readonly expr: ViewExprNode; readonly op: string; readonly value: unknown; } | { readonly kind: "and"; readonly clauses: readonly ViewFilterClause[]; } | { readonly kind: "or"; readonly clauses: readonly ViewFilterClause[]; }; /** A scalar literal in a resolved computed expression (mirrors attr.expression's ExprLiteral). */ export type ViewExprLiteral = string | number | boolean | null; /** * A resolved node of an `origin.computed` expression (#195). Field refs are already * lowered to a physical `alias.column` (base alias + naming strategy applied), so the * emitter is a pure tree-walk. Mirrors the closed `attr.expression` node grammar. */ export type ViewExprNode = { readonly kind: "col"; readonly ref: string; } | { readonly kind: "lit"; readonly value: ViewExprLiteral; } | { readonly kind: "cmp"; readonly op: string; readonly left: ViewExprNode; readonly right: ViewExprNode; } | { readonly kind: "nullTest"; readonly negated: boolean; readonly arg: ViewExprNode; } | { readonly kind: "not"; readonly arg: ViewExprNode; } | { readonly kind: "logic"; readonly op: "and" | "or"; readonly args: readonly ViewExprNode[]; } | { readonly kind: "coalesce"; readonly args: readonly ViewExprNode[]; }; /** A resolved ordering key: a physical column (naming-strategy applied) + direction. */ export interface ViewOrderKey { readonly column: string; readonly dir: "asc" | "desc"; } /** One column of the SELECT list. */ export type SelectColumn = { readonly kind: "passthrough"; readonly fieldName: string; readonly dbColAlias: string; readonly sourceAlias: string; readonly sourceColumn: string; } | { readonly kind: "aggregate"; readonly fieldName: string; readonly dbColAlias: string; readonly agg: AggregateFunction; readonly sourceAlias: string; readonly sourceColumn: string; /** Optional scoping filter (origin.aggregate @filter) → SQL aggregate FILTER (WHERE …). */ readonly filter?: ViewFilterClause; } | { readonly kind: "predicateAgg"; readonly fieldName: string; readonly dbColAlias: string; readonly quant: "any" | "all"; readonly sourceAlias: string; readonly joinedPkColumn: string; readonly pred: ViewFilterClause; } | { readonly kind: "collectAgg"; readonly fieldName: string; readonly dbColAlias: string; readonly sourceAlias: string; readonly sourceColumn: string; readonly joinedPkColumn: string; readonly distinct: boolean; /** Element ordering over the @of entity's columns; empty ⇒ value-ascending default. */ readonly orderBy: readonly ViewOrderKey[]; } | { readonly kind: "collectObjectAgg"; readonly fieldName: string; readonly dbColAlias: string; readonly sourceAlias: string; readonly joinedPkColumn: string; /** The declared value object's members, in declaration order. `memberName` is the * emitted JSON key; `sourceColumn` is the TERMINAL entity's physical column it reads. * The loader guarantees every member resolves (ERR_COLLECT_MEMBER_UNRESOLVED). */ readonly members: readonly { readonly memberName: string; readonly sourceColumn: string; }[]; /** Element ordering over the @via terminal entity's columns; empty ⇒ PK ascending. */ readonly orderBy: readonly ViewOrderKey[]; } | { readonly kind: "computed"; readonly fieldName: string; readonly dbColAlias: string; readonly expr: ViewExprNode; } | { readonly kind: "first"; readonly fieldName: string; readonly dbColAlias: string; readonly childEntity: string; readonly childAlias: string; readonly sourceColumn: string; /** Correlation direction (mirrors JoinNode.referenceHolder). */ readonly referenceHolder: "source" | "target"; readonly fkColumn: string; readonly pkColumn: string; readonly childPkColumn: string; readonly orderBy: readonly ViewOrderKey[]; /** Optional scoping filter over the child entity (refs use `childAlias`). */ readonly filter?: ViewFilterClause; }; export interface SelectSpec { readonly columns: readonly SelectColumn[]; } /** Top-level view specification consumed by view-ddl-emit + Drizzle declaration. */ export interface ViewSpec { readonly viewName: string; readonly joinTree: JoinTree; readonly selectSpec: SelectSpec; /** non-aggregate column SQL fragments to put in GROUP BY (empty if no aggregates). */ readonly groupBy: readonly string[]; /** * #207 — a projection-level row `@filter` (view-level WHERE): a resolved predicate * over the projection's OWN fields (each ref already lowered to `alias.column`), * rendered as an outer `WHERE` BEFORE any `GROUP BY` — it scopes which base rows the * view returns (soft-delete / status / type views). Distinct from an aggregate's * `@filter`, which scopes the rows a single aggregate spans. Undefined = no filter. */ readonly where?: ViewFilterClause; } //# sourceMappingURL=view-spec.d.ts.map