From 3b88548e939fc0209b84c18c6439efe05ad5255a Mon Sep 17 00:00:00 2001 From: Noritaka Sekiyama Date: Tue, 8 Sep 2026 16:58:33 +0900 Subject: [PATCH 1/6] fix: replace instead of drop on managed Iceberg full refresh A full refresh of an incremental model on a Unity Catalog managed Iceberg table dropped the table and then ran the CTAS, leaving it absent for the duration of the rebuild and losing its history. The replaceability check keyed its Iceberg arm off `file_format`, which an Iceberg model never sets to iceberg -- `iceberg_table_properties` raises for anything but delta, and `iceberg` is not an accepted `file_format` at all, so that arm was unreachable. The Delta arm then failed too, because the existing table reports `Provider = iceberg`. Key the arm off `table_format` plus the `use_managed_iceberg` flag instead, matching the condition `file_format_clause` uses to emit `using iceberg`. Extracted into `format_allows_create_or_replace` so both the V1 and V2 paths share it and it can be tested without a warehouse. Resolves #1662 Co-authored-by: Isaac --- .../incremental/incremental.sql | 4 +- .../incremental/replaceable_format.sql | 20 ++++ .../incremental/test_replaceable_format.py | 103 ++++++++++++++++++ 3 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 dbt/include/databricks/macros/materializations/incremental/replaceable_format.sql create mode 100644 tests/unit/macros/materializations/incremental/test_replaceable_format.py diff --git a/dbt/include/databricks/macros/materializations/incremental/incremental.sql b/dbt/include/databricks/macros/materializations/incremental/incremental.sql index e79859822..3f8527bdd 100644 --- a/dbt/include/databricks/macros/materializations/incremental/incremental.sql +++ b/dbt/include/databricks/macros/materializations/incremental/incremental.sql @@ -11,9 +11,7 @@ {% set partition_by = config.get('partition_by') %} {% set language = model['language'] %} {% set on_schema_change = incremental_validate_on_schema_change(config.get('on_schema_change'), default='ignore') %} - {% set is_delta = (catalog_relation.file_format == 'delta' and existing_relation.is_delta) %} - {% set is_iceberg = (catalog_relation.file_format == 'iceberg' and existing_relation.is_iceberg) %} - {% set is_replaceable_format = is_delta or is_iceberg %} + {% set is_replaceable_format = format_allows_create_or_replace(catalog_relation, existing_relation) %} {% set compiled_code = adapter.clean_sql(model['compiled_code']) %} {% if adapter.get_behavior_flag_no_warn('use_materialization_v2') %} diff --git a/dbt/include/databricks/macros/materializations/incremental/replaceable_format.sql b/dbt/include/databricks/macros/materializations/incremental/replaceable_format.sql new file mode 100644 index 000000000..46f0f47e4 --- /dev/null +++ b/dbt/include/databricks/macros/materializations/incremental/replaceable_format.sql @@ -0,0 +1,20 @@ +{#-- True when `create or replace table` can stand in for drop-then-create on a full refresh. + + Managed Iceberg needs its own arm rather than reusing `file_format`: an Iceberg model keeps + `file_format` at delta (`iceberg_table_properties` raises for anything else) and `iceberg` is + not an accepted `file_format` at all, so the `file_format == 'iceberg'` test this replaced + could never be true. The target is Iceberg exactly when `table_format` is iceberg and the + behavior flag is on -- the same condition `file_format_clause` uses to emit `using iceberg`. + The flag is read without warning because this runs for every incremental model, including + projects that never opt in (issue #1266). --#} +{% macro format_allows_create_or_replace(catalog_relation, existing_relation) %} + {%- set target_is_managed_iceberg = ( + catalog_relation.table_format == 'iceberg' + and adapter.get_behavior_flag_no_warn('use_managed_iceberg') + ) -%} + {%- set replaceable = ( + (catalog_relation.file_format == 'delta' and existing_relation.is_delta is true) + or (target_is_managed_iceberg and existing_relation.is_iceberg is true) + ) -%} + {{ return(replaceable) }} +{% endmacro %} diff --git a/tests/unit/macros/materializations/incremental/test_replaceable_format.py b/tests/unit/macros/materializations/incremental/test_replaceable_format.py new file mode 100644 index 000000000..0f678d593 --- /dev/null +++ b/tests/unit/macros/materializations/incremental/test_replaceable_format.py @@ -0,0 +1,103 @@ +from unittest.mock import Mock + +import pytest + +from tests.unit.macros.base import MacroTestBase + + +class TestFormatAllowsCreateOrReplace(MacroTestBase): + """The predicate that decides whether a full refresh can use `create or replace table` + instead of dropping the existing relation first (issue #1662).""" + + @pytest.fixture(scope="class") + def template_name(self) -> str: + return "replaceable_format.sql" + + @pytest.fixture(scope="class") + def macro_folders_to_load(self) -> list: + return ["macros/materializations/incremental"] + + def _catalog_relation(self, table_format="default", file_format="delta"): + catalog_relation = Mock() + catalog_relation.table_format = table_format + catalog_relation.file_format = file_format + return catalog_relation + + def _existing_relation(self, is_delta=False, is_iceberg=False): + existing_relation = Mock() + existing_relation.is_delta = is_delta + existing_relation.is_iceberg = is_iceberg + return existing_relation + + def run_predicate(self, template_bundle, catalog_relation, existing_relation, managed_iceberg): + template_bundle.context["adapter"].get_behavior_flag_no_warn = Mock( + side_effect=lambda name: managed_iceberg if name == "use_managed_iceberg" else False + ) + return self.run_macro_raw( + template_bundle.template, + "format_allows_create_or_replace", + catalog_relation, + existing_relation, + ).strip() + + def test_delta_target_on_delta_relation(self, template_bundle): + result = self.run_predicate( + template_bundle, + self._catalog_relation(), + self._existing_relation(is_delta=True), + managed_iceberg=False, + ) + assert result == "True" + + def test_delta_target_on_iceberg_relation(self, template_bundle): + """Provider changed under the model, so the table has to be dropped.""" + result = self.run_predicate( + template_bundle, + self._catalog_relation(), + self._existing_relation(is_iceberg=True), + managed_iceberg=False, + ) + assert result == "False" + + def test_managed_iceberg_target_on_iceberg_relation(self, template_bundle): + """The case from #1662: an Iceberg model keeps `file_format` at delta, so keying the + Iceberg arm off `file_format` never matched and the full refresh dropped the table.""" + result = self.run_predicate( + template_bundle, + self._catalog_relation(table_format="iceberg"), + self._existing_relation(is_iceberg=True), + managed_iceberg=True, + ) + assert result == "True" + + def test_uniform_target_on_delta_relation(self, template_bundle): + """`table_format: iceberg` without the behavior flag writes a Delta table with UniForm + properties, so the relation stays Delta and remains replaceable.""" + result = self.run_predicate( + template_bundle, + self._catalog_relation(table_format="iceberg"), + self._existing_relation(is_delta=True), + managed_iceberg=False, + ) + assert result == "True" + + def test_managed_iceberg_target_on_delta_relation(self, template_bundle): + """A project that has just switched the flag on still has a Delta table. The Delta arm + already covered this before #1662 and is left alone, so the full refresh keeps replacing + rather than dropping.""" + result = self.run_predicate( + template_bundle, + self._catalog_relation(table_format="iceberg"), + self._existing_relation(is_delta=True), + managed_iceberg=True, + ) + assert result == "True" + + def test_non_delta_file_format(self, template_bundle): + result = self.run_predicate( + template_bundle, + self._catalog_relation(file_format="parquet"), + self._existing_relation(is_delta=True), + managed_iceberg=False, + ) + assert result == "False" From 349485c24a6e58a9336a98ed46e62916c62c5861 Mon Sep 17 00:00:00 2001 From: Noritaka Sekiyama Date: Tue, 8 Sep 2026 22:57:29 +0900 Subject: [PATCH 2/6] test: cover managed Iceberg full refresh replacing in place Asserts on the table's history rather than its id or creation time: both of those change on `create or replace` as well, so only the first history entry surviving distinguishes a replace from a drop and recreate. Co-authored-by: Isaac --- .../adapter/iceberg/test_iceberg_support.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/functional/adapter/iceberg/test_iceberg_support.py b/tests/functional/adapter/iceberg/test_iceberg_support.py index 5bfb77b9f..b4495231e 100644 --- a/tests/functional/adapter/iceberg/test_iceberg_support.py +++ b/tests/functional/adapter/iceberg/test_iceberg_support.py @@ -26,6 +26,19 @@ def get_tblproperty(project, identifier, key): return values[0] if values else None +def get_version_zero_timestamp(project, identifier): + """Timestamp of the table's first history entry. A `create or replace` keeps it; dropping + and recreating the table starts a new history, so the value changes.""" + rows = project.run_sql( + f"describe history {{database}}.{{schema}}.{identifier}", + fetch="all", + ) + for row in rows: + if int(row[0]) == 0: + return str(row[1]) + return None + + @pytest.mark.skip_profile("databricks_cluster") class TestIcebergTables: @pytest.fixture(scope="class") @@ -172,3 +185,24 @@ def test_iceberg_incremental_merge(self, project): assert result[0][1] == "updated" # Updated via merge assert result[1][0] == 2 assert result[1][1] == "new" # New row + + +@pytest.mark.skip_profile("databricks_cluster") +class TestManagedIcebergFullRefresh(ManagedIcebergMixin): + """A full refresh must replace a managed Iceberg table in place rather than dropping it + first, so the table stays queryable for the whole rebuild (issue #1662).""" + + @pytest.fixture(scope="class") + def models(self): + return {"iceberg_full_refresh.sql": fixtures.incremental_iceberg_base} + + def test_full_refresh_keeps_the_table(self, project): + util.run_dbt() + created = get_version_zero_timestamp(project, "iceberg_full_refresh") + assert created is not None, "expected history on the managed Iceberg table" + + util.run_dbt(["run", "--full-refresh"]) + + assert get_version_zero_timestamp(project, "iceberg_full_refresh") == created, ( + "history restarted, so the full refresh dropped and recreated the table" + ) From f488453b7c97e599be7e63538bd717b215606223 Mon Sep 17 00:00:00 2001 From: Noritaka Sekiyama Date: Wed, 9 Sep 2026 05:01:10 +0900 Subject: [PATCH 3/6] docs: changelog entry for the managed Iceberg full refresh fix Co-authored-by: Isaac --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 248457155..48aaaef86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## dbt-databricks 1.12.6 (TBD) +### Fixes + +- Use `create or replace table` instead of dropping the table first when a full refresh rebuilds an incremental model on a Unity Catalog managed Iceberg table ([#1669](https://github.com/databricks/dbt-databricks/pull/1669) resolves [#1662](https://github.com/databricks/dbt-databricks/issues/1662)) + ### Under the Hood - Emit only changed `databricks_tags` keys in `ALTER … SET TAGS` ([#1667](https://github.com/databricks/dbt-databricks/pull/1667)) From 1024e3150742cd2a0d83ac6d382f0432d1e6c221 Mon Sep 17 00:00:00 2001 From: Noritaka Sekiyama Date: Sat, 12 Sep 2026 09:39:53 +0900 Subject: [PATCH 4/6] fix: drop rather than replace when the target's provider differs The Delta arm matched a managed-Iceberg target over a legacy Delta table, because `file_format` reads delta for an Iceberg model too. `create or replace` cannot change a table's provider -- Databricks rejects it with `MANAGED_ICEBERG_OPERATION_NOT_SUPPORTED` and leaves the table alone -- so a project switching `use_managed_iceberg` on failed on its first full refresh until someone dropped the table by hand. Make the arms mutually exclusive, so a provider change drops and recreates. Reported by @cemsbr on #1674, who probed the cross-provider REPLACE. Co-authored-by: Isaac --- .../incremental/replaceable_format.sql | 19 ++++++++++---- .../adapter/iceberg/test_iceberg_support.py | 26 +++++++++++++++++++ .../incremental/test_replaceable_format.py | 9 ++++--- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/dbt/include/databricks/macros/materializations/incremental/replaceable_format.sql b/dbt/include/databricks/macros/materializations/incremental/replaceable_format.sql index 46f0f47e4..23342bd2d 100644 --- a/dbt/include/databricks/macros/materializations/incremental/replaceable_format.sql +++ b/dbt/include/databricks/macros/materializations/incremental/replaceable_format.sql @@ -6,15 +6,24 @@ could never be true. The target is Iceberg exactly when `table_format` is iceberg and the behavior flag is on -- the same condition `file_format_clause` uses to emit `using iceberg`. The flag is read without warning because this runs for every incremental model, including - projects that never opt in (issue #1266). --#} + projects that never opt in (issue #1266). + + The two arms are mutually exclusive because `create or replace` cannot change a table's + provider: Databricks rejects it with `MANAGED_ICEBERG_OPERATION_NOT_SUPPORTED` and leaves the + table as it was. A managed-Iceberg target over a legacy Delta table -- a project that has just + switched the flag on -- must therefore drop and recreate, even though `file_format` still reads + delta for it. --#} {% macro format_allows_create_or_replace(catalog_relation, existing_relation) %} {%- set target_is_managed_iceberg = ( catalog_relation.table_format == 'iceberg' and adapter.get_behavior_flag_no_warn('use_managed_iceberg') ) -%} - {%- set replaceable = ( - (catalog_relation.file_format == 'delta' and existing_relation.is_delta is true) - or (target_is_managed_iceberg and existing_relation.is_iceberg is true) - ) -%} + {%- if target_is_managed_iceberg -%} + {%- set replaceable = existing_relation.is_iceberg is true -%} + {%- else -%} + {%- set replaceable = ( + catalog_relation.file_format == 'delta' and existing_relation.is_delta is true + ) -%} + {%- endif -%} {{ return(replaceable) }} {% endmacro %} diff --git a/tests/functional/adapter/iceberg/test_iceberg_support.py b/tests/functional/adapter/iceberg/test_iceberg_support.py index b4495231e..2bb0ab720 100644 --- a/tests/functional/adapter/iceberg/test_iceberg_support.py +++ b/tests/functional/adapter/iceberg/test_iceberg_support.py @@ -206,3 +206,29 @@ def test_full_refresh_keeps_the_table(self, project): assert get_version_zero_timestamp(project, "iceberg_full_refresh") == created, ( "history restarted, so the full refresh dropped and recreated the table" ) + + +@pytest.mark.skip_profile("databricks_cluster") +class TestManagedIcebergOverExistingDelta(ManagedIcebergMixin): + """Switching `use_managed_iceberg` on over tables a project already has as Delta must drop and + recreate them. `create or replace` cannot change a table's provider, so replacing here fails + with MANAGED_ICEBERG_OPERATION_NOT_SUPPORTED and leaves the table Delta (issue #1662).""" + + @pytest.fixture(scope="class") + def models(self): + return {"iceberg_over_delta.sql": fixtures.incremental_iceberg_base} + + def test_full_refresh_converts_the_delta_table(self, project): + project.run_sql( + "create or replace table {database}.{schema}.iceberg_over_delta using delta " + "as select 1 as id, 'initial' as status" + ) + assert get_provider(project, "iceberg_over_delta") == "delta" + + util.run_dbt(["run", "--full-refresh"]) + + assert get_provider(project, "iceberg_over_delta") == "iceberg" + rows = project.run_sql( + "select id, status from {database}.{schema}.iceberg_over_delta", fetch="all" + ) + assert len(rows) == 1 diff --git a/tests/unit/macros/materializations/incremental/test_replaceable_format.py b/tests/unit/macros/materializations/incremental/test_replaceable_format.py index 0f678d593..0b6b70efa 100644 --- a/tests/unit/macros/materializations/incremental/test_replaceable_format.py +++ b/tests/unit/macros/materializations/incremental/test_replaceable_format.py @@ -82,16 +82,17 @@ def test_uniform_target_on_delta_relation(self, template_bundle): assert result == "True" def test_managed_iceberg_target_on_delta_relation(self, template_bundle): - """A project that has just switched the flag on still has a Delta table. The Delta arm - already covered this before #1662 and is left alone, so the full refresh keeps replacing - rather than dropping.""" + """A project that has just switched the flag on still has a Delta table. `create or + replace` cannot change a table's provider -- Databricks rejects it with + MANAGED_ICEBERG_OPERATION_NOT_SUPPORTED -- so this has to drop and recreate even though + `file_format` still reads delta for a managed Iceberg model.""" result = self.run_predicate( template_bundle, self._catalog_relation(table_format="iceberg"), self._existing_relation(is_delta=True), managed_iceberg=True, ) - assert result == "True" + assert result == "False" def test_non_delta_file_format(self, template_bundle): result = self.run_predicate( From d8fd4af2037acbac0114f65be90b5e326e63c0dd Mon Sep 17 00:00:00 2001 From: "Carlos Eduardo M. Santos" Date: Wed, 9 Sep 2026 15:22:49 -0300 Subject: [PATCH 5/6] fix: replace instead of drop when rebuilding a managed Iceberg table The `table` materialization gated `create or replace` on `adapter.resolve_file_format(config) in ('delta', 'iceberg')`. With the `use_managed_iceberg` behavior flag on, `resolve_file_format` returns `parquet` for `table_format: iceberg`, so every rebuild of a Unity Catalog managed Iceberg table dropped it first and then ran a `create or replace table ... using iceberg` CTAS that would have been atomic on its own. The table was unavailable for the whole CTAS on every run and its history restarted each time. Reuse `format_allows_create_or_replace` (from the incremental full-refresh fix for #1662) in both the v1 and v2 branches of `table.sql`. Shallow clones and non-table relations are still dropped, and Delta targets keep their previous behavior. Resolves #1662 Signed-off-by: Carlos Eduardo M. Santos --- CHANGELOG.md | 1 + .../macros/materializations/table.sql | 5 +++-- .../adapter/iceberg/test_iceberg_support.py | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48aaaef86..707c60d07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Fixes - Use `create or replace table` instead of dropping the table first when a full refresh rebuilds an incremental model on a Unity Catalog managed Iceberg table ([#1669](https://github.com/databricks/dbt-databricks/pull/1669) resolves [#1662](https://github.com/databricks/dbt-databricks/issues/1662)) +- Use `create or replace table` instead of dropping the table first when the `table` materialization rebuilds a Unity Catalog managed Iceberg table ([#1674](https://github.com/databricks/dbt-databricks/pull/1674) resolves [#1662](https://github.com/databricks/dbt-databricks/issues/1662)) ### Under the Hood diff --git a/dbt/include/databricks/macros/materializations/table.sql b/dbt/include/databricks/macros/materializations/table.sql index eee612c36..47bdf6e87 100644 --- a/dbt/include/databricks/macros/materializations/table.sql +++ b/dbt/include/databricks/macros/materializations/table.sql @@ -9,6 +9,7 @@ {% set existing_relation = adapter.get_relation(database=database, schema=schema, identifier=identifier, needs_information=True) %} {% set target_relation = this.incorporate(type='table') %} {% set compiled_code = adapter.clean_sql(compiled_code) %} + {%- set catalog_relation = adapter.build_catalog_relation(config.model) -%} {% if adapter.get_behavior_flag_no_warn('use_materialization_v2') %} {% set intermediate_relation = make_intermediate_relation(target_relation) %} @@ -25,7 +26,7 @@ {% if safe_create and existing_relation.can_be_renamed %} {{ safe_relation_replace(existing_relation, staging_relation, intermediate_relation, compiled_code) }} {% else %} - {% if existing_relation and (existing_relation.is_shallow_clone or existing_relation.type != 'table' or not (existing_relation.can_be_replaced and adapter.resolve_file_format(config) in ('delta', 'iceberg'))) -%} + {% if existing_relation and (existing_relation.is_shallow_clone or existing_relation.type != 'table' or not (existing_relation.can_be_replaced and format_allows_create_or_replace(catalog_relation, existing_relation))) -%} {{ adapter.drop_relation(existing_relation) }} {%- endif %} {{ create_table_at(target_relation, intermediate_relation, compiled_code) }} @@ -46,7 +47,7 @@ -- setup: if the target relation already exists, drop it -- in case if the existing and future table is delta or iceberg, we want to do a -- create or replace table instead of dropping, so we don't have the table unavailable - {% if existing_relation and (existing_relation.is_shallow_clone or existing_relation.type != 'table' or not (existing_relation.can_be_replaced and adapter.resolve_file_format(config) in ('delta', 'iceberg'))) -%} + {% if existing_relation and (existing_relation.is_shallow_clone or existing_relation.type != 'table' or not (existing_relation.can_be_replaced and format_allows_create_or_replace(catalog_relation, existing_relation))) -%} {{ adapter.drop_relation(existing_relation) }} {%- endif %} diff --git a/tests/functional/adapter/iceberg/test_iceberg_support.py b/tests/functional/adapter/iceberg/test_iceberg_support.py index 2bb0ab720..1b5eb1616 100644 --- a/tests/functional/adapter/iceberg/test_iceberg_support.py +++ b/tests/functional/adapter/iceberg/test_iceberg_support.py @@ -232,3 +232,23 @@ def test_full_refresh_converts_the_delta_table(self, project): "select id, status from {database}.{schema}.iceberg_over_delta", fetch="all" ) assert len(rows) == 1 + + +class TestManagedIcebergTableRebuild(ManagedIcebergMixin): + """Rebuilding a `table` model must replace a managed Iceberg table in place rather than + dropping it first, so the table stays queryable for the whole rebuild (issue #1662).""" + + @pytest.fixture(scope="class") + def models(self): + return {"iceberg_table_rebuild.sql": fixtures.basic_iceberg_swap} + + def test_rebuild_keeps_the_table(self, project): + util.run_dbt() + created = get_version_zero_timestamp(project, "iceberg_table_rebuild") + assert created is not None, "expected history on the managed Iceberg table" + + util.run_dbt() + + assert get_version_zero_timestamp(project, "iceberg_table_rebuild") == created, ( + "history restarted, so the rebuild dropped and recreated the table" + ) From 29f63ff33e111d434570db29c39018e23794d8ba Mon Sep 17 00:00:00 2001 From: "Carlos Eduardo M. Santos" Date: Mon, 14 Sep 2026 18:41:44 -0300 Subject: [PATCH 6/6] test: cover a table model switching to managed Iceberg over an existing Delta table Counterpart of TestManagedIcebergOverExistingDelta for the `table` materialization: with the provider-aware predicate, turning `use_managed_iceberg` on over a table the project already has as Delta drops and recreates it on the next run instead of failing with MANAGED_ICEBERG_OPERATION_NOT_SUPPORTED. Signed-off-by: Carlos Eduardo M. Santos --- .../adapter/iceberg/test_iceberg_support.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/functional/adapter/iceberg/test_iceberg_support.py b/tests/functional/adapter/iceberg/test_iceberg_support.py index 1b5eb1616..30ec8f5db 100644 --- a/tests/functional/adapter/iceberg/test_iceberg_support.py +++ b/tests/functional/adapter/iceberg/test_iceberg_support.py @@ -252,3 +252,35 @@ def test_rebuild_keeps_the_table(self, project): assert get_version_zero_timestamp(project, "iceberg_table_rebuild") == created, ( "history restarted, so the rebuild dropped and recreated the table" ) + + +@pytest.mark.skip_profile("databricks_cluster") +class TestManagedIcebergTableOverExistingDelta(ManagedIcebergMixin): + """`table` counterpart of TestManagedIcebergOverExistingDelta: switching `use_managed_iceberg` + on over an existing Delta table must drop and recreate it on the next run, since + `create or replace` cannot change a table's provider (issue #1662). + + This does not reproduce the original bug: the pre-#1674 gate also dropped here, because + `resolve_file_format` returned `parquet`. It guards the composition instead -- with the + `table.sql` from #1674 it fails with MANAGED_ICEBERG_OPERATION_NOT_SUPPORTED if + `format_allows_create_or_replace` ever loses its provider check (the symmetric arm from + #1669), which is the part someone could plausibly "simplify" away later.""" + + @pytest.fixture(scope="class") + def models(self): + return {"iceberg_table_over_delta.sql": fixtures.basic_iceberg_swap} + + def test_run_converts_the_delta_table(self, project): + project.run_sql( + "create or replace table {database}.{schema}.iceberg_table_over_delta using delta " + "as select 1 as id" + ) + assert get_provider(project, "iceberg_table_over_delta") == "delta" + + util.run_dbt() + + assert get_provider(project, "iceberg_table_over_delta") == "iceberg" + rows = project.run_sql( + "select id from {database}.{schema}.iceberg_table_over_delta", fetch="all" + ) + assert len(rows) == 1