diff --git a/CHANGELOG.md b/CHANGELOG.md index dd97cdc1..b5b30d10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ## Features * Add support for materialized views ([#6911](https://github.com/dbt-labs/dbt-core/issues/6911)) + * important note! unlike [dbt's materialized view](https://docs.getdbt.com/docs/build/materializations), [Synapse's materialized view](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-materialized-view-as-select-transact-sql?view=azure-sqldw-latest&context=%2Fazure%2Fsynapse-analytics%2Fcontext%2Fcontext) must be created using aggregation and/or "GROUP BY"! * ~~dbt clone ([#7258](https://github.com/dbt-labs/dbt-core/issues/7258)~~ Synapse does not support CLONE) * Revamp dbt debug ([#7104](https://github.com/dbt-labs/dbt-core/issues/7104)) diff --git a/tests/functional/adapter/test_materialized_views.py b/tests/functional/adapter/test_materialized_views.py index 955ea640..06e86220 100644 --- a/tests/functional/adapter/test_materialized_views.py +++ b/tests/functional/adapter/test_materialized_views.py @@ -39,6 +39,36 @@ """ +def drop_cascade(project, test_model_identifier): + # SYNAPSE HAS NO "DROP SCHEMA...CASCADE" + # so drop all test materializations, to allow drop my_seed + # "my_materialized_view" always created in setup(), so always need to be dropped before my_seed + for identifier in ["my_materialized_view", test_model_identifier]: + project.run_sql( + f""" + if object_id ('"{project.test_schema}"."{identifier}"','V') is not null + begin + drop view "{project.test_schema}"."{identifier}" + end + + if object_id ('"{project.test_schema}"."{identifier}"','U') is not null + begin + drop table "{project.test_schema}"."{identifier}" + end + """ + ) + # then drop object my_seed, to allow drop schema + project.run_sql( + f""" + if object_id ('"{project.test_schema}"."my_seed"','U') is not null + begin + drop table "{project.test_schema}"."my_seed" + end + """ + ) + # finally drop schema can proceed in setup function + + class TestMaterializedViewsBasicSynapse(MaterializedViewBasic): @pytest.fixture(scope="class", autouse=True) def models(self): @@ -48,7 +78,7 @@ def models(self): "my_materialized_view.sql": MY_MATERIALIZED_VIEW, } - @pytest.fixture(scope="class", autouse=True) + @pytest.fixture(scope="function", autouse=True) def setup(self, project, my_materialized_view): run_dbt(["seed"]) run_dbt(["run", "--models", my_materialized_view.identifier, "--full-refresh"]) @@ -60,6 +90,8 @@ def setup(self, project, my_materialized_view): # and then reset them after the test runs set_model_file(project, my_materialized_view, initial_model) + # Synapse no support "if exists" and "cascade" + project.run_sql(f"drop schema {project.test_schema}") def test_materialized_view_create(self, project): # check relation types @@ -67,7 +99,9 @@ def test_materialized_view_create(self, project): # sys.objects has no type "materialized view", it's type "view" "my_materialized_view": "view", } - return check_relation_types(project.adapter, expected) + check_relation_types(project.adapter, expected) + + drop_cascade(project, "my_materialized_view") def test_materialized_view_create_idempotent(self, project, my_materialized_view): # setup creates it once; verify it's there and run once @@ -80,25 +114,29 @@ def test_materialized_view_create_idempotent(self, project, my_materialized_view run_dbt(["run", "--models", my_materialized_view.identifier]) expected = { # sys.objects has no type "materialized view", it's type "view" - "my_materialized_view": "view", + my_materialized_view.identifier: "view", } check_relation_types(project.adapter, expected) + drop_cascade(project, my_materialized_view.identifier) + def test_materialized_view_full_refresh(self, project, my_materialized_view): _, logs = run_dbt_and_capture( ["--debug", "run", "--models", my_materialized_view.identifier, "--full-refresh"] ) expected = { # sys.objects has no type "materialized view", it's type "view" - "my_materialized_view": "view", + my_materialized_view.identifier: "view", } check_relation_types(project.adapter, expected) assert_message_in_logs(f"Applying REPLACE to: {my_materialized_view}", logs) + drop_cascade(project, my_materialized_view.identifier) + def test_materialized_view_replaces_table(self, project, my_table): run_dbt(["run", "--models", my_table.identifier]) expected = { - "my_table": "table", + my_table.identifier: "table", } check_relation_types(project.adapter, expected) @@ -107,14 +145,16 @@ def test_materialized_view_replaces_table(self, project, my_table): run_dbt(["run", "--models", my_table.identifier]) expected = { # sys.objects has no type "materialized view", it's type "view" - "my_table": "view", + my_table.identifier: "view", } check_relation_types(project.adapter, expected) + drop_cascade(project, my_table.identifier) + def test_materialized_view_replaces_view(self, project, my_view): run_dbt(["run", "--models", my_view.identifier]) expected = { - "my_view": "view", + my_view.identifier: "view", } check_relation_types(project.adapter, expected) @@ -123,15 +163,17 @@ def test_materialized_view_replaces_view(self, project, my_view): run_dbt(["run", "--models", my_view.identifier]) expected = { # sys.objects has no type "materialized view", it's type "view" - "my_view": "view", + my_view.identifier: "view", } check_relation_types(project.adapter, expected) + drop_cascade(project, my_view.identifier) + def test_table_replaces_materialized_view(self, project, my_materialized_view): run_dbt(["run", "--models", my_materialized_view.identifier]) expected = { # sys.objects has no type "materialized view", it's type "view" - "my_materialized_view": "view", + my_materialized_view.identifier: "view", } check_relation_types(project.adapter, expected) @@ -139,16 +181,17 @@ def test_table_replaces_materialized_view(self, project, my_materialized_view): run_dbt(["run", "--models", my_materialized_view.identifier]) expected = { - "my_materialized_view": "table", + my_materialized_view.identifier: "table", } check_relation_types(project.adapter, expected) + drop_cascade(project, my_materialized_view.identifier) + def test_view_replaces_materialized_view(self, project, my_materialized_view): - self.swap_table_to_materialized_view(project, my_materialized_view) # hotfix run_dbt(["run", "--models", my_materialized_view.identifier]) expected = { # sys.objects has no type "materialized view", it's type "view" - "my_materialized_view": "view", + my_materialized_view.identifier: "view", } check_relation_types(project.adapter, expected) @@ -156,10 +199,12 @@ def test_view_replaces_materialized_view(self, project, my_materialized_view): run_dbt(["run", "--models", my_materialized_view.identifier]) expected = { - "my_materialized_view": "view", + my_materialized_view.identifier: "view", } check_relation_types(project.adapter, expected) + drop_cascade(project, my_materialized_view.identifier) + @pytest.mark.skip(reason="Synapse materialized view is always updated") def test_materialized_view_only_updates_after_refresh( self, project, my_materialized_view, my_seed