Skip to main content
A model is a SQL file that defines one transformation step. Each model produces one table or view in the warehouse.

MODEL() header

Every model file starts with a MODEL() block that declares its materialization, configuration, and schema metadata:

Materialization types

view

Creates a database view. Rebuilt on every run.

table

Creates a table via CREATE 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.
See Snapshots for full configuration, historical input modes, and querying patterns.

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.
Custom materializations use @@@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:
Seeds use __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 the MODEL() header. There is no separate schema.yml for models.

Column-level audits

Attach audits to individual columns inside the columns 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 the MODEL() header declares a type, type enforcement is automatically enabled for that model:
When enabled, SQLBuild casts columns to declared types and uses them for schema-change detection. There is no need to set 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. When contract enforced is set, the declared columns become the authoritative output contract.
Contract enforcement happens at two levels: Compile time - config fields that reference columns (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. VARCHAR where INTEGER was declared) fail with code K013
If any validation fails, the production table is untouched. Types are compared using adapter-aware normalization, so equivalent types across dialects are handled correctly. Contract values: Contracts interact with schema change policies. For snapshot models, 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 specify run_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 a sql("...") hook that executes SQL, or a python("hook_name") hook that calls a Python function from the hooks/ directory.

SQL hooks

SQL hooks support macro expansion (@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:

Python hooks

Python hooks call @hook-decorated functions discovered from the hooks/ directory:
Reference a Python hook in the MODEL() header by name, with optional keyword arguments:
You can mix SQL and Python hooks in the same list:

Hook context

Python hooks receive a HookContext as their first parameter (named ctx, context, or hook_context): Pre-hooks can return 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:

Discovery rules

  • Hook functions are discovered from .py files under hooks/ recursively
  • Files named __init__.py or starting with _ are skipped
  • Each function decorated with @hook is 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 every python("hook_name") reference:
  • The hook name must match a discovered @hook function
  • 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

Four knobs gate compile-time SQL validation, from broadest to narrowest:
  1. settings.sql_analysis - master switch for all SQL-analysis features
  2. --no-sql-validation - per-run CLI kill switch
  3. settings.sql_validation - project-level validation setting
  4. MODEL (sql_validation ...) - per-model override of the project setting
Validation runs only when every broader knob allows it: sql_analysis must be on and --no-sql-validation absent before the project/model sql_validation values are consulted.

Incremental config

See Incremental for detailed usage.

Custom materialization config

Diff config