Default VDE
When you runsqb build in virtual mode without --virtual-env, the VDE name defaults to the active physical target name:
dev, this builds into VDE dev. On first run, it creates physical version relations and logical VDE views for all models.
Explicit VDE
Use--virtual-env to build into a named VDE:
pr_123 does not exist, SQLBuild creates it by inheriting refs from the baseline VDE (the default VDE for the active physical target). Unchanged models share the same physical relations as the baseline - no data is copied.
Only models with changed code, config, or upstream versions get new physical version relations.
Physical naming
Physical version relations are stored in a dedicated schema:dev__sqb_physical.fact_orders__v_8f3a9c12
Logical VDE views are created in a VDE-suffixed schema:
dev__pr_123.fact_orders
When unsuffixed_virtual_env is configured, the named VDE uses the base schema directly:
VDE status
Partial builds
Build a subset of models with--select:
active (working) status if downstream models remain stale. A
follow-up sqb build --virtual-env pr_123 --changes-only without --select builds the remaining
stale models to finalize the VDE. A plain sqb build --virtual-env pr_123 runs the whole selection.
Stale upstream coherence
If a selected model has stale required upstream models, the build blocks by default:--include-stale-upstreams to expand the selection to the minimal set of stale ancestor models needed to make the selected scope coherent:
Stale-driven execution
Virtual environment builds run the full selection by default. Add--changes-only to limit a plan
or build to work whose expected version is not already bound in the target VDE:
--changes-only is a virtual-mode execution option. Direct mode always runs its selected build
scope, although its plan still reports change reasons and uses them to choose incremental actions.
See Planning and change detection for the shared identity and cascade model.
You can make stale-driven execution the default for virtual plans and builds:
Run despite unchanged
run_despite_unchanged lets a table remain eligible during stale-driven execution even when its
version identity is unchanged. This is useful when external data changes cannot be represented by
source freshness.
alwaysruns whenever the model is selected.- A duration such as
24h,30d, or90mkeeps the model eligible while the newest timestamp data version from an upstream source is no older than that duration; it is not a periodic scheduler.
Stale detection
SQLBuild determines which models and seeds need rebuilding by comparing expected version hashes against bound version hashes in the VDE refs:- Expected local hash - derived from the node’s query SQL (for models), content hash (for seeds), version-identity config, and source freshness hashes
- Expected version hash - local hash propagated through upstream dependencies (upstream hash changes cascade downstream)
- Bound version hash - the hash currently stored in the VDE refs from the last successful build
bound != expected. Stale nodes whose own local hash changed are roots (query changed, config changed, function changed). Others are stale due to upstream changed.
Seeds participate in version identity the same way as models. They are loaded into versioned physical tables with logical VDE views on top, and their version hashes and refs are tracked per virtual environment in the state backend. Unchanged seeds are not reloaded.
Source freshness observations and Python node identities are also persisted per virtual environment and included in version hash computation. When a source’s observed data version changes, its downstream models become stale. See Sources: Source freshness for configuration.
Seeded incremental builds
When an incremental model’s version hash changes, SQLBuild seeds the new physical version from the prior physical version before running the incremental delta. This avoids full rebuilds of large incremental tables.
For append models with bounded replay (
replay_on_change bounded-7d), the seed copies only rows before the replay window cutoff. The incremental delta then appends the bounded range without duplicating rows.
Custom materializations
Custom materializations are supported in virtual mode. By default, SQLBuild seeds new physical versions using the direct clone/copy strategy before calling the custommaterialize function.
For custom materializations that need different seeding behavior, define a prepare_version function alongside materialize:
prepare_version is not defined, the framework uses the default clone/copy. Most custom materializations do not need to define it.
VersionPrepareContext provides prior_relation (the source physical version), destination (the new physical destination relation), adapter, connection, execute_sql(), config, and vars.
Custom materializations in virtual mode must write only to ctx.destination. Side-effect writes to other relations are not tracked by virtual state, not cleaned up by janitor, and not restored by rollback.
Functions
Functions are published into the logical VDE schema, not the physical layer. Each VDE has its own copy of function definitions. Function versions are tracked in state and participate in promotion and rollback.Plan
Usesqb plan to preview what a virtual build would do without executing:
- Virtual environment name and status (finalized/working)
- Stale root count and root set
- Stale model count
- Query diffs from prior bound versions
- Remaining stale models after partial selection

