-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
dbt test --store-failures #3316
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,46 @@ | ||
{%- materialization test, default -%} | ||
|
||
{% set relations = [] %} | ||
|
||
{% if should_store_failures() %} | ||
|
||
{% set identifier = model['alias'] %} | ||
{% set old_relation = adapter.get_relation(database=database, schema=schema, identifier=identifier) %} | ||
{% set target_relation = api.Relation.create( | ||
identifier=identifier, schema=schema, database=database, type='table') -%} %} | ||
|
||
{% if old_relation %} | ||
{% do adapter.drop_relation(old_relation) %} | ||
{% endif %} | ||
|
||
{% call statement(auto_begin=True) %} | ||
{{ create_table_as(False, target_relation, sql) }} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is 🔥 |
||
{% endcall %} | ||
|
||
{% do relations.append(target_relation) %} | ||
|
||
{% set main_sql %} | ||
select count(*) as validation_errors | ||
from {{ target_relation }} | ||
{% endset %} | ||
|
||
{{ adapter.commit() }} | ||
|
||
{% else %} | ||
|
||
{% set main_sql %} | ||
select count(*) as validation_errors | ||
from ( | ||
{{ sql }} | ||
) _dbt_internal_test | ||
{% endset %} | ||
|
||
{% endif %} | ||
|
||
{% call statement('main', fetch_result=True) -%} | ||
select count(*) as validation_errors | ||
from ( | ||
{{ sql }} | ||
) _dbt_internal_test | ||
{{ main_sql }} | ||
{%- endcall %} | ||
|
||
{{ return({'relations': relations}) }} | ||
|
||
{%- endmaterialization -%} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,16 +41,21 @@ def get_nice_schema_test_name( | |
clean_flat_args = [re.sub('[^0-9a-zA-Z_]+', '_', arg) for arg in flat_args] | ||
unique = "__".join(clean_flat_args) | ||
|
||
cutoff = 32 | ||
if len(unique) <= cutoff: | ||
label = unique | ||
else: | ||
label = hashlib.md5(unique.encode('utf-8')).hexdigest() | ||
# for the file path + alias, the name must be <64 characters | ||
# if the full name is too long, include the first 30 identifying chars plus | ||
# a 32-character hash of the full contents | ||
|
||
test_identifier = '{}_{}'.format(test_type, test_name) | ||
full_name = '{}_{}'.format(test_identifier, unique) | ||
|
||
filename = '{}_{}_{}'.format(test_type, test_name, label) | ||
name = '{}_{}_{}'.format(test_type, test_name, unique) | ||
if len(full_name) >= 64: | ||
test_trunc_identifier = test_identifier[:30] | ||
label = hashlib.md5(full_name.encode('utf-8')).hexdigest() | ||
short_name = '{}_{}'.format(test_trunc_identifier, label) | ||
else: | ||
short_name = full_name | ||
|
||
return filename, name | ||
return short_name, full_name | ||
|
||
|
||
@dataclass | ||
|
@@ -185,7 +190,7 @@ class TestBuilder(Generic[Testable]): | |
r'(?P<test_name>([a-zA-Z_][0-9a-zA-Z_]*))' | ||
) | ||
# kwargs representing test configs | ||
MODIFIER_ARGS = ('severity', 'tags', 'enabled') | ||
MODIFIER_ARGS = ('severity', 'tags', 'enabled', 'store_failures') | ||
|
||
def __init__( | ||
self, | ||
|
@@ -231,6 +236,10 @@ def __init__( | |
self.compiled_name: str = compiled_name | ||
self.fqn_name: str = fqn_name | ||
|
||
# use hashed name as alias if too long | ||
if compiled_name != fqn_name: | ||
self.modifiers['alias'] = compiled_name | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. really clever & really cooll! |
||
|
||
def _bad_type(self) -> TypeError: | ||
return TypeError('invalid target type "{}"'.format(type(self.target))) | ||
|
||
|
@@ -271,13 +280,19 @@ def extract_test_args(test, name=None) -> Tuple[str, Dict[str, Any]]: | |
def enabled(self) -> Optional[bool]: | ||
return self.modifiers.get('enabled') | ||
|
||
def alias(self) -> Optional[str]: | ||
return self.modifiers.get('alias') | ||
|
||
def severity(self) -> Optional[str]: | ||
sev = self.modifiers.get('severity') | ||
if sev: | ||
return sev.upper() | ||
else: | ||
return None | ||
|
||
def store_failures(self) -> Optional[bool]: | ||
return self.modifiers.get('store_failures') | ||
|
||
def tags(self) -> List[str]: | ||
tags = self.modifiers.get('tags', []) | ||
if isinstance(tags, str): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -455,7 +455,7 @@ def get_model_schemas( | |
for node in self.manifest.nodes.values(): | ||
if node.unique_id not in selected_uids: | ||
continue | ||
if node.is_refable and not node.is_ephemeral: | ||
if node.is_relational and not node.is_ephemeral: | ||
relation = adapter.Relation.create_from(self.config, node) | ||
result.add(relation.without_identifier()) | ||
|
||
|
@@ -525,7 +525,6 @@ def create_schema(relation: BaseRelation) -> None: | |
db_schema = (db_lower, schema.lower()) | ||
if db_schema not in existing_schemas_lowered: | ||
existing_schemas_lowered.add(db_schema) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a reason why this disappeared? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nope :) thanks for catching it! |
||
|
||
fut = tpe.submit_connected( | ||
adapter, f'create_{info.database or ""}_{info.schema}', | ||
create_schema, info | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this definitely feels like the most expedient approach, but by making this a global flag, we lose the ability to specify something like this as a model-level config. Do you think there's a path to implementing this more like the
full_refresh
config?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
definitely!