Skip to content

CLI Reference

sqb plan

Preview what SQLBuild will do before executing.

Shows the execution plan without making any changes. Useful for inspecting change detection, backfill policies, and selector scope before building.

sqb plan [SELECTOR ...] [flags]

Positional SELECTOR arguments work like --select, and both forms can be combined. --project-dir <path>, --no-color, and --debug can go before or after the command name.

Flag Description
--no-sql-analysis Disable compile-time SQL analysis (--no-sql-validation is an alias)
--no-cache Bypass the reusable compile-analysis cache for this invocation
--no-python Exclude read-side Python tasks and assets from the plan
--defer-to Resolve unselected model references against another target
--as Compile for another target and show its plan, including model migration decisions, using the current connection. Inspection only.
--json Output the plan as JSON
--selection-diagnostics Warn when a selected model will build on changed upstream models that are not selected; see Selection and staleness
--full-refresh Plan a full rebuild for selected models unless a model sets full_refresh false; full_refresh true forces a model without this flag
--start-cursor Override the start cursor, typed from the selected incremental models’ cursor type; fails with S303 if the selection mixes timestamp and integer cursors. With no cursor model selected, an integer value is read as an integer cursor and anything else as an ISO timestamp
--end-cursor Override the end cursor, typed the same way as --start-cursor
--start-cursor-ts Override start cursor for timestamp incremental models (ISO format)
--end-cursor-ts Override end cursor for timestamp incremental models (ISO format)
--start-cursor-int Override start cursor for integer incremental models
--end-cursor-int Override end cursor for integer incremental models
SELECTOR ... Positional selectors, the same as --select
--select, -s Select specific models
--exclude Exclude specific models
--warehouse <name> Snowflake warehouse for this invocation; overrides the target’s build warehouse group and the connection warehouse

First run:

Plan ready 15 selected, 2 sources to load
Sources to load (2)
├── raw__customers table
└── raw__orders delete_insert (cursor: ordered_at)
Changed functions (2)
├── table_fn__customer_orders table function
│ └── reason first run
└── udf__is_completed_order sql udf
└── reason first run
First run (12)
├── stg_customers view
├── stg_payments view
├── stg_orders view
├── daily_order_partitioned partition_tracked (custom)
├── daily_revenue table
├── dim_customers table
├── fact_orders table
├── customer_status_snapshot merge (timestamp)
│ ├── cursor last_ordered_at (timestamp)
│ └── bounds computed at run time, once model fact_orders is up to date
├── hourly_order_activity delete_insert (timestamp, microbatch)
│ ├── cursor activity_hour (timestamp)
│ ├── mode microbatch
...
└── order_status_index delete_insert (integer)
├── cursor order_id (integer)
└── bounds computed at run time, once model fact_orders is up to date
Seeds (1)
└── waffle_types (first_run)
✓ Plan complete 15 selected, 2 sources to load

Steady state:

Plan ready 15 selected, 2 sources to load
Sources to load (2)
├── raw__customers table
└── raw__orders delete_insert (cursor: ordered_at)
Models (12)
├── stg_customers view
├── stg_payments view
...
└── order_status_index delete_insert (integer)
├── cursor order_id (integer)
└── bounds computed at run time, once model fact_orders is up to date
Functions (2)
├── table_fn__customer_orders table function
└── udf__is_completed_order sql udf
Seeds (1)
└── waffle_types (current)
✓ Plan complete 15 selected, 2 sources to load

When query, function, or schema changes are detected, the plan lists each affected model under its own change with its backfill action. A model that calls a changed function directly appears under Function changed with cause: function <name> changed; models further downstream continue forward. See Replay decisions.

In sqb plan --json, each model’s reason is its own change. A direct caller of a changed function has "reason": "function_changed" and a changed_functions list, and a renamed model whose query also changed has "reason": "renamed" and "query_changed": true. A changed model also carries the detail the text plan prints under it: query_diff when its query changed and the previous query is known, and config_diff when its config changed, each as unified diff lines, and schema_findings for added, removed, or retyped columns.

Warnings and maintenance notices, such as migrate_from can be removed, are listed in a Warnings section before the final Plan complete line. sqb plan --json output is unchanged.

Work that runs before models build is listed first. Model migrations appear under Migrations, and in-place column renames under Column migrations:

Column migrations (1)
└── fact_orders migrate columns
└── amount -> revenue rename in place

When a removed column and an added column look related but are not an exact rename, the schema diff shows a hint such as similar to amount; if this is a rename, add revenue (migrate_from amount) until the change is built.

sqb plan exits 0 when the plan is ready, including plans that only have warnings. It exits 1 when the plan has an error-severity finding that sqb build would refuse, such as an M114 name claim; the final line then reads ✗ Plan failed with errors. sqb plan --json uses the same exit codes, so CI can gate on either form.

When sqb plan --json fails before it can produce a plan, for example on an unknown selector, it still exits 1 and prints its coded error on stderr, and prints the error as JSON on stdout: {"error": {"code": "S007", "message": "...", "help": null}}.

Release note: earlier versions printed Plan complete with errors and exited 0 for such plans.

Planning a scoped selection whose upstream inputs were never built in the target fails with S301 rather than producing a plan that would break at build time:

error[S301]: cannot build selected scope: 3 missing upstream dependencies (stg_orders, stg_payments, waffle_types)

Build the upstream chain first, or select it along with the model: sqb build --select +fact_orders.

The check also covers resources that an attached audit on a selected resource reads, even when the model SQL does not read them. Selecting +fact_orders follows data lineage only, so it does not pull those resources in. When a missing resource is needed only by an audit, the error names the audit, its target, and the resource:

error[S301]: cannot build selected scope: audit 'relationships' on 'stg_orders' reads 'waffle_types', which is not selected and does not exist in the warehouse; select it or build it first

Load or build the resource first (for a seed, sqb seed), or add it to the selection. When both kinds are missing, the error lists the missing upstream dependencies and then the audit reads.

When a selected model reads an external source whose table does not exist in the warehouse, planning fails with S405 before anything runs. The error names each missing source, its table, the selected models that read it, and the source declaration file and line. See Source freshness: Missing source tables.