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

dbt Constraints / model contracts #426

Merged
merged 25 commits into from
Feb 17, 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
7 changes: 7 additions & 0 deletions .changes/unreleased/Features-20221220-193731.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Features
body: 'dbt-constraints support for BigQuery as per dbt-core issue #1358'
time: 2022-12-20T19:37:31.982821+01:00
custom:
Author: victoriapm
Issue: "444"
PR: "426"
5 changes: 5 additions & 0 deletions dbt/include/bigquery/macros/adapters.sql
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,13 @@
{{ sql_header if sql_header is not none }}

create or replace table {{ relation }}
{% if config.get('constraints_enabled', False) %}
{{ get_assert_columns_equivalent(sql) }}
{{ get_columns_spec_ddl() }}
{% endif %}
{{ partition_by(partition_config) }}
{{ cluster_by(raw_cluster_by) }}

{{ bigquery_table_options(config, model, temporary) }}
as (
{{ compiled_code }}
Expand Down
16 changes: 16 additions & 0 deletions dbt/include/bigquery/macros/utils/get_columns_spec_ddl.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% macro bigquery__get_columns_spec_ddl() %}
{# loop through user_provided_columns to create DDL with data types and constraints #}
{%- set ns = namespace(at_least_one_check=False) -%}
{%- set user_provided_columns = model['columns'] -%}
(
{% for i in user_provided_columns -%}
{%- set col = user_provided_columns[i] -%}
{% set constraints = col['constraints'] -%}
{%- set ns.at_least_one_check = ns.at_least_one_check or col['constraints_check'] %}
{{ col['name'] }} {{ col['data_type'] }} {% for x in constraints %} {{ x or "" }} {% endfor %} {{ "," if not loop.last }}
{%- endfor %}
)
{%- if ns.at_least_one_check -%}
{{exceptions.warn("We noticed you have `constraints_check` configs, these are NOT compatible with BigQuery and will be ignored")}}
{%- endif %}
{% endmacro %}
1 change: 0 additions & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# 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

black~=22.8.0
bumpversion~=0.6.0
flake8
Expand Down
59 changes: 59 additions & 0 deletions tests/functional/adapter/test_constraints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import pytest
from dbt.tests.util import relation_from_name
from dbt.tests.adapter.constraints.test_constraints import (
BaseConstraintsColumnsEqual,
BaseConstraintsRuntimeEnforcement
)
from dbt.tests.adapter.constraints.fixtures import (
my_model_sql,
my_model_wrong_order_sql,
my_model_wrong_name_sql,
model_schema_yml,
)

_expected_sql_bigquery = """
create or replace table {0} (
id integer not null ,
color string ,
date_day date
)
OPTIONS()
as (
select
1 as id,
'blue' as color,
cast('2019-01-01' as date) as date_day
);
"""

# 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", "")

class TestBigQueryConstraintsColumnsEqual(BaseConstraintsColumnsEqual):
@pytest.fixture(scope="class")
def models(self):
return {
"my_model_wrong_order.sql": my_model_wrong_order_sql,
"my_model_wrong_name.sql": my_model_wrong_name_sql,
"constraints_schema.yml": constraints_yml,
}


class TestBigQueryConstraintsRuntimeEnforcement(BaseConstraintsRuntimeEnforcement):
@pytest.fixture(scope="class")
def models(self):
return {
"my_model.sql": my_model_sql,
"constraints_schema.yml": constraints_yml,
}

@pytest.fixture(scope="class")
def expected_sql(self, project):
relation = relation_from_name(project.adapter, "my_model")
return _expected_sql_bigquery.format(relation)

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