Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 1.5.latest] Nicer error message for contracted model missing 'columns' #8041

Merged
merged 2 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/Fixes-20230704-114752.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Fixes
body: Nicer error message if model with enforced contract is missing 'columns' specification
time: 2023-07-04T11:47:52.976527+02:00
custom:
Author: jtcohen6
Issue: "7943"
5 changes: 5 additions & 0 deletions core/dbt/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2250,6 +2250,11 @@ def get_mismatches(self) -> agate.Table:
return table_from_data_flat(mismatches_sorted, column_names)

def get_message(self) -> str:
if not self.yaml_columns:
return (
"This model has an enforced contract, and its 'columns' specification is missing"
)

table: agate.Table = self.get_mismatches()
# Hack to get Agate table output as string
output = io.StringIO()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@
If any differences in name, data_type or number of columns exist between the two schemas, raises a compiler error
#}
{% macro assert_columns_equivalent(sql) %}

{#-- First ensure the user has defined 'columns' in yaml specification --#}
{%- set user_defined_columns = model['columns'] -%}
{%- if not user_defined_columns -%}
{{ exceptions.raise_contract_error([], []) }}
{%- endif -%}

{#-- Obtain the column schema provided by sql file. #}
{%- set sql_file_provided_columns = get_column_schema_from_query(sql, config.get('sql_header', none)) -%}
{#--Obtain the column schema provided by the schema file by generating an 'empty schema' query from the model's columns. #}
{%- set schema_file_provided_columns = get_column_schema_from_query(get_empty_schema_sql(model['columns'])) -%}
{%- set schema_file_provided_columns = get_column_schema_from_query(get_empty_schema_sql(user_defined_columns)) -%}

{#-- create dictionaries with name and formatted data type and strings for exception #}
{%- set sql_columns = format_columns(sql_file_provided_columns) -%}
Expand Down
15 changes: 15 additions & 0 deletions tests/functional/configs/test_contract_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,3 +471,18 @@ def test__python_errors(self, project):
exc_str = " ".join(str(err_info.value).split())
expected_python_error = "Language Error: Expected 'sql' but found 'python'"
assert expected_python_error in exc_str


class TestModelContractMissingYAMLColumns:
@pytest.fixture(scope="class")
def models(self):
return {
"my_model.sql": my_model_contract_sql,
}

def test__missing_column_contract_error(self, project):
results = run_dbt(["run"], expect_pass=False)
expected_error = (
"This model has an enforced contract, and its 'columns' specification is missing"
)
assert expected_error in results[0].message