Skip to main content

View

A View is a named SQL query stored in the MDL. It behaves like a virtual table — clients can query it by name, and the engine inlines the statement SQL before execution. Unlike a Model, a View does not declare columns explicitly; its schema is inferred from the statement at query time.

Structure

Each view lives in its own directory under views/ as views/<name>/metadata.yml.

The statement SQL can be inline in metadata.yml or in a separate sql.yml file. The sql.yml file takes precedence if both exist.

Inline statement:

# views/top_customers/metadata.yml
name: top_customers
statement: >
SELECT customer_id, SUM(total) AS lifetime_value
FROM wren.public.orders GROUP BY 1 ORDER BY 2 DESC LIMIT 100
properties:
description: "Top customers by lifetime value"

Separate SQL file:

# views/monthly_revenue/metadata.yml
name: monthly_revenue
properties:
description: "Monthly revenue aggregation"
# views/monthly_revenue/sql.yml
statement: >
SELECT DATE_TRUNC('month', order_date) AS month,
SUM(total) AS total_revenue
FROM wren.public.orders
GROUP BY 1

JSON format (MDL manifest)

{
"name": "top_customers",
"statement": "SELECT customer_id, SUM(total) AS lifetime_value FROM wren.public.orders GROUP BY 1 ORDER BY 2 DESC LIMIT 100"
}

Fields

FieldRequiredDescription
nameYesUnique identifier used in SQL queries
statementYesA complete SQL SELECT statement; may reference other models or views
dialectNoSQL dialect of the view's statement (e.g. bigquery, postgres). Currently metadata only — the engine always parses view statements with its generic SQL parser. Requires schema_version: 3. See Dialect Override.
propertiesNoArbitrary key-value metadata (use properties.description for a human-readable description)

Model vs View

ModelView
Data sourcetable_reference or ref_sqlSQL statement
Column declarationsExplicit (with types)Inferred from statement
Relationship columnsSupportedNot supported
Calculated columnsSupportedNot supported
Primary keySupportedNot applicable
Access controlColumn omission, RLAC/CLACColumn omission via statement

Use a Model when you need typed columns, relationships, or calculated fields. Use a View for pre-built queries — dashboards, saved filters, or cross-model aggregations — that you want to expose as a named table.

jaffle_shop Example

The jaffle_shop workspace ships with an empty views.yml (views: []), but views become useful once you have mart-layer models in place. Here are representative examples:

Simple filter view

# views/completed_orders/metadata.yml
name: completed_orders
statement: >
SELECT order_id, customer_id, order_date, amount
FROM orders
WHERE status = 'completed'
properties:
description: "Orders with completed status"
SELECT * FROM completed_orders WHERE amount > 50;

Cross-model aggregation view

# views/customer_order_summary/metadata.yml
name: customer_order_summary
statement: >
SELECT
c.customer_id,
c.first_name,
c.last_name,
COUNT(o.order_id) AS total_orders,
SUM(o.amount) AS lifetime_value
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.first_name, c.last_name
properties:
description: "Per-customer order counts and lifetime value"

The statement references customers and orders by their model names. The engine resolves them through the normal model pipeline after expanding the view.

View referencing another view

# views/vip_customers/metadata.yml
name: vip_customers
statement: >
SELECT customer_id, first_name, last_name, lifetime_value
FROM customer_order_summary
WHERE lifetime_value > 500
properties:
description: "Customers with lifetime value over 500"

Views can reference other views. The engine expands all view references recursively before resolving model references.

Querying a View

Once defined, a view is a first-class table name:

SELECT * FROM completed_orders;

SELECT customer_id, total_orders
FROM customer_order_summary
ORDER BY total_orders DESC
LIMIT 10;

The view name can be qualified with catalog and schema:

SELECT * FROM wren.main.completed_orders;

Engine Internals

Session registration

At session initialisation, each view's statement is parsed into a DataFusion LogicalPlan and wrapped in a ViewTable. The ViewTable is registered under the view's fully-qualified name (catalog.schema.name) in the DataFusion catalog.

view.statement  →  ctx.state().create_logical_plan()
→ ViewTable::new(plan, statement)
→ ctx.register_table(catalog.schema.name, view_table)

Query-time expansion: ExpandWrenViewRule

ExpandWrenViewRule runs as the first analyzer pass — before ModelAnalyzeRule and all other rules. It performs a bottom-up walk of the logical plan tree. Whenever it encounters a TableScan whose name belongs to the MDL and matches a registered view, it replaces the scan node with the view's LogicalPlan wrapped in a subquery alias:

TableScan("completed_orders")
↓ ExpandWrenViewRule
Subquery(
Filter(status = 'completed', TableScan("orders")),
alias = "completed_orders"
)

After the view is inlined, the remaining TableScan("orders") nodes are processed by ModelAnalyzeRule in the next pass, which resolves them to physical tables.

This ordering ensures that a view's statement can freely reference other models or views — all references are resolved in subsequent passes after expansion.

Recursive view expansion

If a view references another view, ExpandWrenViewRule handles the recursion automatically. The transform_up_with_subqueries traversal processes the tree from leaves to root, so inner views are expanded before outer views reference them.