Skip to content

Commit c8657eb

Browse files
authored
Add DuckDB generic test build execution
Adds source-grounded DuckDB not_null/unique generic-test execution for test-only dxt build selections, with dbt-shaped run_results output and native/CLI coverage.
1 parent 0e01874 commit c8657eb

10 files changed

Lines changed: 562 additions & 28 deletions

File tree

.agent/research/dbt-upstream-reference-map.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Every compatibility slice should record:
5555
| Adapter capability and SQL identity | v1 adapter behavior is distributed across adapters and context providers | `crates/dbt-adapter-core/src/lib.rs::AdapterType`, `quote_char`, static-analysis support matrix, microbatch capability; `crates/dbt-adapter-sql/src/ident.rs`, `statements.rs`, `types/*` | Future `src/project/adapter.zig`, `src/project/sql.zig`, and cross-database planner modules |
5656
| DuckDB SQL model execution and run results | `schemas/dbt/run-results/v6.json`; `core/dbt/artifacts/schemas/run/v5/run.py::RunResultOutput`, `process_run_result`, `RunResultsArtifact.from_execution_results`; `core/dbt/compilation.py::Compiler.compile_node`, `write_graph_file` | `crates/dbt-auth/src/duckdb/mod.rs::DuckDbAuth.configure`; `crates/dbt-loader/src/dbt_macro_assets/dbt-duckdb/macros/adapters.sql::duckdb__create_table_as`, `duckdb__create_view_as`; `crates/dbt-loader/src/dbt_macro_assets/dbt-duckdb/macros/materializations/table.sql`; `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/materializations/models/view.sql`; `crates/dbt-schemas/src/schemas/run_results.rs::RunResultOutput`, `RunResultsArtifact`; `crates/dbt-tasks-core/src/stats_to_results.rs`, `utils.rs::build_run_results_artifact` | `src/project/duckdb.zig` owns the first CLI-backed DuckDB execution slice, local-file path guardrails, and table/view SQL rendering; `src/project.zig` currently owns selected-model dependency ordering until a runner module exists; `src/project/run_results.zig` owns the minimal v6 run-results writer; future adapter ABI should replace the CLI backend with embedded DuckDB/linking and add task timing, adapter responses, relation staging, DAG scheduling, seeds, and tests |
5757
| DuckDB seed build execution and run results | `core/dbt/parser/seeds.py::SeedParser`; `core/dbt/artifacts/resources/v1/seed.py::SeedConfig`, `Seed`; `core/dbt/context/providers.py::load_agate_table`; `core/dbt/task/seed.py::SeedRunner`, `SeedTask`; `core/dbt/task/build.py::BuildTask.RUNNER_MAP`; `core/dbt/artifacts/schemas/run/v5/run.py::process_run_result`; `schemas/dbt/run-results/v6.json` | `crates/dbt-parser/src/resolve/resolve_seeds.rs`; `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/materializations/seeds/seed.sql`; `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/materializations/seeds/helpers.sql`; `crates/dbt-loader/src/dbt_macro_assets/dbt-duckdb/macros/seed.sql`; `crates/dbt-adapter/src/adapter/mod.rs::get_seed_file_path`; `crates/dbt-schemas/src/schemas/run_results.rs::RunResultOutput` | `src/project/duckdb.zig` owns the first root-project CSV seed load SQL and file-path rendering; `src/project.zig` owns the seed-only `build` boundary until a runner module exists; `src/project/run_results.zig` owns null compiled fields for seed results; future work must add package seed roots, seed configs, `dxt seed`, mixed build DAG scheduling, and full materialization semantics |
58+
| DuckDB generic test execution and run results | `core/dbt/task/build.py::BuildTask.RUNNER_MAP`; `core/dbt/task/test.py::TestRunner.execute_data_test`, `build_test_run_result`; `core/dbt/artifacts/resources/v1/generic_test.py::GenericTest`; `core/dbt/artifacts/schemas/run/v5/run.py::process_run_result`; `schemas/dbt/run-results/v6.json` | `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/tests/generic/builtin.sql`; `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/generic_test_sql/not_null.sql`; `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/generic_test_sql/unique.sql`; `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/materializations/tests/test.sql`; `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/materializations/tests/helpers.sql`; `crates/dbt-schemas/src/schemas/run_results.rs::ContextRunResult`; `crates/dbt-tasks-core/src/test_aggregation.rs` | `src/project/duckdb.zig` owns the first direct DuckDB rendering/execution for `not_null` and `unique` column generic tests; `src/project.zig` owns the test-only `build` branch until a runner module exists; `src/project/run_results.zig` owns `pass`/`fail` generic-test result serialization; future work must add mixed build DAG scheduling, macro-backed test execution, wider generic/singular/unit/source tests, configs, and store-failures semantics |
5859
| Fusion-style scalable artifacts | v1 JSON artifacts remain the base compatibility contract | README v2 notes JSON compatibility plus Parquet artifacts; `crates/dbt-index-core/src/ingest/ingest_state.rs`, `crates/dbt-index-core/src/db.rs` define metadata parquet directories and DuckDB views under `dbt.*` and `dbt_rt.*` | Future parse cache/state store, not M1 product behavior |
5960
| Semantic layer and metrics | `schema_yaml_readers.py::MetricParser`, `SemanticModelParser`, `SavedQueryParser`; `manifest.py::process_metrics`, semantic manifest validation and writer | `crates/dbt-schemas/src/schemas/semantic_layer/*`, `crates/dbt-schemas/src/schemas/manifest/semantic_model.rs`, `crates/dbt-parser/src/resolve/resolve_semantic_models.rs`, `crates/dbt-parser/src/resolve/validate_semantic_models.rs`, `crates/dbt-metricflow/*` | Future `src/project/semantic.zig`, semantic manifest writer, metric planner; M1 should keep empty maps schema-valid until implemented |
6061

