Skip to content

Models

Configuration

MODEL() header fields and SQL-analysis controls.

Every MODEL() key must be one of the fields on this page. Any other key fails compilation with D002, naming the key and its file and line and suggesting the nearest supported key. SQL comments (-- ... and /* ... */) inside a header are ignored:

error[D002]: MODEL() in 'models/fct_orders.sql:3' has unsupported keys: descripton
= help: did you mean 'description'?

[defaults] and [path_defaults.*] in sqlbuild_project.toml reject unknown keys in the same way with D001. Values that a custom materialization reads belong inside its config (...) block, which accepts any keys.

Field Description
materialized view, table, incremental, snapshot, or a custom materialization name
tags Tags used by selectors
critical true marks the model as critical, so sqb review builds and compares it whenever something upstream of it changes (default false)
description Required human-readable model description; see Descriptions
columns Model-local column declarations, inherited-column audit augmentation, or column renames with migrate_from (incremental and snapshot models; see Column migrations). On a table or view model that declares its own migrate_from, a column migrate_from only names the old column in the compatibility view
model_schema Reusable column schema name
audits Model-level audit instances
enums Model-local enum declarations; names must begin with _
constants Model-local scalar, list, set, or object constants; names must begin with _. Use constant(...) for an explicit decimal type or collection rendering override.
schema Destination warehouse schema override
database Destination database override
alias Destination relation-name override
pre_hooks Ordered inline_sql(...), sql("name", ...), or python("name", ...) hooks before materialization
post_hooks Ordered inline_sql(...), sql("name", ...), or python("name", ...) hooks after materialization
enabled Set to false to disable the model
contract none for an open statically checked declaration, or enforced for an exact declaration
sql_analysis Per-model SQL-analysis override
dynamic_columns Named pivot column families with pivot_column, value_column, aggregate, type, and name_pattern
audit_factories Audit factories that generate audit cases for this model; see Audits
max_duration Time limit for one build attempt of this model, or "disabled"; see Execution time limit

Every named resource must have a non-empty description. A missing description is a P010 compile error, so sqb plan and sqb build stop too. There is no setting to turn the check off. One compile reports every missing description, each at the resource’s declaration with the exact text to add:

