Skip to content

Commit 19d86a4

Browse files
willweldsd-db
andauthored
fix: skip DESCRIBE TABLE EXTENDED AS JSON for foreign/federated tables (#1472)
Resolves # ### Description For federated/foreign tables, `DESCRIBE TABLE EXTENDED ... AS JSON` is not supported. The adapter was attempting it on every `get_columns_in_relation` call before catching the error and falling back to plain `DESCRIBE TABLE`, causing repeated failures and extra latency on affected code paths (e.g. incremental merge strategy, table create). The fix adds `relation.is_foreign_table` to the `use_legacy_logic` guard in `get_columns_in_relation`, so the JSON path is never attempted for foreign tables. This is consistent with the existing check in `is_describe_as_json_supported()` which already excludes foreign tables — the column-fetch path was simply missing the same guard. No new infrastructure is needed: `DatabricksRelationType.Foreign` and `relation.is_foreign_table` already exist. ### Checklist - [ ] I have run this code in development and it appears to resolve the stated issue - [x] This PR includes tests, or tests are not required/relevant for this PR - [ ] I have updated the `CHANGELOG.md` and added information about my change to the "dbt-databricks next" section. **Note:** I do not have access to a Databricks workspace with federated tables to run functional tests against. Per CONTRIBUTING.md, I'm flagging this for a maintainer `/integration-test` run. --------- Signed-off-by: willweld <williamweld@outlook.com> Co-authored-by: Shubham Dhal <shubham.dhal@databricks.com>
1 parent 2d36bee commit 19d86a4

6 files changed

Lines changed: 106 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Fix missing f-string prefix in `JobRunsApi.submit` debug log ([#1471](https://github.com/databricks/dbt-databricks/pull/1471))
1111
- Fix capability-branching macros falling through to their legacy path at parse/compile time on SQL warehouses. The parse-time stub of `has_dbr_capability` now returns `True` on warehouse profiles for capabilities flagged `sql_warehouse_supported`, so macros select the modern branch during compilation instead of the legacy fallback. ([#1449](https://github.com/databricks/dbt-databricks/pull/1449) closes [#1331](https://github.com/databricks/dbt-databricks/issues/1331))
1212
- Fix snapshots not applying `databricks_tags` on columns ([#1442](https://github.com/databricks/dbt-databricks/pull/1442) closes [#1441](https://github.com/databricks/dbt-databricks/issues/1441))
13+
- Skip `DESCRIBE TABLE EXTENDED ... AS JSON` for foreign/federated tables in `get_columns_in_relation`, avoiding repeated failures and extra latency on those sources ([#1472](https://github.com/databricks/dbt-databricks/pull/1472))
1314

1415
### Under the Hood
1516

dbt/adapters/databricks/impl.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ def get_columns_in_relation( # type: ignore[override]
644644
# TODO: Replace with streaming table capability check when 17.1 is current version
645645
# for SQL warehouses
646646
or relation.type == DatabricksRelationType.StreamingTable
647+
or relation.is_foreign_table
647648
)
648649
return self.get_column_behavior.get_columns_in_relation(self, relation, use_legacy_logic)
649650

tests/functional/adapter/columns/fixtures.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,17 @@
5252
- name: string_col
5353
data_type: string
5454
"""
55+
56+
foreign_table_source_model = """
57+
{{ config(materialized='table') }}
58+
select cast(1 as bigint) as id, 'federated' as name
59+
"""
60+
61+
foreign_table_source_schema = """
62+
version: 2
63+
models:
64+
- name: foreign_table_source
65+
columns:
66+
- name: id
67+
- name: name
68+
"""
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import pytest
2+
from dbt.tests import util
3+
4+
from dbt.adapters.databricks.column import DatabricksColumn
5+
from dbt.adapters.databricks.relation import DatabricksRelation, DatabricksRelationType
6+
from tests.functional.adapter.columns import fixtures
7+
from tests.functional.adapter.fixtures import (
8+
RequiresDescribeAsJsonCapabilityMixin,
9+
fail_if_get_columns_as_json_called_macros,
10+
)
11+
12+
13+
# The HMS cluster profile uses hive_metastore, where get_columns_in_relation already
14+
# takes the legacy path via is_hive_metastore() — before the foreign-table check
15+
# matters. This test only validates the fix on UC profiles where JSON column
16+
# metadata would otherwise be preferred.
17+
@pytest.mark.skip_profile("databricks_cluster")
18+
class TestForeignTableGetColumns(RequiresDescribeAsJsonCapabilityMixin):
19+
"""Foreign tables must fetch columns without attempting DESCRIBE AS JSON."""
20+
21+
@pytest.fixture(scope="class")
22+
def models(self):
23+
# Lakehouse Federation foreign tables aren't available in CI, so we
24+
# materialize a normal UC table and assert column fetch works when the
25+
# relation is typed as Foreign (see test method below).
26+
return {
27+
"foreign_table_source.sql": fixtures.foreign_table_source_model,
28+
"schema.yml": fixtures.foreign_table_source_schema,
29+
}
30+
31+
@pytest.fixture(scope="class")
32+
def macros(self):
33+
return {"fail_if_get_columns_as_json_called.sql": fail_if_get_columns_as_json_called_macros}
34+
35+
@pytest.fixture(scope="class", autouse=True)
36+
def setup(self, project):
37+
util.run_dbt(["debug", "--connection"])
38+
util.run_dbt(["run"])
39+
40+
@pytest.fixture(scope="class")
41+
def expected_columns(self):
42+
return [
43+
DatabricksColumn(column="id", dtype="bigint"),
44+
DatabricksColumn(column="name", dtype="string"),
45+
]
46+
47+
def test_foreign_table_get_columns_returns_expected_columns(self, project, expected_columns):
48+
# No real federated table in CI — reuse the managed table above but mark
49+
# the relation Foreign so get_columns_in_relation exercises that code path.
50+
foreign_relation = DatabricksRelation.create(
51+
database=project.database,
52+
schema=project.test_schema,
53+
identifier="foreign_table_source",
54+
type=DatabricksRelationType.Foreign,
55+
)
56+
57+
with project.adapter.connection_named("_test"):
58+
actual_columns = project.adapter.get_columns_in_relation(foreign_relation)
59+
60+
assert actual_columns == expected_columns

tests/functional/adapter/fixtures.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
{% endmacro %}
2020
"""
2121

22+
fail_if_get_columns_as_json_called_macros = """
23+
{% macro get_columns_comments_as_json(relation) %}
24+
{{ exceptions.raise_compiler_error("get_columns_comments_as_json should not be called") }}
25+
{% endmacro %}
26+
"""
27+
2228

2329
class MaterializationV1Mixin:
2430
@pytest.fixture(scope="class")

tests/unit/test_adapter.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,30 @@ def test_get_columns_materialized_view(self, mock_get_columns, adapter, unity_re
12681268
assert result[0].dtype == "string"
12691269
assert result[0].comment == "mv col"
12701270

1271+
@patch(
1272+
"dbt.adapters.databricks.behaviors.columns.GetColumnsByDescribe._get_columns_with_comments"
1273+
)
1274+
def test_get_columns_foreign_table_uses_legacy_logic(
1275+
self, mock_get_columns, adapter, unity_relation
1276+
):
1277+
foreign_relation = DatabricksRelation.create(
1278+
database=unity_relation.database,
1279+
schema=unity_relation.schema,
1280+
identifier=unity_relation.identifier,
1281+
type=DatabricksRelationType.Foreign,
1282+
)
1283+
# Foreign/federated tables don't support AS JSON — always use legacy logic
1284+
with patch.object(adapter, "has_capability", return_value=True):
1285+
mock_get_columns.return_value = [
1286+
{"col_name": "federated_col", "data_type": "string", "comment": ""},
1287+
]
1288+
result = adapter.get_columns_in_relation(foreign_relation)
1289+
assert mock_get_columns.call_count == 1
1290+
mock_get_columns.assert_called_with(adapter, foreign_relation, "get_columns_comments")
1291+
assert len(result) == 1
1292+
assert result[0].column == "federated_col"
1293+
assert result[0].dtype == "string"
1294+
12711295
@patch(
12721296
"dbt.adapters.databricks.behaviors.columns.GetColumnsByDescribe._get_columns_with_comments"
12731297
)

0 commit comments

Comments
 (0)