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

Model-Level Constraints #648

Merged
merged 9 commits into from
Apr 11, 2023
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20230406-104433.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Add support for model-level constraints
time: 2023-04-06T10:44:33.045896-04:00
custom:
Author: peterallenwebb
Issue: "569"
25 changes: 25 additions & 0 deletions dbt/adapters/bigquery/impl.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from dataclasses import dataclass
import threading
from typing import Dict, List, Optional, Any, Set, Union, Type

from dbt.contracts.graph.nodes import ColumnLevelConstraint, ModelLevelConstraint, ConstraintType
from dbt.dataclass_schema import dbtClassMixin, ValidationError

import dbt.deprecations
Expand Down Expand Up @@ -907,3 +909,26 @@ def python_submission_helpers(self) -> Dict[str, Type[PythonJobHelper]]:
"cluster": ClusterDataprocHelper,
"serverless": ServerlessDataProcHelper,
}

@classmethod
def render_column_constraint(cls, constraint: ColumnLevelConstraint) -> str:
if constraint.type == ConstraintType.not_null:
return super().render_column_constraint(constraint)
elif constraint.type == ConstraintType.primary_key:
return "primary key not enforced"
elif constraint.type == ConstraintType.foreign_key:
c = super().render_column_constraint(constraint)
return f"{c} not enforced"
peterallenwebb marked this conversation as resolved.
Show resolved Hide resolved
else:
return ""
VersusFacit marked this conversation as resolved.
Show resolved Hide resolved

@classmethod
def render_model_constraint(cls, constraint: ModelLevelConstraint) -> Optional[str]:
if (
constraint.type == ConstraintType.primary_key
VersusFacit marked this conversation as resolved.
Show resolved Hide resolved
or constraint.type == ConstraintType.foreign_key
):
c = super().render_model_constraint(constraint)
return f"{c} not enforced" if c else None
else:
return None
2 changes: 1 addition & 1 deletion dbt/include/bigquery/macros/adapters.sql
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
{%- set contract_config = config.get('contract') -%}
{%- if contract_config.enforced -%}
{{ get_assert_columns_equivalent(compiled_code) }}
{{ get_columns_spec_ddl() }}
{{ get_table_columns_and_constraints() }}
{%- set compiled_code = get_select_subquery(compiled_code) %}
{% endif %}
{{ partition_by(partition_config) }}
Expand Down
31 changes: 0 additions & 31 deletions dbt/include/bigquery/macros/utils/get_columns_spec_ddl.sql

This file was deleted.

4 changes: 2 additions & 2 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# install latest changes in dbt-core
# TODO: how to automate switching from develop to version branches?
git+https://github.com/dbt-labs/dbt-core.git#egg=dbt-core&subdirectory=core
git+https://github.com/dbt-labs/dbt-core.git#egg=dbt-tests-adapter&subdirectory=tests/adapter
git+https://github.com/dbt-labs/dbt-core.git@paw/ct-1922-model-level-constraints#egg=dbt-core&subdirectory=core
git+https://github.com/dbt-labs/dbt-core.git@paw/ct-1922-model-level-constraints#egg=dbt-tests-adapter&subdirectory=tests/adapter

# if version 1.x or greater -> pin to major version
# if version 0.x -> pin to minor
Expand Down
42 changes: 37 additions & 5 deletions tests/functional/adapter/test_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
BaseConstraintsRuntimeDdlEnforcement,
BaseConstraintsRollback,
BaseIncrementalConstraintsRuntimeDdlEnforcement,
BaseIncrementalConstraintsRollback,
BaseIncrementalConstraintsRollback, BaseModelConstraintsRuntimeEnforcement,
)
from dbt.tests.adapter.constraints.fixtures import (
my_model_sql,
Expand All @@ -18,7 +18,7 @@
my_model_wrong_name_sql,
my_model_view_wrong_name_sql,
my_model_incremental_wrong_name_sql,
model_schema_yml,
model_schema_yml, constrained_model_schema_yml,
)

_expected_sql_bigquery = """
Expand All @@ -43,8 +43,8 @@
# Different on BigQuery:
# - does not support a data type named 'text' (TODO handle this via type translation/aliasing!)
# - raises an explicit error, if you try to set a primary key constraint, because it's not enforced
constraints_yml = model_schema_yml.replace("text", "string").replace("primary key", "")

constraints_yml = model_schema_yml.replace("text", "string")
model_constraints_yml = constrained_model_schema_yml.replace("text", "string")

class BigQueryColumnEqualSetup:
@pytest.fixture
Expand Down Expand Up @@ -167,4 +167,36 @@ def models(self):

@pytest.fixture(scope="class")
def expected_error_messages(self):
return ["Required field id cannot be null"]
return ["Required field id cannot be null"]


class TestBigQueryModelConstraintsRuntimeEnforcement(BaseModelConstraintsRuntimeEnforcement):

@pytest.fixture(scope="class")
def models(self):
return {
"my_model.sql": my_incremental_model_sql,
"constraints_schema.yml": model_constraints_yml,
}

@pytest.fixture(scope="class")
def expected_sql(self):
return """
create or replace table <model_identifier> (
id integer not null,
color string,
date_day string,
primary key (id) not enforced
)
OPTIONS()
as (
select id,
color,
date_day from
(
select 1 as id,
'blue' as color,
'2019-01-01' as date_day
) as model_subq
);
"""