Skip to content

Commit

Permalink
Support Delta constraints (#71)
Browse files Browse the repository at this point in the history
### Description
This PR adds support for [Delta Constraints](https://docs.databricks.com/delta/delta-constraints.html) with dbt table models and incremental models. It supports two types of constraints: model level [Check constraint](https://docs.databricks.com/delta/delta-constraints.html#check-constraint) and column level [not_null](https://docs.databricks.com/delta/delta-constraints.html#not-null-constraint) constraint. 

Users can specify model-level and column-level constraints under the `meta` field of the model config. For example:
```yaml
# schema.yml
models:
  - name: my_model
    meta:
      constraints:
        - name: id_greater_than_zero
          condition: id > 0	
    columns:
      - name: id
      - name: name
        meta:
          constraint: not_null
```
Constraints specified will be created if the `persist_constraints` config is enabled (default: false):
```sql
-- my_model.sql
{{ config(
    materailized='table',
    persist_constraints=True
) }}
...
```

Note, Delta constraints are only available in Databricks Runtime 7.4 and above. So DBR 7.3 LTS won't support this feature.
  • Loading branch information
allisonwang-db committed May 6, 2022
1 parent 2ed551d commit 8db21bc
Show file tree
Hide file tree
Showing 22 changed files with 449 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## dbt-databricks 1.1.0 (Release TBD)

### Features
- Add support for [Delta constraints](https://docs.databricks.com/delta/delta-constraints.html) ([#71](https://github.com/databricks/dbt-databricks/pull/71))

### Under the hood
- Port testing framework changes from [dbt-labs/dbt-spark#299](https://github.com/dbt-labs/dbt-spark/pull/299) and [dbt-labs/dbt-spark#314](https://github.com/dbt-labs/dbt-spark/pull/314) ([#70](https://github.com/databricks/dbt-databricks/pull/70))

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@ your_profile_name:

### Compatibility

The `dbt-databricks` adapter has been tested against `Databricks SQL` and `Databricks runtime releases 7.3 LTS` and later.
The `dbt-databricks` adapter has been tested against `Databricks SQL` and `Databricks runtime releases 9.1 LTS` and later.

56 changes: 56 additions & 0 deletions dbt/include/databricks/macros/adapters.sql
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,59 @@
{% endfor %}
{% endif %}
{% endmacro %}

{# Persist table-level and column-level constraints. #}
{% macro persist_constraints(relation, model) %}
{{ return(adapter.dispatch('persist_constraints', 'dbt')(relation, model)) }}
{% endmacro %}

{% macro databricks__persist_constraints(relation, model) %}
{% if config.get('persist_constraints', False) and config.get('file_format', 'delta') == 'delta' %}
{% do alter_table_add_constraints(relation, model.meta.constraints) %}
{% do alter_column_set_constraints(relation, model.columns) %}
{% endif %}
{% endmacro %}

{% macro alter_table_add_constraints(relation, constraints) %}
{{ return(adapter.dispatch('alter_table_add_constraints', 'dbt')(relation, constraints)) }}
{% endmacro %}

{% macro databricks__alter_table_add_constraints(relation, constraints) %}
{% if constraints is sequence %}
{% for constraint in constraints %}
{% set name = constraint['name'] %}
{% if not name %}
{{ exceptions.raise_compiler_error('Invalid check constraint name: ' ~ name) }}
{% endif %}
{% set condition = constraint['condition'] %}
{% if not condition %}
{{ exceptions.raise_compiler_error('Invalid check constraint condition: ' ~ condition) }}
{% endif %}
{# Skip if the update is incremental. #}
{% if not is_incremental() %}
{% call statement() %}
alter table {{ relation }} add constraint {{ name }} check ({{ condition }});
{% endcall %}
{% endif %}
{% endfor %}
{% endif %}
{% endmacro %}

{% macro alter_column_set_constraints(relation, column_dict) %}
{{ return(adapter.dispatch('alter_column_set_constraints', 'dbt')(relation, column_dict)) }}
{% endmacro %}

{% macro databricks__alter_column_set_constraints(relation, column_dict) %}
{% for column_name in column_dict %}
{% set constraint = column_dict[column_name]['meta']['constraint'] %}
{% if constraint %}
{% if constraint != 'not_null' %}
{{ exceptions.raise_compiler_error('Invalid constraint for column ' ~ column_name ~ '. Only `not_null` is supported.') }}
{% endif %}
{% set quoted_name = adapter.quote(column_name) if column_dict[column_name]['quote'] else column_name %}
{% call statement() %}
alter table {{ relation }} change column {{ quoted_name }} set not null
{% endcall %}
{% endif %}
{% endfor %}
{% endmacro %}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@

{% do persist_docs(target_relation, model) %}

{% do persist_constraints(target_relation, model) %}

{{ run_hooks(post_hooks) }}

{{ return({'relations': [target_relation]}) }}
Expand Down
2 changes: 2 additions & 0 deletions dbt/include/databricks/macros/materializations/snapshot.sql
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@

{% do persist_docs(target_relation, model) %}

{% do persist_constraints(target_relation, model) %}

{{ run_hooks(post_hooks, inside_transaction=True) }}

{{ adapter.commit() }}
Expand Down
2 changes: 2 additions & 0 deletions dbt/include/databricks/macros/materializations/table.sql
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

{% do persist_docs(target_relation, model) %}

{% do persist_constraints(target_relation, model) %}

{{ run_hooks(post_hooks) }}

{{ return({'relations': [target_relation]})}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
insert into {schema}.seed values (0, 'Cathy', '2022-03-01');
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
insert into {schema}.seed values (3, null, '2022-03-01');
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{ config(materialized='table') }}

select * from values
(1, 'Alice', '2022-01-01'),
(2, 'Bob', '2022-02-01'),
(3, 'Cathy', '2022-03-01')
t(id, name, date);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{ config(materialized='table') }}

select * from values
(1, 'Alice', '2022-01-01'),
(2, 'Bob', '2022-02-01')
t(id, name, date);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{ config(materialized='table') }}

select * from values
(1, 'Alice', '2022-01-01'),
(2, 'Bob', '2022-02-01'),
(3, null, '2022-03-01')
t(id, name, date);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{config(materialized='incremental')}}

select * from {{ ref('seed') }}

{% if is_incremental() %}
where date > (select max(date) from {{ this }})
{% endif %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{config(materialized='table')}}

select * from {{ ref('seed') }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{{config(materialized='table')}}

select * from {{ ref('seed') }}

50 changes: 50 additions & 0 deletions tests/integration/persist_constraints/models/schema.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
version: 2

models:
- name: table_model
meta:
constraints:
- name: id_greater_than_zero
condition: id > 0
columns:
- name: id
- name: name
meta:
constraint: not_null
- name: date

- name: incremental_model
meta:
constraints:
- name: id_greater_than_zero
condition: id > 0
columns:
- name: id
- name: name
meta:
constraint: not_null
- name: date

- name: invalid_check_constraint
meta:
constraints:
- name: invalid_constraint
condition:

- name: invalid_column_constraint
columns:
- name: id
meta:
constraint: invalid

- name: table_model_disable_constraints
meta:
constraints:
- name: id_greater_than_zero
condition: id > 0
columns:
- name: id
- name: name
meta:
constraint: not_null
- name: date
3 changes: 3 additions & 0 deletions tests/integration/persist_constraints/models/table_model.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{config(materialized='table')}}

select * from {{ ref('seed') }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{config(materialized='table', persist_constraints=False)}}

select * from {{ ref('seed') }}
3 changes: 3 additions & 0 deletions tests/integration/persist_constraints/seeds/seed.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,date
1,Alice,2022-01-01
2,Bob,2022-02-01
9 changes: 9 additions & 0 deletions tests/integration/persist_constraints/seeds/seed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: 2

seeds:
- name: seed
config:
column_types:
id: int
name: string
date: string
15 changes: 15 additions & 0 deletions tests/integration/persist_constraints/snapshots/snapshot.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% snapshot my_snapshot %}

{{
config(
check_cols=["name", "date"],
unique_key="id",
strategy="check",
target_schema=schema
)

}}

select * from {{ ref('seed') }}

{% endsnapshot %}
12 changes: 12 additions & 0 deletions tests/integration/persist_constraints/snapshots/snapshot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: 2

snapshots:
- name: my_snapshot
meta:
constraints:
- name: id_greater_than_zero
condition: id > 0
columns:
- name: name
meta:
constraint: not_null
Loading

0 comments on commit 8db21bc

Please sign in to comment.