MODEL() header
Every model file starts with aMODEL() block that declares its materialization, configuration, and schema metadata:
Materialization types
view
Creates a database view. Rebuilt on every run.table
Creates a table viaCREATE TABLE AS. SQLBuild materializes into a staging table first, runs audits, then promotes to the target. Fully rebuilt each time.
incremental
Inserts or updates into an existing table using a cursor-based strategy. See Incremental for full configuration.snapshot
Maintains historical row versions with SCD Type 2 semantics. Supports timestamp-based and value-check-based change detection, historical source inputs, hard delete invalidation, and configurable full-refresh safety policies.custom
User-defined Python materialization function. Custom materializations get full access to the framework including adapter, schema change signals, query change detection, and audit hooks.@@@placeholder syntax for values substituted at runtime. These deferred placeholders are preserved through compilation and resolved by the materialization at execution time. The config block passes arbitrary key-value pairs to the Python materialize() function via ctx.config.
References
Models use typed reference calls that SQLBuild resolves to qualified warehouse relation names during compilation:| Reference | Syntax | Resolves to |
|---|---|---|
| Model | __ref("name") | Another model |
| Seed | __seed("name") | A seed CSV table |
| Source | __source("name") | An external source |
| Scalar UDF | __udf("name") | A user-defined function |
__seed(), not __ref(). Using __ref() with a seed name raises a compile error with a helpful message pointing you to __seed().
See Functions for UDF and table function details.
DAG ordering
SQLBuild automatically discovers the dependency graph from reference calls, then executes models in topological order. Upstream models are always built before their downstream dependents.Schema declarations
Model metadata - description, columns, audits, and type information - lives directly in theMODEL() header. There is no separate schema.yml for models.
Column-level audits
Attach audits to individual columns inside thecolumns block. Simple audits like not_null and unique are listed by name. Parameterized audits like accepted_values pass arguments inline:
Model-level audits
Attach audits to the model itself for multi-column or expression-based checks:Type enforcement
Type enforcement is implicit. If any column in theMODEL() header declares a type, type enforcement is automatically enabled for that model:
type_enforcement: true explicitly.
Contracts
Contracts enforce that a model’s output matches its declared column schema exactly - column names, column count, and column types. Whencontract enforced is set, the declared columns become the authoritative output contract.
unique_key, cursor, updated_at, check_columns) are validated against the declared column names. If a referenced column is not in the contract, compilation fails.
Runtime - after materialization into the staging table, SQLBuild inspects the actual output columns and validates them against the contract before promotion:
- Missing declared columns fail with code
K010 - Extra undeclared columns fail with code
K011 - Type mismatches (e.g.
VARCHARwhereINTEGERwas declared) fail with codeK013
| Value | Behavior |
|---|---|
enforced | Declared columns are the complete, authoritative output schema |
none | No contract enforcement (default) |
snapshot_schema_change append_new_columns is incompatible with contract enforced because appending columns would violate the contract.
Audit run scope
Audits on incremental models can specifyrun_scope to control when they execute:
delta_and_final runs the audit against each delta batch before DML and again against the target after all batches complete. See Audits for details.
Hooks
Pre-hooks and post-hooks run before and after materialization. Each entry is either asql("...") hook that executes SQL, or a python("hook_name") hook that calls a Python function from the hooks/ directory.
SQL hooks
@macro()), project variables (@@name), environment variables (@@ENV:NAME), and context variables (@@CTX:). SQL is validated at compile time when SQL analysis is enabled.
Available context variables in hooks:
| Variable | Value |
|---|---|
@@CTX:destination.qualified | Fully qualified destination relation name |
@@CTX:destination.schema | Destination schema |
@@CTX:destination.table | Destination relation name |
@@CTX:model.name | Model name |
@@CTX:run.target | Active target name |
@@CTX:run.id | Current run ID |
Python hooks
Python hooks call@hook-decorated functions discovered from the hooks/ directory:
Hook context
Python hooks receive aHookContext as their first parameter (named ctx, context, or hook_context):
| Field | Description |
|---|---|
ctx.model_name | Name of the model being built |
ctx.phase | pre_hooks or post_hooks |
ctx.hook_name | Name of the hook being invoked |
ctx.run_id | Current run ID |
ctx.target | Active target name |
ctx.vars | Project variables |
ctx.destination.qualified | Fully qualified destination relation name |
ctx.destination.schema | Destination schema |
ctx.destination.name | Destination relation name |
ctx.destination.database | Destination database |
ctx.adapter | Adapter instance |
ctx.connection | Live connection |
ctx.execute_sql(sql) | Execute SQL on the connection |
ctx.query(sql) | Execute SQL and return rows |
ctx.log(message) | Log to the run output |
ctx.skip(reason, mode=...) | Skip the model’s materialization. mode accepts "soft" (default) or "hard" (blocks downstream models). |
ctx.providers | Access discovered providers by name |
ctx.skip(...) to skip the model’s materialization entirely. A soft skip skips only this model; a hard skip also blocks downstream models. Providers can also be injected directly as hook function parameters by name. See Providers.
Hook decorator
The@hook decorator accepts optional metadata:
| Argument | Description |
|---|---|
name | Override the hook name (defaults to the function name) |
description | Human-readable description (defaults to the function docstring) |
Discovery rules
- Hook functions are discovered from
.pyfiles underhooks/recursively - Files named
__init__.pyor starting with_are skipped - Each function decorated with
@hookis registered by name - Hook names must be unique across all hook files
- Python hook references in MODEL() headers are validated at compile time: unknown names, unknown kwargs, and missing required parameters all raise compile errors
Validation
At compile time, SQLBuild validates everypython("hook_name") reference:
- The hook name must match a discovered
@hookfunction - Any keyword arguments passed in the MODEL() header must match parameters on the function signature
- If the function does not accept
**kwargs, unknown arguments raise a compile error
Config reference
Common config
| Field | Description |
|---|---|
materialized | view, table, incremental, or a custom materialization name |
tags | List of tags for selector filtering |
description | Human-readable description of the model |
columns | Column declarations with optional types, audits, and descriptions |
audits | Model-level audit instances |
schema | Override target schema |
database | Override target database |
alias | Override target relation name |
pre_hooks | Lifecycle hooks to run before materialization: sql("...") and/or python("hook_name") entries |
post_hooks | Lifecycle hooks to run after materialization: sql("...") and/or python("hook_name") entries |
enabled | Set to false to skip the model |
contract | enforced or none. When enforced, declared columns are the authoritative output schema. |
Incremental config
| Field | Description |
|---|---|
incremental_strategy | append, delete_insert, or merge |
cursor | Output column used to track incremental position |
cursor_type | timestamp or integer |
cursor_grain | Time grain for timestamp cursors: second, minute, hour, day, month, year |
cursor_start | Lower bound floor for the cursor |
cursor_inputs | Map of upstream ref/source names to their cursor columns |
unique_key | Column(s) used for merge and delete_insert matching |
incremental_mode | Set to microbatch to enable batched execution |
batch_size | Batch window size (e.g. 1d, 1h, or an integer) |
lookback | Extend the replay window backwards to re-process recent data |
on_schema_change | append_new_columns, sync_all_columns, ignore, or fail |
replay_on_change | forward (default), full, or bounded-<duration> (e.g. bounded-14d) |
run_despite_unchanged | Force periodic rebuilds: always or a duration (e.g. 24h, 30d). Table materializations only. |
Custom materialization config
| Field | Description |
|---|---|
config | Arbitrary key-value pairs passed to ctx.config in the Python function |
placeholders | Default values for @@@placeholder tokens in the SQL |
Diff config
| Field | Description |
|---|---|
row_diff_exclude_columns | Columns to exclude from row-level diff comparisons |
row_diff_tolerances | Tolerance rules for numeric diff comparisons |

