Skip to content

view_update_via_alter: true ignores --full-refresh when view definition is unchanged #1404

@asos-dipeshbhundia

Description

@asos-dipeshbhundia

Describe the bug

When view_update_via_alter: true is configured alongside use_materialization_v2: true, running dbt run --full-refresh on a view whose SQL definition has not changed produces a no-op — the view is silently skipped rather than replaced. The --full-refresh flag is completely ignored by the v2 materialization path.

This is problematic because --full-refresh is an explicit user instruction to force-recreate a relation, for example to pick up permission changes, ownership changes, or to recover from a corrupt/stale view state. With view_update_via_alter: true enabled globally, there is currently no way to force-recreate a view without temporarily disabling the flag.

Steps To Reproduce

  1. Enable use_materialization_v2: true in dbt_project.yml flags
  2. Set view_update_via_alter: true on a view model (globally or per-model)
  3. Run the model to create the view: dbt run -s my_view
  4. Without modifying the model SQL, run with full refresh: dbt run -s my_view --full-refresh
  5. Observe the run completes as a no-op — execute_no_op is called and no SQL is executed against Databricks

Expected behavior

--full-refresh should always force a CREATE OR REPLACE VIEW regardless of whether the view definition has changed. The changeset diff optimisation (which avoids unnecessary ALTER VIEW calls) is a valid performance improvement for normal runs, but must be bypassed entirely when --full-refresh is explicitly requested — consistent with how every other dbt materialization treats the flag.

Screenshots and log output

With debug logging (dbt --debug run -s my_view --full-refresh) the log will show:

MATERIALIZING VIEW
Using no-op for <view_name>

No SQL is executed. The expected output would be Using replace_with_view followed by a CREATE OR REPLACE VIEW statement.

System information

The output of dbt --version:

Core:
  - installed: 1.11.6
  - latest:    1.11.8

Plugins:
  - databricks: 1.11.6
  - spark:      1.10.1

The operating system you're using: Windows 11

The output of python --version: Python 3.12.12

Additional context

Root cause analysis:

The v2 materialization in dbt/include/databricks/macros/materializations/view.sql has the following decision tree when an existing view relation is found:

{% if relation_should_be_altered(existing_relation) %}
  {% set configuration_changes = get_configuration_changes(existing_relation) %}
  {% if configuration_changes and configuration_changes.changes %}
    {% if configuration_changes.requires_full_refresh %}
      {{ replace_with_view(existing_relation, target_relation) }}
    {% else %}
      {{ alter_view(target_relation, configuration_changes.changes) }}
    {% endif %}
  {% else %}
    {{ execute_no_op(target_relation) }}  {# ← BUG: fires even on --full-refresh #}
  {% endif %}
{% else %}
  {{ replace_with_view(existing_relation, target_relation) }}
{% endif %}

relation_should_be_altered() only checks whether the flag is set and the relation is a Unity Catalog view — it never checks should_full_refresh():

{% macro relation_should_be_altered(existing_relation) %}
  {% set update_via_alter = config.get('view_update_via_alter', False) | as_bool %}
  {% if existing_relation.is_view and update_via_alter %}
    {% if existing_relation.is_hive_metastore() %}
      {{ exceptions.raise_compiler_error("...") }}
    {% endif %}
    {{ return(True) }}
  {% endif %}
  {{ return(False) }}
{% endmacro %}

Because relation_should_be_altered() returns True, and no configuration changes are detected (SQL is identical), the code falls into execute_no_op — completely bypassing the --full-refresh signal.

Suggested fix:

relation_should_be_altered() should short-circuit to False when should_full_refresh() is True, which causes the outer branch to fall through to replace_with_view() unconditionally — the correct behaviour:

{% macro relation_should_be_altered(existing_relation) %}
  {% if should_full_refresh() %}
    {{ return(False) }}
  {% endif %}
  {% set update_via_alter = config.get('view_update_via_alter', False) | as_bool %}
  {% if existing_relation.is_view and update_via_alter %}
    {% if existing_relation.is_hive_metastore() %}
      {{ exceptions.raise_compiler_error("...") }}
    {% endif %}
    {{ return(True) }}
  {% endif %}
  {{ return(False) }}
{% endmacro %}

Workaround: Temporarily set view_update_via_alter: false on the specific model before running --full-refresh, then revert.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions