SWITCHING THE DEFAULT TARGET
The default target is where your standard models run — and it's a setting, not a commitment. Because federation models are written in DuckDB dialect and never depend on the default target's engine, swapping the entire lakehouse under a DVT project is a profile change, not a rewrite.
This page is the concept, told through a real migration we ran: a 67-model project moved from a PostgreSQL default target to Databricks. Total code change: 2 profile lines, 1 cast fix, and 4 lines of seed config. All 67 models green on the first full run.
WHY THIS WORKS: THE DUAL-DIALECT DESIGN
A DVT project contains two kinds of SQL:
FEDERATION MODELS — DUCKDB DIALECT
Decomposed, transpiled per source engine, computed in DuckDB, loaded anywhere by Sling. They never touch the default target's SQL dialect — so a target switch can't break them.
STANDARD MODELS — TARGET DIALECT
table / view / incremental models compile and run on the default target through the official dbt adapter — as do the macros, snapshots and tests they carry. This is the only SQL the target's dialect touches — and most ANSI SQL ports cleanly.
So the blast radius of a lakehouse switch is limited to engine-specific syntax in your standard models — typically date functions and casts. Everything else moves untouched.
THE ONE-COMMAND WAY: DVT FLIP-TARGET-TO
One command does the whole move. It makes the new output the default target AND takes every file that runs on it — models, snapshots, tests, analyses, macros — into the new engine's dialect. Since 0.2.59 there is one translator: the AI lane, with machines judging every answer. Files already valid on the new engine are left untouched; your Jinja survives byte-for-byte; federated and target-pinned models are never touched.
dvt flip-target-to dbx_dev --dry-run # the ledger: what flips, what doesn't dvt flip-target-to dbx_dev # flip the files + the default target
Every changed file keeps a timestamped .bak. Each file that needs the new dialect gets its own bounded AI conversation, and no answer is written until the validators accept it — Jinja intact, parses in the new dialect, no function the engine lacks. Then the new engine itself compiles the project as the final judge. A file the lane cannot carry is kept as written and named with its full path. The default target always moves. It's also in Chat with Martin as /flip-target-to.
config-checkup runs first, always: a model declared table whose dependencies are federated is really DuckDB SQL — the checkup fixes its config so the flip correctly skips it instead of translating it into corruption. Two known limits: the flip translates only while the AI-Proxy is connected (without it, the target moves and every file still to translate is named); and on Oracle, custom-schema models need the schema user to exist first — Oracle schemas are users.
That's the concept. The mechanics — the full ledger, role changes, the lane, the engine judge, backups, every flag — live on the command's own page: dvt flip-target-to →
STEP 1 — POINT THE PROFILE AT THE NEW ENGINE
The manual steps below are what the command automates — worth reading once so you know what actually changes. It starts in profiles.yml: change the target: to an output that already exists in your profile (or add one):
my_project:
target: dbx_dev # was: pg_dev — this is the whole switch
outputs:
pg_dev:
type: postgres
# ...
dbx_dev:
type: databricks
catalog: main
schema: analytics
host: xxxx.cloud.databricks.com
http_path: /sql/1.0/warehouses/xxxx
token: "{{ env_var('DATABRICKS_TOKEN') }}"Then dvt sync installs the new adapter and drivers, and dvt debug --all verifies every connection on both the adapter and Sling layers before you run anything.
STEP 2 — FIX ENGINE-SPECIFIC SQL (USUALLY CASTS)
Standard models now compile in the new engine's dialect. In our migration exactly one model needed a change: Databricks runs ANSI mode, which rejects implicit string→date casts that PostgreSQL tolerated.
-- before (worked on postgres, implicit cast)
where transaction_date >= '2015-01-01'
-- after (explicit ANSI cast, works everywhere)
where transaction_date >= cast('2015-01-01' as date)Writing standard models in portable ANSI SQL from day one makes this step a no-op.
STEP 3 — PIN SEED COLUMN TYPES WHERE NEEDED
Engines infer seed column types differently. DVT honors dbt-native +column_types in dbt_project.yml and passes them through Sling — our migration needed 4 lines for two seeds with non-ISO date strings:
seeds:
my_project:
transactions_b:
+column_types:
transaction_date: dateSTEP 4 — RUN
dvt build
dvt build does the rest: seeds load through Sling, standard models run on the new engine, and federation models run exactly as before — same sources, same DuckDB compute, results now landing in the new default target.
The real migration result: 67 of 67 models green after a 2-line profile change, 1 cast fix, and 4 lines of seed config. The cross-engine catalog (dvt docs) reflected the switch automatically — models that were stamped postgres now stamp databricks.
WHAT THIS MEANS
- ▸No vendor lock-in at the transformation layer — your models are the asset, the engine is a setting.
- ▸Trial a warehouse migration on a branch: switch the target, run, compare. Roll back by switching it back.
- ▸Run dev on a cheap engine (PostgreSQL, DuckDB) and prod on the warehouse — same project, different target.
- ▸Per-model target= overrides let you migrate gradually instead of all at once.