Skip to main content
Each model is one SQL file with a MODEL() header and one outer SELECT. Models default to streaming tables: StreamBuild creates a ClickHouse table and the materialized view that writes into it. A kind view model instead creates an ordinary terminal query view.
The header grammar is:
  • fields are whitespace-separated key value entries
  • lists use [...]
  • nested mappings use (...)
  • commas are optional separators and may trail the final entry
  • values containing spaces must be quoted
YAML-like key: value and {...} mappings are not accepted. MODEL (); uses all defaults.

SQL contract

The outer statement must be one SELECT. Every outer projection must:
  • be explicit rather than *
  • have a unique alias
  • declare an exact ClickHouse type with CAST(expr AS Type) AS alias or expr::Type AS alias
CTEs may contain stars, untyped expressions, and set operations. A top-level UNION, INTERSECT, or EXCEPT is rejected; wrap it in a CTE and finish with a typed projection. order_by, partition_by, and ttl expressions may reference only output columns.

Header fields

replay_on_change and bounded_replay_fallback are rejected in direct mode. replay_anchor remains valid because direct rebuilds also need replay roots. engine, order_by, partition_by, ttl, settings, replay_anchor, replay_on_change, and bounded_replay_fallback are table-only and are rejected on views. An explicit model ttl overrides [defaults].model_ttl. StreamBuild validates the effective expression against the model’s output columns and includes it in table storage identity, desired state, and drift detection.

Driving references

A model has exactly one untyped driving input:
or:
Source-root models use __source. Downstream models use __ref. Additional joins and subqueries must declare their role:
Mutable references emit a replay warning and prevent the model from being a replay anchor. Both side-reference types participate in graph scope and ordering. The single driving edge determines the streaming and replay path. Reference and mutable side edges both affect graph ordering and scope, but a mutable side edge also prevents the receiving model from becoming a replay anchor. A terminal view may read several graph relations for query-time joins, but it has no outgoing edge to another model.

Terminal views

A terminal view has no driving input and may read any number of sources or models:
View references do not use ref_type: every reference is an ordinary query dependency. Views do not participate in source inference, replay roots, watermarks, retention, or replay population. A view must have zero downstream model edges across the complete project graph, including cross-pipeline references. Other models cannot reference it, while tests and audits may target it. Without relation_name, tables use the effective table_prefix and views use view_prefix. Pipeline naming overrides project naming, whose defaults are tbl__ and view__. Logical refs continue to use filename stems. kafka__, raw__, and mv__ are reserved internal prefixes.

Replay columns

Normalized replay columns are source-independent:
Managed Kafka landing exposes partition, offset, timestamp, and landed-at values. Adopted sources expose the roles declared in their column mapping. Preserve the columns required by the source mode through non-aggregate models when those outputs should remain eligible replay anchors. Aggregate models do not need to expose post-aggregate replay columns. StreamBuild places replay predicates on their input query instead.

Engines and duplicate delivery

Common engines include MergeTree, ReplacingMergeTree, SummingMergeTree, and AggregatingMergeTree, including replicated variants. Engine choice is part of replay correctness: the replay cutoff is inclusive, so authored SQL and storage engines must tolerate duplicate delivery where necessary.