diff --git a/CHANGELOG.md b/CHANGELOG.md index 97c16aa48..8a3a81f48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Validate relation identifier length at creation time and raise a clear error when it exceeds Databricks' 255-character limit ([#1309](https://github.com/databricks/dbt-databricks/issues/1309)) - Fix spurious `MicrobatchConcurrency` behavior-change warning firing on every run regardless of whether the project contained microbatch models ([#1406](https://github.com/databricks/dbt-databricks/issues/1406)) - Fix DBR capability cache being permanently poisoned by a transient version-query failure ([#1398](https://github.com/databricks/dbt-databricks/issues/1398)) +- Fix spurious `use_materialization_v2` behavior-change warning firing on table, view, seed, and incremental materializations ([#1089](https://github.com/databricks/dbt-databricks/issues/1089)) ## dbt-databricks 1.11.7 (Apr 17, 2026) diff --git a/dbt/adapters/databricks/impl.py b/dbt/adapters/databricks/impl.py index 4d6964495..dab1ce041 100644 --- a/dbt/adapters/databricks/impl.py +++ b/dbt/adapters/databricks/impl.py @@ -770,6 +770,20 @@ def _get_columns_for_catalog( # type: ignore[override] as_dict["column_type"] = as_dict.pop("dtype") yield as_dict + @available + def get_behavior_flag_no_warn(self, behavior_flag_name: str) -> bool: + """Get the value of a behavior flag without triggering a warning. + + dbt logs a warning the first time a behavior flag with a False value is accessed. Use + this method to access the value of a behavior flag without triggering a warning. + + Decorated with `@available` so that Jinja macros can call it via the + `RuntimeDatabaseWrapper` exposed in the dbt runtime context. + """ + # As intended - This method will error out if the behavior flag is missing. + behavior_flag = getattr(self.behavior, behavior_flag_name) + return behavior_flag.no_warn + def add_query( self, sql: str, diff --git a/dbt/include/databricks/macros/materializations/incremental/incremental.sql b/dbt/include/databricks/macros/materializations/incremental/incremental.sql index ac717ae45..934adbab8 100644 --- a/dbt/include/databricks/macros/materializations/incremental/incremental.sql +++ b/dbt/include/databricks/macros/materializations/incremental/incremental.sql @@ -16,7 +16,7 @@ {% set is_replaceable_format = is_delta or is_iceberg %} {% set compiled_code = adapter.clean_sql(model['compiled_code']) %} - {% if adapter.behavior.use_materialization_v2 %} + {% if adapter.get_behavior_flag_no_warn('use_materialization_v2') %} {{ log("USING V2 MATERIALIZATION") }} {#-- Set vars --#} {% set safe_create = config.get('use_safer_relation_operations', False) | as_bool %} diff --git a/dbt/include/databricks/macros/materializations/seeds/seeds.sql b/dbt/include/databricks/macros/materializations/seeds/seeds.sql index b6683204b..90e4b4059 100644 --- a/dbt/include/databricks/macros/materializations/seeds/seeds.sql +++ b/dbt/include/databricks/macros/materializations/seeds/seeds.sql @@ -1,7 +1,7 @@ {% materialization seed, adapter='databricks' %} {% set target_relation = this.incorporate(type='table') %} - {% if adapter.behavior.use_materialization_v2 %} + {% if adapter.get_behavior_flag_no_warn('use_materialization_v2') %} {{ create_seed_v2(target_relation) }} {% else %} {{ create_seed_v1(target_relation) }} diff --git a/dbt/include/databricks/macros/materializations/table.sql b/dbt/include/databricks/macros/materializations/table.sql index b749c5148..8f360cdae 100644 --- a/dbt/include/databricks/macros/materializations/table.sql +++ b/dbt/include/databricks/macros/materializations/table.sql @@ -10,7 +10,7 @@ {% set target_relation = this.incorporate(type='table') %} {% set compiled_code = adapter.clean_sql(compiled_code) %} - {% if adapter.behavior.use_materialization_v2 %} + {% if adapter.get_behavior_flag_no_warn('use_materialization_v2') %} {% set intermediate_relation = make_intermediate_relation(target_relation) %} {% set staging_relation = make_staging_relation(target_relation) %} diff --git a/dbt/include/databricks/macros/materializations/view.sql b/dbt/include/databricks/macros/materializations/view.sql index 8c824e5de..a2b443d94 100644 --- a/dbt/include/databricks/macros/materializations/view.sql +++ b/dbt/include/databricks/macros/materializations/view.sql @@ -6,7 +6,7 @@ {% set tags = config.get('databricks_tags') %} {% set sql = adapter.clean_sql(sql) %} - {% if adapter.behavior.use_materialization_v2 %} + {% if adapter.get_behavior_flag_no_warn('use_materialization_v2') %} {{ run_pre_hooks() }} {% if existing_relation %} {% if relation_should_be_altered(existing_relation) %} diff --git a/tests/unit/test_adapter.py b/tests/unit/test_adapter.py index 23ce774d8..44673f412 100644 --- a/tests/unit/test_adapter.py +++ b/tests/unit/test_adapter.py @@ -7,6 +7,7 @@ import pytest from agate import Row from dbt.config import RuntimeConfig +from dbt_common.events.event_manager_client import add_callback_to_manager from dbt_common.exceptions import DbtConfigError, DbtDatabaseError, DbtValidationError from dbt.adapters.databricks import DatabricksAdapter, __version__, constants @@ -1351,3 +1352,37 @@ def test_is_uniform_with_invalid_materialization_error( DbtConfigError, match="When table_format is 'iceberg', materialized must be" ): adapter.is_uniform(mock_config) + + +class TestMaterializationV2BehaviorFlag(DatabricksAdapterBase): + """The `use_materialization_v2` deprecation warning must not fire on every dbt run.""" + + @pytest.fixture + def adapter(self): + with patch("dbt.adapters.databricks.connections.DatabricksConnectionManager"): + return DatabricksAdapter(self._get_config(), get_context("spawn")) + + @pytest.mark.parametrize("flag_value", [False, True]) + def test_get_behavior_flag_no_warn_does_not_fire_event(self, adapter, flag_value): + """The helper must return the flag value without firing + BehaviorChangeEvent for `use_materialization_v2`, regardless of value.""" + adapter.behavior.use_materialization_v2.setting = flag_value + + caught = [] + + def record(event_msg): + if ( + event_msg.info.name == "BehaviorChangeEvent" + and event_msg.data.flag_name == "use_materialization_v2" + ): + caught.append(event_msg) + + add_callback_to_manager(record) + + result = adapter.get_behavior_flag_no_warn("use_materialization_v2") + + assert result == flag_value + assert caught == [], ( + "get_behavior_flag_no_warn must not fire BehaviorChangeEvent for " + f"use_materialization_v2 (fired {len(caught)} times)" + )