diff --git a/dbt/include/sqlserver/macros/materializations/models/view/view.sql b/dbt/include/sqlserver/macros/materializations/models/view/view.sql index 4ae35c9e..5ae2029a 100644 --- a/dbt/include/sqlserver/macros/materializations/models/view/view.sql +++ b/dbt/include/sqlserver/macros/materializations/models/view/view.sql @@ -36,14 +36,8 @@ -- `BEGIN` happens here: {{ run_hooks(pre_hooks, inside_transaction=True) }} - -- build model - {% call statement('main') -%} - {{ get_create_view_as_sql(intermediate_relation, sql) }} - {%- endcall %} - - -- cleanup - -- move the existing view out of the way - {% if existing_relation is not none %} + -- move the existing table if not view out of the way + {% if existing_relation is not none and existing_relation.type != 'view' %} /* Do the equivalent of rename_if_exists. 'existing_relation' could have been dropped since the variable was first set. */ {% set existing_relation = load_cached_relation(existing_relation) %} @@ -51,7 +45,14 @@ {{ adapter.rename_relation(existing_relation, backup_relation) }} {% endif %} {% endif %} - {{ adapter.rename_relation(intermediate_relation, target_relation) }} + + -- build model + {% call statement('main') -%} + {# The underlying macro sqlserver__create_view_as handles CREATE vs ALTER logic #} + {{ get_create_view_as_sql(target_relation, sql) }} + {%- endcall %} + + -- cleanup {% set should_revoke = should_revoke(existing_relation, full_refresh_mode=True) %} {% do apply_grants(target_relation, grant_config, should_revoke=should_revoke) %} diff --git a/dbt/include/sqlserver/macros/relations/views/create.sql b/dbt/include/sqlserver/macros/relations/views/create.sql index ef1ba303..870e8736 100644 --- a/dbt/include/sqlserver/macros/relations/views/create.sql +++ b/dbt/include/sqlserver/macros/relations/views/create.sql @@ -6,8 +6,14 @@ {{ get_assert_columns_equivalent(sql) }} {%- endif %} + {% set existing_relation = load_cached_relation(relation) %} + {% set query %} - create view {{ relation.include(database=False) }} as {{ sql }}; + {% if existing_relation is not none %} + alter view {{ relation.include(database=False) }} as {{ sql }}; + {% else %} + create view {{ relation.include(database=False) }} as {{ sql }}; + {% endif %} {% endset %} {% set tst %} diff --git a/tests/functional/adapter/dbt/test_basic.py b/tests/functional/adapter/dbt/test_basic.py index bdbe38f8..028d078c 100644 --- a/tests/functional/adapter/dbt/test_basic.py +++ b/tests/functional/adapter/dbt/test_basic.py @@ -1,3 +1,5 @@ +import os + import pytest from dbt.tests.adapter.basic.test_adapter_methods import BaseAdapterMethod from dbt.tests.adapter.basic.test_base import BaseSimpleMaterializations @@ -9,10 +11,64 @@ from dbt.tests.adapter.basic.test_singular_tests_ephemeral import BaseSingularTestsEphemeral from dbt.tests.adapter.basic.test_snapshot_check_cols import BaseSnapshotCheckCols from dbt.tests.adapter.basic.test_snapshot_timestamp import BaseSnapshotTimestamp +from dbt.tests.util import run_dbt class TestSimpleMaterializations(BaseSimpleMaterializations): - pass + + def test_existing_view_materialization(self, project, models): + """Test that materializing an existing view works correctly.""" + # Create a temporary model file directly in the project + model_path = os.path.join(project.project_root, "models", "view_model_exists.sql") + + # Write the initial model without the value column + with open(model_path, "w") as f: + f.write( + """ + {{ config(materialized='view') }} + select + 1 as id + {% if var('include_value_column', false) %} + , 2 as value + {% endif %} + """ + ) + + # First run to create the view without the extra column + results = run_dbt(["run", "-m", "view_model_exists"]) + assert len(results) == 1 + + # Generate catalog to get column information + catalog = run_dbt(["docs", "generate"]) + + # Check columns in the catalog + node_id = "model.base.view_model_exists" + assert node_id in catalog.nodes + + # Get columns from the catalog + columns = catalog.nodes[node_id].columns + column_names = [name.lower() for name in columns.keys()] + + # Verify only the id column exists + assert "id" in column_names + assert "value" not in column_names + + # Second run with a variable to include the extra column + results = run_dbt( + ["run", "-m", "view_model_exists", "--vars", '{"include_value_column": true}'] + ) + assert len(results) == 1 + + # Generate catalog again to get updated column information + catalog = run_dbt(["docs", "generate"]) + + # Get updated columns from the catalog + columns = catalog.nodes[node_id].columns + column_names = [name.lower() for name in columns.keys()] + + # Verify both columns exist now + assert "id" in column_names + assert "value" in column_names class TestSingularTests(BaseSingularTests):