Skip to content

Fix: Handle view materialization alterations #610

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
19 changes: 10 additions & 9 deletions dbt/include/sqlserver/macros/materializations/models/view/view.sql
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,23 @@
-- `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) %}
{% if existing_relation is not none %}
{{ 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) %}
Expand Down
8 changes: 7 additions & 1 deletion dbt/include/sqlserver/macros/relations/views/create.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
Expand Down
58 changes: 57 additions & 1 deletion tests/functional/adapter/dbt/test_basic.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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):
Expand Down