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
- Enable
use_materialization_v2: true in dbt_project.yml flags
- Set
view_update_via_alter: true on a view model (globally or per-model)
- Run the model to create the view:
dbt run -s my_view
- Without modifying the model SQL, run with full refresh:
dbt run -s my_view --full-refresh
- 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.
Describe the bug
When
view_update_via_alter: trueis configured alongsideuse_materialization_v2: true, runningdbt run --full-refreshon a view whose SQL definition has not changed produces a no-op — the view is silently skipped rather than replaced. The--full-refreshflag is completely ignored by the v2 materialization path.This is problematic because
--full-refreshis 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. Withview_update_via_alter: trueenabled globally, there is currently no way to force-recreate a view without temporarily disabling the flag.Steps To Reproduce
use_materialization_v2: trueindbt_project.ymlflagsview_update_via_alter: trueon a view model (globally or per-model)dbt run -s my_viewdbt run -s my_view --full-refreshexecute_no_opis called and no SQL is executed against DatabricksExpected behavior
--full-refreshshould always force aCREATE OR REPLACE VIEWregardless of whether the view definition has changed. The changeset diff optimisation (which avoids unnecessaryALTER VIEWcalls) is a valid performance improvement for normal runs, but must be bypassed entirely when--full-refreshis 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:No SQL is executed. The expected output would be
Using replace_with_viewfollowed by aCREATE OR REPLACE VIEWstatement.System information
The output of
dbt --version:The operating system you're using: Windows 11
The output of
python --version: Python 3.12.12Additional context
Root cause analysis:
The v2 materialization in
dbt/include/databricks/macros/materializations/view.sqlhas the following decision tree when an existing view relation is found:relation_should_be_altered()only checks whether the flag is set and the relation is a Unity Catalog view — it never checksshould_full_refresh():Because
relation_should_be_altered()returnsTrue, and no configuration changes are detected (SQL is identical), the code falls intoexecute_no_op— completely bypassing the--full-refreshsignal.Suggested fix:
relation_should_be_altered()should short-circuit toFalsewhenshould_full_refresh()isTrue, which causes the outer branch to fall through toreplace_with_view()unconditionally — the correct behaviour:Workaround: Temporarily set
view_update_via_alter: falseon the specific model before running--full-refresh, then revert.