Skip to content

Commit

Permalink
Touch-ups
Browse files Browse the repository at this point in the history
  • Loading branch information
jtcohen6 committed Apr 11, 2023
1 parent 09c9bb9 commit 5d58707
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 26 deletions.
65 changes: 42 additions & 23 deletions core/dbt/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import builtins
import json
import re
import io
import agate
from typing import Any, Dict, List, Mapping, Optional, Union

from dbt.dataclass_schema import ValidationError
Expand Down Expand Up @@ -2145,51 +2147,68 @@ def __init__(self, yaml_columns, sql_columns):
self.sql_columns = sql_columns
super().__init__(msg=self.get_message())

def get_message(self) -> str:
msg = (
"This model has an enforced contract that failed.\n"
"Please ensure the name, data_type, and number of columns in your `yml` file "
"match the columns in your SQL file.\n"
# separate column headers by 20 spaces
f"{'sql col:' : <20} {'yaml col:' : <20} {'issue:' : <20}\n"
)
def get_mismatches(self) -> agate.Table:
# avoid a circular import
from dbt.clients.agate_helper import table_from_data_flat

# list of ordered matches (or not matches) for printing
ordered_match = []
column_names = ["column_name", "definition_type", "contract_type", "mismatch_reason"]
# list of mismatches
mismatches: List[Dict[str, str]] = []
# track sql cols so we don't need another for loop later
sql_col_set = set()
# for each sql col list
for sql_col in self.sql_columns:
# add sql col to set
sql_col_set.add(sql_col['name'])
sql_col_set.add(sql_col["name"])
# for each yaml col list
for i, yaml_col in enumerate(self.yaml_columns):
# if name matches
if sql_col['name'] == yaml_col['name']:
if sql_col["name"] == yaml_col["name"]:
# if type matches
if sql_col['formatted'] == yaml_col['formatted']:
# its a perfect match!
ordered_match += [[sql_col['formatted'], yaml_col['formatted'], '']]
if sql_col["formatted"] == yaml_col["formatted"]:
# its a perfect match! don't include in mismatch table
break
else:
# same name, diff type
ordered_match += [[sql_col['formatted'], yaml_col['formatted'], 'Incorrect type']]
row = [
sql_col["name"],
sql_col["data_type"],
yaml_col["data_type"],
"data type",
]
mismatches += [dict(zip(column_names, row))]
break
# if last loop, then no name match
if i == len(self.yaml_columns)-1:
ordered_match += [[sql_col['formatted'], '', 'No match']]
if i == len(self.yaml_columns) - 1:
row = [sql_col["name"], sql_col["data_type"], "", "missing in contract"]
mismatches += [dict(zip(column_names, row))]

# now add all yaml cols without a match
for yaml_col in self.yaml_columns:
if yaml_col['name'] not in sql_col_set:
ordered_match += [['', yaml_col['name'], 'No match']]
if yaml_col["name"] not in sql_col_set:
row = [yaml_col["name"], "", yaml_col["data_type"], "missing in definition"]
mismatches += [dict(zip(column_names, row))]

mismatches_sorted = sorted(mismatches, key=lambda d: d["column_name"])
return table_from_data_flat(mismatches_sorted, column_names)

# add each match to the message, printed in columns
for match in ordered_match:
msg += f'{match[0] : <20} {match[1] : <20} {match[2] : <20}\n'
def get_message(self) -> str:
table: agate.Table = self.get_mismatches()
# Hack to get Agate table output as string
output = io.StringIO()
table.print_table(output=output, max_rows=None, max_column_width=50) # type: ignore
mismatches = output.getvalue()

msg = (
"This model has an enforced contract that failed.\n"
"Please ensure the name, data_type, and number of columns in your contract "
"match the columns in your model's definition.\n\n"
f"{mismatches}"
)

return msg


# not modifying these since rpc should be deprecated soon
class UnknownAsyncIDException(Exception):
CODE = 10012
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@

{#-- create dictionaries with name and formatted data type and strings for exception #}
{%- set sql_columns = format_columns(sql_file_provided_columns) -%}
{%- set string_sql_columns = stringify_formatted_columns(sql_columns) -%}
{%- set yaml_columns = format_columns(schema_file_provided_columns) -%}
{%- set string_yaml_columns = stringify_formatted_columns(yaml_columns) -%}

{%- if sql_columns|length != yaml_columns|length -%}
{%- do exceptions.raise_contract_error(yaml_columns, sql_columns) -%}
Expand Down Expand Up @@ -70,7 +68,7 @@
{% set formatted_columns = [] %}
{% for column in columns %}
{%- set formatted_column = adapter.dispatch('format_column', 'dbt')(column) -%}
{%- do formatted_columns.append({'name': column.name, 'formatted': formatted_column}) -%}
{%- do formatted_columns.append({'name': column.name, 'data_type': column.dtype, 'formatted': formatted_column}) -%}
{% endfor %}
{{ return(formatted_columns) }}
{% endmacro %}
Expand Down

0 comments on commit 5d58707

Please sign in to comment.