version:0.1 doc:query-planner updated:2026

Query Planner

The planner is the center of the runtime story. It turns semantic packages and query ASTs into validated, explainable, executable plans.

Planner input contract

Queries are built from stable IDs and structured nodes such as select, group_by, where, metric_filters, time, path_policy, and order_by.

query.json
{
  "version": 1,
  "select": [
    {
      "expression": { "measure": "measure.jaffle.order_count" },
      "as": "orders"
    },
    {
      "expression": { "metric": "metric.sales.aov_usd" },
      "as": "aov_usd"
    }
  ],
  "group_by": ["dimension.jaffle_store_name"],
  "time": {
    "temporal_role": "temporal_role.jaffle_order_time",
    "grain": "month"
  },
  "order_by": [{ "field": "orders", "direction": "DESC" }],
  "limit": 25
}
What the planner does
  • Resolve aliases and semantic object references into stable internal objects.
  • Choose a valid root and path set for the requested measures, dimensions, filters, and time axis.
  • Reject ambiguous or unsafe joins before SQL is produced.
  • Rewrite supported mixed-grain shapes into independent leaf aggregates when needed.
  • Lower the resulting plan into typed SQL AST nodes and rendered warehouse SQL.
Scope boundary
Semantic Rails is a semantic layer, not an ELT tool. The planner's relation surface (model relation references, graph keys, semantic joins, segments, SQL AST lowering, rendered SQL) exists to lower queries - it is not a user-facing pipeline API. Arbitrary chained CTEs, materialization policies, and free-form raw-SQL snippets aren't shipped planner inputs. See Current Limits for the boundary list.
Raw SQL to governed query shape

A raw SQL analysis might join a CTE of customers with 10 or more monthly orders back to orders for revenue by store. In the shipped runtime, the governed version should model the order relation, publish durable revenue and order metrics, and express the threshold with a supported contextual metric_predicate rather than passing a free-form CTE pipeline to the planner.

Mixed-grain planning
Case Planner behavior Surface signal
Direct compatible grain Compile normally rewrite_strategy.status = "direct"
Supported mixed-grain shape Rewrite into leaf aggregates and join on final grain rewrite_strategy.status = "rewritten"
Unsafe or unsupported expansion Fail fast MIXED_GRAIN_INVALID or REWRITE_NOT_SUPPORTED
Guardrails the planner enforces
common failures
AMBIGUOUS_ALIAS
AMBIGUOUS_PATH
FANOUT_UNSAFE
INVALID_TEMPORAL_ROLE
INCOMPATIBLE_TEMPORAL_ROLE
PREDICATE_SCOPE_UNSAFE
PREDICATE_TIME_INCOMPATIBLE
CONVERSION_NOT_SUPPORTED
DUPLICATE_OUTPUT_ALIAS
Explain output matters

The public site leans into explainability because it is a real differentiator of the runtime. A legal query can surface:

  • alias resolution results
  • chosen semantic paths
  • rewrite steps
  • logical plan
  • SQL AST
  • rendered warehouse SQL
The goal is not only to answer queries. It is to make the semantic reasoning inspectable enough for builders, engineers, and agents to trust the result.

Continue to Agent Actions for how the public builder APIs expose this planner to apps and agents.