Silver Layer (Models)
The silver layer is where you write custom SQL transformations — the equivalent of dbt models. Each model is a SQL query paired with TOML configuration that declares dependencies, materialization strategy, and target table.
Model formats
Section titled “Model formats”Sidecar format (recommended)
Section titled “Sidecar format (recommended)”Each model is two files with the same base name:
models/├── fct_orders.sql # Pure SQL — opens cleanly in any SQL editor└── fct_orders.toml # ConfigurationInline format (legacy)
Section titled “Inline format (legacy)”A single SQL file with TOML frontmatter. Supported for backward compatibility; the sidecar format is preferred because embedded TOML breaks SQL editor tooling.
---tomlname = "fct_orders"depends_on = ["stg_orders"]
[strategy]type = "full_refresh"
[target]catalog = "acme_warehouse"schema = "analytics"table = "fct_orders"---
SELECT ...Configuration
Section titled “Configuration”Model TOML fields (full reference: Model Format):
| Field | Required | Description |
|---|---|---|
name | Yes | Model identifier, used in depends_on references |
depends_on | No | List of upstream model names (execution order) |
[strategy] | No | Materialization config (see below) — defaults to full_refresh |
[target] | Yes | Output table: { catalog, schema, table } |
[[sources]] | No | Input tables (for documentation and lineage) |
[strategy]
Section titled “[strategy]”# Incremental[strategy]type = "incremental"timestamp_column = "updated_at"
# Merge[strategy]type = "merge"unique_key = ["customer_id"]update_columns = ["name", "email", "updated_at"] # optional, defaults to all non-key columnsExample: sidecar model
Section titled “Example: sidecar model”models/fct_orders.toml
name = "fct_orders"depends_on = ["stg_orders", "dim_customers"]
[strategy]type = "full_refresh"
[target]catalog = "acme_warehouse"schema = "analytics"table = "fct_orders"models/fct_orders.sql
SELECT o.order_id, o.customer_id, c.customer_name, o.total_amount, o.order_dateFROM acme_warehouse.staging__us_west__shopify.orders oJOIN acme_warehouse.analytics.dim_customers c ON o.customer_id = c.customer_idWHERE o.order_date >= '2024-01-01'Example: merge model
Section titled “Example: merge model”models/dim_customers.toml
name = "dim_customers"depends_on = ["stg_customers"]
[strategy]type = "merge"unique_key = ["customer_id"]
[target]catalog = "acme_warehouse"schema = "analytics"table = "dim_customers"models/dim_customers.sql
SELECT customer_id, customer_name, email, signup_date, current_timestamp() AS updated_atFROM acme_warehouse.staging__us_west__shopify.customersThis generates a MERGE statement:
MERGE INTO acme_warehouse.analytics.dim_customers AS targetUSING ( SELECT customer_id, customer_name, email, signup_date, current_timestamp() AS updated_at FROM acme_warehouse.staging__us_west__shopify.customers) AS sourceON target.customer_id = source.customer_idWHEN MATCHED THEN UPDATE SET *WHEN NOT MATCHED THEN INSERT *Materialization strategies
Section titled “Materialization strategies”| Strategy | When to use | Adapters |
|---|---|---|
full_refresh | Small tables, complex transforms, guaranteed consistency | All |
incremental | Large append-mostly tables, timestamped events | All |
merge | SCDs, upserts by key | All |
time_interval | Partition-keyed reprocessing with @start_date / @end_date | All |
materialized_view | Warehouse-managed view refresh | Databricks |
dynamic_table | Target-lag managed tables | Snowflake |
full_refresh (default)
Section titled “full_refresh (default)”Rebuilds the entire table on every run:
CREATE OR REPLACE TABLE target AS SELECT ...incremental
Section titled “incremental”Appends new rows past the stored watermark:
INSERT INTO target SELECT ... WHERE updated_at > :watermarkWatermarks live in Rocky’s embedded state store (state management) and advance after each successful run.
Upserts by unique key:
MERGE INTO target USING (...) AS sourceON target.key = source.keyWHEN MATCHED THEN UPDATE SET *WHEN NOT MATCHED THEN INSERT *Validation
Section titled “Validation”Run rocky validate to load all models and validate the DAG before execution:
rocky validate --models ./modelsThis checks:
- All model files parse correctly
- All
depends_onreferences point to existing models - No circular dependencies exist
- Target table identifiers pass SQL validation