Skip to content

Commit 590de2b

Browse files
committed
fix: warn on contract.enforced=true for materialized_view (#1279)
Upstream dbt does not support contracts on materialized views (https://docs.getdbt.com/docs/mesh/govern/model-contracts). dbt-databricks was silently accepting the flag, hiding yml/SQL drift. Now emits a warning pointing at the dbt docs. Strictly additive; existing build outcomes and constraint behavior are unchanged. Co-authored-by: Isaac
1 parent 59c69f6 commit 590de2b

3 files changed

Lines changed: 113 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## dbt-databricks 1.11.8 (TBD)
2+
3+
### Fixes
4+
5+
- Warn when `contract.enforced: true` is set on a `materialized_view` model ([#1279](https://github.com/databricks/dbt-databricks/issues/1279))
6+
17
## dbt-databricks 1.11.7 (Apr 17, 2026)
28

39
### Features

dbt/include/databricks/macros/relations/materialized_view/create.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
{%- set model_columns = model.get('columns', {}) -%}
2525
{%- set contract_config = config.get('contract') -%}
2626
{%- if contract_config and contract_config.enforced -%}
27+
{%- do exceptions.warn(
28+
"model '" ~ model.name ~ "' sets contract.enforced=true, but contracts are not supported for materialized_view. See https://docs.getdbt.com/docs/mesh/govern/model-contracts"
29+
) -%}
2730
{%- set model_constraints = model.get('constraints', []) -%}
2831
{%- else -%}
2932
{%- set model_constraints = [] -%}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""Tests for contract handling on materialized_view models. See #1279."""
2+
3+
import pytest
4+
from dbt.tests import util
5+
6+
7+
MY_SEED = """
8+
id,value
9+
1,100
10+
2,200
11+
3,300
12+
""".strip()
13+
14+
15+
MV_CONTRACT_ENFORCED_SQL = """
16+
{{ config(materialized='materialized_view') }}
17+
select id, value from {{ ref('my_seed') }}
18+
"""
19+
20+
MV_CONTRACT_ENFORCED_SCHEMA = """
21+
version: 2
22+
23+
models:
24+
- name: mv_contract_enforced
25+
config:
26+
contract:
27+
enforced: true
28+
columns:
29+
- name: id
30+
data_type: bigint
31+
- name: value
32+
data_type: bigint
33+
"""
34+
35+
36+
MV_NOT_NULL_SQL = """
37+
{{ config(materialized='materialized_view') }}
38+
select
39+
case when id = 2 then cast(null as bigint) else id end as id,
40+
value
41+
from {{ ref('my_seed') }}
42+
"""
43+
44+
MV_NOT_NULL_SCHEMA = """
45+
version: 2
46+
47+
models:
48+
- name: mv_not_null
49+
columns:
50+
- name: id
51+
data_type: bigint
52+
constraints:
53+
- type: not_null
54+
- name: value
55+
data_type: bigint
56+
"""
57+
58+
59+
@pytest.mark.dlt
60+
@pytest.mark.skip_profile("databricks_cluster", "databricks_uc_cluster")
61+
class TestMVContractEnforcedEmitsWarning:
62+
@pytest.fixture(scope="class")
63+
def seeds(self):
64+
return {"my_seed.csv": MY_SEED}
65+
66+
@pytest.fixture(scope="class")
67+
def models(self):
68+
return {
69+
"mv_contract_enforced.sql": MV_CONTRACT_ENFORCED_SQL,
70+
"schema.yml": MV_CONTRACT_ENFORCED_SCHEMA,
71+
}
72+
73+
def test_contract_enforced_warning(self, project):
74+
util.run_dbt(["seed"])
75+
_, log_output = util.run_dbt_and_capture(
76+
["run", "--select", "mv_contract_enforced"]
77+
)
78+
assert "contracts are not supported for materialized_view" in log_output.lower()
79+
assert "docs.getdbt.com" in log_output.lower()
80+
81+
82+
@pytest.mark.dlt
83+
@pytest.mark.skip_profile("databricks_cluster", "databricks_uc_cluster")
84+
class TestMVColumnConstraintsAreEnforced:
85+
@pytest.fixture(scope="class")
86+
def seeds(self):
87+
return {"my_seed.csv": MY_SEED}
88+
89+
@pytest.fixture(scope="class")
90+
def models(self):
91+
return {
92+
"mv_not_null.sql": MV_NOT_NULL_SQL,
93+
"schema.yml": MV_NOT_NULL_SCHEMA,
94+
}
95+
96+
def test_not_null_constraint_enforced_at_runtime(self, project):
97+
util.run_dbt(["seed"])
98+
results = util.run_dbt(
99+
["run", "--select", "mv_not_null"],
100+
expect_pass=False,
101+
)
102+
assert len(results) == 1
103+
assert results[0].status == "error"
104+
assert "NOT_NULL_CONSTRAINT_VIOLATED" in str(results[0].message)

0 commit comments

Comments
 (0)