@@ -64,13 +65,15 @@ Every compatibility slice should record:
6465
- Current implemented command surface is `parse`, `ls`, `compile`, `docs
6566
generate`, `run`, `build`, `version`, and help. `compile` and `docs generate`
6667
are render-only artifact boundaries for the supported parser graph. `run`
67-
now executes selected enabled DuckDB SQL models with `table` and `view`
68+
executes selected enabled DuckDB SQL models with `table` and `view`
6869
materializations through a Zig-owned external CLI backend, validates
6970
supported materializations before opening DuckDB, executes selected models in
7071
dependency order, writes `manifest.json`, compiled SQL, and a minimal v6
71-
`run_results.json`. `build` remains a truthful preflight boundary that parses,
72-
selects, compiles, writes artifacts, and then fails before seed/model/test
73-
execution.
72+
`run_results.json`. `build` executes root-project CSV seed-only selections and
73+
test-only selected DuckDB column `not_null`/`unique` generic tests against
74+
already-existing attached relations. Mixed seed/model/test scheduling,
75+
model-build execution, wider generic/singular/unit tests, and full
76+
materialization semantics remain future work.
7477
- `src/project/loader.zig` now owns graph loading order, installed-package
7578
traversal, target-path lookup, project/package resource traversal,
7679
macro/property application sequencing, duplicate checks, and graph sorting.
@@ -113,6 +116,15 @@ Every compatibility slice should record:
113116
execution, package seeds, seed configs, `dxt seed`, and full materialization
114117
semantics remain future work. This is documented in
115118
`.agent/research/m3-duckdb-build-seeds.md`.
119+
- Test-only `dxt build` execution now supports selected DuckDB column-level
120+
`not_null` and `unique` generic tests. It renders the dbt built-in failing-row
121+
query shape directly in Zig, wraps it in the standard `failures`,
122+
`should_warn`, and `should_error` projection, writes `pass`/`fail` run-results
123+
entries, and returns exit code `1` on failed tests. Mixed build scheduling,
124+
macro-backed generic tests, `accepted_values`, `relationships`, singular
125+
tests, unit tests, source tests, custom test configs, and store-failures remain
126+
future work. This is documented in
127+
`.agent/research/m3-duckdb-generic-tests.md`.
116128
- Literal inline model `config(schema=..., alias=...)` is scanned in
117129
`src/project/jinja.zig`, stored on `src/project/types.zig` nodes, and consumed
118130
by `src/project/compiler.zig` for default render-only relation names, refs to
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# M3 DuckDB Generic Test Execution Slice
2+
3+
This slice implements the first executable generic-test boundary for `dxt build`
4+
while keeping product runtime behavior in Zig.
5+
6+
## Scope
7+
8+
- Execute test-only `dxt build` selections when every selected resource is a
9+
generic test.
10+
- Support DuckDB only.
11+
- Support only column-level `not_null` and `unique` generic tests.
12+
- Render the supported built-in SQL directly from the parsed `GenericTestNode`
13+
and its attached relation. Do not execute dbt macros in this slice.
14+
- Write dbt Run Results v6-shaped `run_results.json` with test statuses
15+
`pass` or `fail`, integer `failures`, `compiled: true`, and the compiled
16+
failure-row SQL as `compiled_code`.
17+
- Return exit code `1` when any selected generic test fails.
18+
19+
## Upstream References
20+
21+
dbt Core v1 / Python:
22+
23+
- `core/dbt/task/build.py::BuildTask.RUNNER_MAP` maps `NodeType.Test` to
24+
`TestRunner` during `dbt build`.
25+
- `core/dbt/task/test.py::TestRunner.execute_data_test` runs the test
26+
materialization and expects one result row with `failures`, `should_warn`, and
27+
`should_error`.
28+
- `core/dbt/task/test.py::TestRunner.build_test_run_result` maps the result row
29+
into `pass`, `fail`, or `warn`, and records `failures`.
30+
- `core/dbt/artifacts/resources/v1/generic_test.py::GenericTest` models generic
31+
tests as compiled test resources with `column_name`, `attached_node`, and
32+
`test_metadata`.
33+
- `core/dbt/artifacts/schemas/run/v5/run.py::process_run_result` serializes
34+
compiled resources into run-results `compiled`, `compiled_code`, and
35+
`relation_name` fields.
36+
- `schemas/dbt/run-results/v6.json` defines the artifact contract.
37+
38+
dbt Core v2 / Fusion:
39+
40+
- `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/tests/generic/builtin.sql`
41+
defines built-in generic test wrappers for `unique`, `not_null`,
42+
`accepted_values`, and `relationships`.
43+
- `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/generic_test_sql/not_null.sql`
44+
renders failing rows where the column is null.
45+
- `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/generic_test_sql/unique.sql`
46+
renders duplicate non-null groups as failing rows.
47+
- `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/materializations/tests/test.sql`
48+
runs data tests through the test materialization.
49+
- `crates/dbt-loader/src/dbt_macro_assets/dbt-adapters/macros/materializations/tests/helpers.sql`
50+
wraps failing-row SQL with `failures`, `should_warn`, and `should_error`.
51+
- `crates/dbt-schemas/src/schemas/run_results.rs::ContextRunResult` records
52+
status, message, failures, unique id, compiled fields, and relation fields.
53+
- `crates/dbt-tasks-core/src/test_aggregation.rs` is a Fusion optimization
54+
reference for eligible `not_null` and `unique` tests, but this slice does not
55+
implement aggregation.
56+
57+
## dxt Ownership
58+
59+
- `src/project.zig` owns the current test-only `build` branch until a runner
60+
module exists.
61+
- `src/project/duckdb.zig` owns direct DuckDB rendering/execution for
62+
`not_null` and `unique` generic tests.
63+
- `src/project/run_results.zig` owns the minimal v6 run-results writer for
64+
model, seed, and generic-test results.
65+
- `src/root.zig` owns the CLI exit-code mapping for failed tests.
66+
- `tests/test_cli.py` owns black-box native-binary pass/fail coverage.
67+
68+
## Validation
69+
70+
- Native Zig tests cover generic-test SQL rendering and run-results test result
71+
serialization.
72+
- Python integration tests first run a model into DuckDB, then execute selected
73+
generic tests against the same target database and validate
74+
`run_results.json` against the local Run Results v6 schema slice.
75+
- The failure fixture verifies non-zero test failure exit behavior and persisted
76+
failure counts.
77+
78+
## Stop Conditions
79+
80+
- Do not add mixed build DAG scheduling.
81+
- Do not execute arbitrary macros or adapter dispatch.
82+
- Do not implement `accepted_values`, `relationships`, custom generic tests,
83+
singular tests, unit tests, source tests, `where`, `limit`, `severity`,
84+
`warn_if`, `error_if`, or `store_failures`.
85+
- Do not implement package test runtime behavior beyond graph-selected nodes
86+
whose attached relation already exists.
87+
- Do not add Python product runtime behavior.

