LIMITATIONS
Every honest engine has this page. DVT's limits come in two very different kinds, and knowing which is which matters more than the list itself: graceful degradations cost performance but never correctness — the engine detects them and takes the safe path automatically. Hard walls are engine realities we surface loudly instead of papering over. Nothing on this page ships wrong rows.
Graceful degradations — slower, never wrong
The architecture rule behind all of these: correctness never depends on transpilability. Your model's full logic always executes — in DuckDB if nowhere else — and every extraction DVT sends to an engine is guaranteed to parse in that engine's own dialect. These guarantees are pinned by a dedicated test battery across ten dialects, not by promise.
- a filter the source dialect can't express isn't pushed down — it still applies in DuckDB compute, where everything you can write in the model is expressible. You extract more rows than optimal; you never lose or gain rows.
- a query the source dialect can't express skips the direct path — single-connection models normally push the whole query to the source engine. If any construct doesn't translate, the attempt cleanly rejects and the federation pipeline runs it instead.
- window functions and subqueries never push down — by design — they can change row semantics at the source. They always compute in DuckDB.
- a table read twice in one model doesn't push filters — both reads share one extraction; pushing one read's filter would starve the other. The engine detects the shape and extracts once, unfiltered, correctly.
- uncertain column attribution extracts the full table — when the engine can't prove which relation a column belongs to, it stops pruning and takes everything. Since 0.2.22 attribution is scoped per SELECT — even unaliased same-named tables on different engines prune and push per branch — so this fallback is rare.
Hard walls — real limits, stated plainly
- federation compute is single-node DuckDB — extraction is parallel and pushdown shrinks it, but the cross-engine join itself runs on the machine executing dvt, bounded by its disk, memory and network. DVT replaces the warehouse's integration role for transformation workloads; it is not a distributed MPP query cluster for hundred-terabyte ad-hoc federation.
- the api-portal is not a low-latency serving layer — every call executes the model live through the federation engine — typically 1–3 seconds. That's the price of answers that are never stale. Put a cache or landed table in front of hot paths.
- Databricks Delta rejects column names with spaces — unless the table enables column mapping — Delta's rule, not DVT's. Cross-engine models carrying space-named columns should land elsewhere or rename at the model boundary.
- tests on foreign-connection sources are skipped — dbt's test runner speaks to one adapter at a time. DVT warns and skips rather than failing your run; test the model that reads the source instead.
- f_incremental without a unique_key is restricted — deltas apply as engine-native merges, which need a key. Keyless jinja models are accepted only for the provably-safe scalar MAX-watermark append; keyless watermark_column models fall back to a full rebuild — loudly, with a warning, never silently.
- Python models compute locally, table-only — same single-node boundary as federation compute: pandas/DuckDB on the executing machine. Incremental Python models don't exist in v1 — put the Python model early and let a downstream f_incremental own the delta.
- Athena is pushdown-only today — it works as a default target through its dbt adapter, but there is no federation extraction from Athena yet. Its underlying S3 data reads directly as a bucket source in the meantime.
How this page stays honest: the graceful-degradation guarantees are enforced by DVT's transpilation-limits test suite — every release proves that extractions parse in their own dialect, that inexpressible predicates degrade instead of erroring, and that advanced query shapes (CTEs, window dedup, aggregations, unions) always render for compute. When a limitation here gets engineered away — as unaliased-branch attribution was in 0.2.22 — it moves off this page and into the release notes.