error[P010]: model 'orders_summary' has no description
--> models/marts/orders_summary.sql:1:1
= help: to describe model 'orders_summary', add this to the MODEL header in models/marts/orders_summary.sql:
MODEL (
description "What one row of orders_summary represents",
...
);
Resource Where the description goes
Model MODEL (description "..."), or the description of its model_schema
Scenario SCENARIO (description "...")
Seed description: on its entry in seeds/*.yml
Source description: on its entry in sources/*.yml (or on the dlt resource)
SQL function or table function FUNCTION (description "...")
Python UDF @udf(description="...") or the function docstring
Named SQL hook HOOK (description "...")
Python hook, task, asset, check description="..." on the decorator or the function docstring
Loader @loader(description="...") or the function docstring; a loader that feeds a declared source may instead rely on the source’s YAML description
Provider The provider class docstring

SQL tests, audits, macros, enums, constants, materializations and event or output sinks do not need a description. Descriptions do not change fingerprints, so adding or editing one does not rebuild a model or redeploy a function (a Python UDF docstring is part of the deployed body, so editing it does). sqb format --check also fails when a MODEL, SCENARIO, FUNCTION or HOOK header has no description (a MODEL header that uses model_schema is left to compile), and sqb format still moves a leading comment into an empty model description.

A quoted value in any SQLBuild header (MODEL, SCENARIO, SCHEMA, FUNCTION, HOOK, MACRO, TEST, AUDIT, ENUM, CONSTANT) is written as "..." or '...'. Text that contains a double quote goes between triple quotes, on one line or across several. Inside """...""", " and ' need no escaping:

MODEL (
description """Matches "Miss" and "Mrs." titles, with or without a dot.""",
...
);

A " inside a "..." string ends it, so it is an error with its line and column rather than the start of an escape. A description has no \" escape and no "" doubling: in a "..." description a backslash is an ordinary character, so "Files under C:\" ends with a backslash and "Path C:\\temp" stores both backslashes. In a '...' description, \' and '' both stand for one ', as in 'it\'s' and 'it''s'. sqb format --fix rewrites a description that escapes its quotes as \" or doubles them as "" into a """...""" string holding real quotes:

error[D002]: MODEL(...) in 'models/customers.sql:2:34' contains invalid SQLBuild header syntax: a double quote ends this "..." string early; use """…""" for text containing double quotes
= help: write the text between triple quotes, where quotes need no escaping: description """Matches "Miss"."""; sqb format --fix rewrites a description that escapes its quotes as \" or doubles them as ""

A """...""" string ends at the first three double quotes in a row. Up to two quotes just before them belong to the text, so the text may end with a quote, but it cannot contain three in a row. A """ inside the text and a """ that is never closed fail with the position of the offending quote or of the opening """.

Descriptions are prose, so SQLBuild stores them folded: the same text gives the same description whether it is written on one line or wrapped, and at any indentation.

  • Text on one line is stored exactly as written.
  • Otherwise the spaces at either end are dropped, a line break and the spaces around it become one space, and a blank line starts a new paragraph (stored as \n\n).
MODEL (
description """
Customers with a "woman's title" flag,
one row each.
Built from the raw seed.
""",
);

stores Customers with a "woman's title" flag, one row each., a paragraph break, and Built from the raw seed.

These strings are folded prose:

String Where
description The top level of every header. MODEL, SCENARIO, SCHEMA, FUNCTION, HOOK and MACRO headers take one; AUDIT, ENUM, CONSTANT and TEST headers reject the key
Column description Entries of columns (...) in MODEL and SCHEMA headers
Audit description The description option of an audit in audits [...]
Exception reason reason of [[rules.rule_exceptions]], [[rules.graph_edge_exceptions]], [[rules.rule_ignores]], [[rules.select_star_allow]] and [[rules.threshold_overrides]] in sqlbuild_project.toml

Every other string means exactly what it says and keeps its text as written, line breaks included: SQL such as audit predicate and where_condition arguments or inline_sql(...) hooks, regular expressions such as name_pattern, literal values in meta, constants, placeholders and audit arguments, and names and paths. In these strings a backslash still escapes the character after it inside "..." and '...'; a """...""" string takes its text exactly as written, backslashes included. sqb format lays descriptions out in one canonical form; see Line width and descriptions.

SQL analysis has project, invocation, and model gates:

  1. settings.sql_analysis must be enabled.
  2. --no-sql-analysis must not be present.
  3. MODEL (sql_analysis false) can disable analysis for that model. If omitted, the project setting is used.

A model cannot re-enable analysis when either broader gate disables it. The older sql_validation and --no-sql-validation spellings remain compatibility aliases; use sql_analysis for new configuration.

A project can stop opt-outs from hiding real findings:

sqlbuild_project.toml
[settings]
require_sql_analysis = true # default false

With this setting, sql_analysis false from a MODEL header or a [path_defaults] entry is accepted only on a model whose SQL the parser cannot read: its query or one of its SQL hooks. On a model that parses, compile reports a P009 error at the header key or [path_defaults] entry, with the findings the opt-out was hiding counted by kind:

error[P009]: `sql_analysis false` is not needed for model 'orders_summary'
--> models/marts/orders_summary.sql:4:3
= note: sqlbuild_project.toml sets [settings] require_sql_analysis = true, which only allows `sql_analysis false` on SQL that SQLBuild cannot parse; this model parses successfully.
= help: remove `sql_analysis false` and fix the findings it was hiding: 2 unknown columns, 1 type mismatch (run `sqb compile` to see them)
= help: to allow `sql_analysis false` on any model, SQL test or audit, set this in sqlbuild_project.toml:
[settings]
require_sql_analysis = false

TEST(...) and AUDIT(...) headers (singular and generic audits) also accept sql_analysis false, which skips compile-time SQL checks on that test or audit; the SQL still runs. SQL tests of a model with sql_analysis false, and audits attached to it, are not analysed either. require_sql_analysis = true applies the same rule to these headers: the opt-out is accepted only when that test or audit SQL cannot be parsed, otherwise compile reports P009 at the header key.

[settings] sql_analysis = false in sqlbuild_local.toml is then a D001 error, and require_sql_analysis itself can only be set in sqlbuild_project.toml. --no-sql-analysis still turns analysis off for one run, including the checks on audits, SQL tests and SQL hooks.

When the parser cannot read a model, the P001 syntax error shows the exact MODEL header entry (sql_analysis false,) and asks you to report the SQL so the parser can support it.

Field Description
table_type Snowflake table type: permanent, transient, or inherit
time_travel_retention Managed time travel retention such as 7d, inherit, or disabled
engine, order_by, partition_by, primary_key, table_settings ClickHouse physical layout of table and incremental models; see ClickHouse
run_despite_unchanged Removed with virtual environments. It is rejected in MODEL headers, project defaults, and path defaults.

Table promotion mode is a project setting rather than a MODEL() field. Staged promotion is the default. Immediate promotion is incompatible with model type enforcement and exact contracts; see Materializations.

Field Description
incremental_strategy append, delete_insert, or merge
cursor Output column used to track incremental position
cursor_type timestamp or integer
cursor_grain Timestamp grain such as second, hour, or day
cursor_start Lower cursor bound
cursor_end Upper cursor bound
cursor_start_max_ahead Largest distance a discovered automatic start may sit ahead of the invocation time, or disabled
cursor_start_max_action cap or error when the start exceeds cursor_start_max_ahead
cursor_future_max_distance Largest distance a discovered cursor watermark may sit in the future, or disabled
cursor_future_action cap or error when a watermark exceeds cursor_future_max_distance
cursor_inputs Upstream names mapped to cursor columns
unique_key Merge or delete/insert matching columns
incremental_mode Set to microbatch for batched execution
microbatch_strategy Required in microbatch mode: watermark or rolling_window
cursor_watermark_mode Watermark strategy policy: all or any
batch_size Timestamp duration string such as 1d or 1h; use a numeric string such as "1000" for an integer cursor
batch_concurrency Concurrent batch workers; values above 1 require delete_insert and the project concurrency gate
microbatch_limit Nested max_batches and action policy for watermark execution
max_microbatches Legacy scalar batch-count guard; use microbatch_limit for new models
unaccounted_partition_policy Handling for microbatch partitions with no recorded completion: synthesize, recover_empty, or recover_all
lookback Backward replay extension
append_cursor_inclusive Include (true, default) or exclude (false) the current append-cursor boundary
merge_exclude_columns Columns left unchanged by matched-row merge updates
full_refresh Optional model execution override: false always runs incrementally, true always full-refreshes, and omission follows the command
on_schema_change append_new_columns, sync_all_columns, ignore, or fail
replay_on_change forward, full, or bounded-<duration>
migrate_from Old model name (or schema.name) whose data this model takes over; see Model migrations. Also valid on snapshot, table, and view models
migrate_force true replaces a destination that already has its own build history during a migration. Incremental and snapshot models only
old_name_view How long a compatibility view keeps the old name working after a migration, such as 7d, or false for none. Overrides [migrations] old_name_views; valid on every model

See Incremental for full semantics.

Unknown on_schema_change or replay_on_change values fail compilation, in MODEL() headers, [defaults] and [path_defaults]. The error names the value and lists the valid ones. bounded-<duration> needs a positive duration such as bounded-14d.

Field Description
unique_key Columns identifying one logical record
snapshot_strategy timestamp or check
updated_at Source update timestamp for the timestamp strategy
check_columns Columns compared by the check strategy, or [*]
observed_at Observation timestamp for historical inputs
historical_input snapshot or changes
initial_valid_from Initial validity policy for first-seen rows
invalidate_hard_deletes Close records that disappear from current-state input
valid_from_column Override the generated valid-from column name
valid_to_column Override the generated valid-to column name
snapshot_full_refresh Snapshot full-refresh safety policy
snapshot_schema_change Snapshot schema-change policy

See Snapshots for strategy combinations, defaults, and safety behavior.

Field Description
config Arbitrary values passed to ctx.config
placeholders Defaults for runtime @@@placeholder tokens
Field Description
row_diff_exclude_columns Columns excluded from row-level comparison
row_diff_tolerances Numeric comparison tolerances
row_diff_sample_rows Deterministic unique-key sample size; 0 disables inherited sampling
row_diff_sample_seed Integer seed used for deterministic key hashing

max_duration bounds how long one build attempt of a model may run. It has no default: a model without it, or with max_duration "disabled", runs until it finishes. Set it in the usual layers, with the MODEL() header taking precedence over the nearest path default, and the path default over [defaults]:

[defaults]
max_duration = "30m"
[path_defaults."marts/order_history"]
max_duration = "2h"
MODEL (
description 'Daily customer order counts.',
materialized table,
max_duration "10m"
);

The limit is a positive fixed duration such as "90s", "15m" or "1h30m". One deadline covers the whole attempt, starting when the model begins executing: its hooks, statements, attached audits and every microbatch. Time spent waiting for upstream models or for a free worker does not count.

At the deadline SQLBuild cancels the model’s running statements through the adapter (see the adapter table), runs no further statements for the attempt, and reports the model as timed out with the elapsed time, the limit, where it was set, the cancellation outcome and the query IDs it saw. Statements that already completed are not undone, but the attempt stops before any later promotion or swap, so a staged table is not published. Models that depend on it are skipped as for any failure, and independent models continue. A model that finishes successfully just after its limit keeps its successful result.

The per-model limit is separate from the target’s whole-build max_duration. When both apply, each statement is bounded by whichever deadline comes first.