PLAN.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,18 @@ not add `dxt seed`, `dxt run` seed execution, package seed execution, seed
328328
configs, mixed build DAG scheduling, tests, hooks, grants, docs persistence,
329329
full-refresh semantics, or adapter materialization macro execution.
330330

331+
Current DuckDB generic test execution source note:
332+
`.agent/research/m3-duckdb-generic-tests.md` maps upstream dbt Core v1
333+
`BuildTask`, `TestRunner`, `GenericTest`, and run-result serialization plus
334+
Fusion built-in generic-test macros and test materialization helpers to dxt's
335+
first executable generic-test slice. This slice lets test-only `dxt build`
336+
selections execute selected DuckDB column-level `not_null` and `unique` generic
337+
tests against already-existing attached relations, write `pass`/`fail`
338+
Run Results v6-shaped artifacts, and return exit code `1` on test failure. It
339+
does not add mixed build DAG scheduling, generic macro execution,
340+
`accepted_values`, `relationships`, singular tests, unit tests, source tests,
341+
custom configs, `store_failures`, or package/runtime macro behavior.
342+
331343
Current project dispatch config source note:
332344
`.agent/research/m2-project-dispatch-config.md` maps upstream dbt Core v1
333345
project `dispatch:` validation, `get_macro_search_order`, and
@@ -743,7 +755,7 @@ Exit criteria:
743755
- `dxt compile` has started as a render-only M2 boundary for the current graph subset. It loads and resolves the same Zig parser graph, applies `--select` and `--exclude`, compiles selected enabled SQL model nodes, writes compiled SQL under `target/compiled/<package>/...`, and emits `compiled`, `compiled_code`, `compiled_path`, `relation_name`, `extra_ctes`, and `extra_ctes_injected` only for compiled model nodes. The current compiler renders `config` to empty text, literal or narrow scalar var-backed `ref`/`source` calls to deterministic quoted relation names, profile-derived `target.*`, current-model `this`, and quoted literal inline `config(schema=..., alias=...)` as default dbt relation schema/identifier components without opening a database connection.
744756
- `dxt docs generate` has started as an adapter-free docs artifact boundary. It loads and resolves the same Zig parser graph, applies `--select` and `--exclude` to compiled model output, writes compiled SQL, writes `manifest.json`, and writes an empty dbt-shaped `catalog.json` because adapter relation introspection is not implemented yet. Macro execution, materializations, tests, profiles-derived relation identity, adapters, `run_results.json`, non-empty `catalog.json`, and `docs serve` remain out of scope.
745757
- `dxt run` has started the M3 DuckDB execution path for selected enabled SQL models. It loads and resolves the same Zig parser graph, applies supported selectors/excludes, compiles selected SQL models, validates that selected models use only `table` or `view` materializations before opening DuckDB, executes selected models in dependency order through a Zig-owned external DuckDB CLI backend, writes compiled SQL, writes `manifest.json`, and writes a minimal dbt-shaped success-only `run_results.json` v6 slice after completed runs. It supports default `target/dxt.duckdb` output plus scalar DuckDB profile `path` resolved relative to the loaded `profiles.yml` directory as a deterministic dxt-local path-base choice for this first CLI-backed slice. It does not execute seeds, tests, snapshots, incremental, ephemeral, hooks, grants, docs persistence, catalog introspection, failure/partial run-results artifacts, relation staging/backup rename parity, threaded scheduling, `:memory:`, MotherDuck, or embedded `libduckdb`.
746-
- `dxt build` remains a truthful execution preflight boundary. It loads and resolves the same Zig parser graph, applies supported selectors/excludes, compiles selected SQL models where applicable, writes `manifest.json`, and then fails before seed/model/test execution with explicit runner boundary errors.
758+
- `dxt build` has started the M3 DuckDB execution path for root-project CSV seed-only selections and test-only selected DuckDB column-level `not_null`/`unique` generic tests. It loads and resolves the same Zig parser graph, applies supported selectors/excludes, writes `manifest.json`, loads seeds through the Zig-owned DuckDB CLI backend, executes supported generic tests against already-existing attached relations, writes a minimal dbt-shaped `run_results.json` v6 slice, and returns exit code `1` when any selected generic test fails. Model-build execution, mixed seed/model/test DAG scheduling, package seeds, seed configs, wider generic tests, singular tests, unit tests, source tests, custom test configs, hooks, grants, docs persistence, full-refresh semantics, `store_failures`, and adapter materialization macro execution remain explicit boundaries.
747759
- Synthetic fixtures cover one model, model refs, seed refs, source refs, narrow scalar var-backed model/source refs with CLI overrides and positional string defaults, exposure refs to models and sources, combined source/model YAML, inline config/tag selection, config materialization selection, comma-intersection selection, YAML model properties and columns, emitted `unique`, `not_null`, `accepted_values`, and `relationships` generic test nodes, project macro artifacts, macro block variants, macro materialization `supported_languages`, and macro properties including patched `docs` and `meta`, configured `macro-paths` replacing the default macro directory, installed package macros with package-qualified calls and package-local macro calls, installed package models, seeds, sources, docs, exposures, package YAML model properties, root package config overrides, and package-qualified/package-local refs/sources, macro calls recorded in model and macro `depends_on.macros`, docs blocks with literal `doc` descriptions, disabled models, disabled ref diagnostics, unmatched model-property warnings, duplicate model and docs diagnostics, unsupported dynamic doc diagnostics, unresolved var diagnostics for var-backed refs without scalar/default values, missing doc diagnostics, malformed docs block diagnostics, unresolved package macro diagnostics, and unsupported unknown macro-call diagnostics.
748760
- The committed M1 public Jaffle gate lives in `scripts/check_jaffle_shop_duckdb_parse.py`. It clones a pinned public Jaffle Shop DuckDB ref into a temporary directory by default, runs the Zig `dxt` binary, validates the current M1 manifest schema slice, asserts the supported partial manifest shape with five SQL models, three CSV seeds, two docs blocks, twenty supported generic test nodes, model/test `refs` artifact fields, dependency maps, materialization/docs config, and checks representative `dxt ls` selector behavior for resource types, materialization config, wildcards, path selectors, and graph expansion. It is a developer-side Python compatibility harness only; product parse/list behavior remains implemented in Zig. Remaining M1 work includes package-provided generic tests/macros beyond the current narrow macro call surface and deeper Jaffle artifact parity.
749761
- Selector wildcard behavior is currently pinned to observed dbt Core 1.10 behavior. dbt Fusion preview currently differs for resource-type-prefixed wildcard selectors such as `model.<package>.*` and filename-suffix path selectors such as `path:*orders.sql`; a future Fusion-compatibility slice must decide whether to support a selector dialect switch or a compatible superset.

0 commit comments

Comments
 (0)