Skip to content

fix(ingest/powerbi): recover Databricks lineage lost to three M-Query shapes - #19364

Open
puneetagarwal-datahub wants to merge 5 commits into
masterfrom
fix/powerbi-mquery-lineage-gaps
Open

fix(ingest/powerbi): recover Databricks lineage lost to three M-Query shapes#19364
puneetagarwal-datahub wants to merge 5 commits into
masterfrom
fix/powerbi-mquery-lineage-gaps

Conversation

@puneetagarwal-datahub

@puneetagarwal-datahub puneetagarwal-datahub commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Three shapes of valid M-Query silently produced no lineage. Each is a separate defect; all three were reproduced against the real M-Query bridge before fixing.

1. Redundant parentheses around a navigation step

tbl = (sch{[Name="my_table",Kind="Table"]}[Data])   // or: then (sch{...}[Data])

_walk had no ParenthesizedExpression case, so it hit the unhandled-node fallback and returned. The parse itself succeeded — only the walk gave up.

2. A navigation step written without Kind=

tbl = sch{[Name="my_table"]}[Data]

table_detail[items["Kind"]] raised a bare KeyError, which the caller's broad except turned into an "Unknown M-Query Pattern" warning — so the report named the pattern rather than the missing key. Databricks chains are ordered catalog → schema → table, so the level now follows from position. A step with neither Kind nor a resolvable Name is genuinely unusable and is reported as such instead of raising.

3. Native SQL on the Databricks.Catalogs connector

Value.NativeQuery was registered for DatabricksMultiCloud.Catalogs but not for plain Databricks.Catalogs, so Azure Databricks native queries bailed at the is_native_parsing_supported gate. get_db_name needed widening in the same change — both connectors take (host, http path, [Database=…, Catalog=…]), and without that half the catalog comes back empty and the emitted URN points at a dataset that does not exist.

Scope

Covers the inline and single-hop source-argument forms of (3). A source argument reached through more than one navigation step is a separate limitation in _get_data_source_tokens — that one is platform-agnostic (it affects Snowflake identically) and is left to its own PR.

Testing

Every fix has a test that was watched failing first, plus a golden integration test asserting the emitted MCPs — table-level upstream and column-level lineage. Verified the golden fails if the Databricks.Catalogs registration is removed, so it genuinely guards the change.

The golden uses a dedicated mock response rather than extending default_mock_response.json. That fixture is loaded by 34 test invocations, so adding a table there regenerates 13 goldens for ~3,400 lines of churn — and would assert nothing, because test_extract_lineage narrows dataset_type_mapping to four platforms and powerbi.py drops upstreams for any platform outside it. The per-platform fixture/golden pair follows the three existing mysql and ODBC pairs.

363 passed, 1 xfailed · ruff, format and mypy clean · no existing goldens touched.

Note for reviewers

This and #19365 both touch resolver.py — this PR adds the ParenthesizedExpression case, the other renames the walk's accumulator. Whichever merges second needs a one-block rebase with no logic overlap. Either order is fine.

Checklist

  • PR conforms to the Contributing Guideline (particularly PR Title Format)
  • Tests for the changes have been added
  • Docs — n/a, no user-facing config change
  • Breaking changes — none

Summary by cubic

Recovers Power BI lineage lost to three valid M-Query shapes and enables native SQL lineage on Azure Databricks. Previously: parenthesized navigation steps aborted the walk; Databricks steps without Kind raised and were masked as “Unknown M-Query Pattern”; Value.NativeQuery on Databricks.Catalogs was unsupported. Now: the walk unwraps parentheses, Databricks levels are inferred by position (catalog → schema → table), unusable steps warn and skip, and native queries on both Databricks connectors emit correct 3-part URNs.

  • Adds ParenthesizedExpression handling in the resolver walk (covers bare (Source{...}[Data]) and then (...)/else (...)).
  • Infers Databricks navigation level when Kind is missing; warns (does not raise) if both Kind and Name are missing.
  • Registers Databricks.Catalogs for native-query parsing and widens database resolution to handle both connectors; fixes URN catalog resolution.
  • Scope: handles inline and single-hop source arguments; multi-hop source arguments remain unsupported and will be addressed separately.
  • Tests: focused unit tests, an integration golden asserting upstream and column-level lineage for Databricks.Catalogs native queries, and an added check pinning mid-chain level inference; no existing goldens changed.
  • Review note: touches resolver.py; expect a trivial one-block rebase if concurrent changes land first.

Written for commit 2e9c885. Summary will update on new commits.

Review in cubic

… steps

Redundant parentheses around a navigation step are valid M and refresh
without complaint in Power BI, but the M-Query resolver had no
ParenthesizedExpression case. The walk hit the unhandled-node fallback
and returned, so every table written that way produced no lineage at
all -- silently, since the fallback only logs at debug level.

Unwrap the node and continue the walk, which covers both a bare
`(Source{[Name=..]}[Data])` step and the `then (...)` / `else (...)`
form inside a conditional.
…is absent

A navigation step may omit Kind (`Schema{[Name="t"]}[Data]`), which is
valid M that Power BI refreshes without complaint. Indexing items["Kind"]
raised a bare KeyError that the caller's broad except turned into an
"Unknown M-Query Pattern" warning, so the table lost its lineage and the
report pointed at the pattern rather than the missing key.

Databricks chains are ordered catalog -> schema -> table, so take the
first level the chain has not filled yet. A step with neither Kind nor a
resolvable Name is genuinely unusable: report it as such instead of
raising.
…nnector

Value.NativeQuery was recognised for DatabricksMultiCloud.Catalogs but not
for plain Databricks.Catalogs, so Azure Databricks native queries bailed
out at the is_native_parsing_supported gate with only a debug log.

Register the connector and, in the same change, widen get_db_name to
accept it. Both connectors take (host, http path, [Database=..., Catalog=...]),
so the database resolves the same way -- without that half, the catalog
would come back empty and the emitted URN would silently point at a
dataset that does not exist.

Covers the inline and single-hop source-argument forms. A source argument
reached through more than one navigation step is a separate limitation in
_get_data_source_tokens and is not addressed here.
… lineage

The focused parser test asserts the upstream URN but nothing exercises the
emitted MCPs, so a wrong URN shape, a wrong lineage type, or a dropped
aspect would pass CI.

Use a dedicated mock response and golden rather than extending
default_mock_response.json. That fixture is loaded by 34 test invocations,
so adding a table there regenerates 13 goldens for roughly 3400 lines of
churn -- and it would assert nothing, because test_extract_lineage narrows
dataset_type_mapping to four platforms and powerbi.py drops upstreams for
any platform outside it. The per-platform fixture and golden pair follows
the three existing mysql and ODBC pairs.

Verified the golden guards the change: removing Databricks.Catalogs from
SUPPORTED_NATIVE_QUERY_DATA_PLATFORM makes it fail.
@github-actions github-actions Bot added the ingestion PR or Issue related to the ingestion of metadata label Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Linear: ING-3347

Thanks for your contribution! We have created an internal ticket to track this PR. A member of the core DataHub team will be assigned to review it within the next few business days - you will get a follow-up comment once a reviewer is assigned.

@cursor

cursor Bot commented Aug 21, 2026

Copy link
Copy Markdown

PR Summary

Overview
Restores Power BI → Databricks lineage for three valid M-Query shapes that previously dropped silently.

The M-Query walker now continues through ParenthesizedExpression nodes, so extra parentheses around a navigation step (including then (...)) no longer abort the walk.

DatabricksLineage infers catalog/schema/table from chain position when a step omits Kind, and reports a warning instead of raising KeyError when a step has neither Kind nor Name.

Value.NativeQuery on Databricks.Catalogs is now treated like the MultiCloud connector, so Azure Databricks native SQL emits 3-part table URNs (and column lineage). Multi-hop source arguments remain unsupported.

Reviewed by Cursor Bugbot for commit 2e9c885. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.57143% with 3 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...tahub/ingestion/source/powerbi/m_query/resolver.py 0.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 7 files

Re-trigger cubic

@datahub-connector-tests

datahub-connector-tests Bot commented Aug 21, 2026

Copy link
Copy Markdown

Connector Tests Results

All connector tests passed for commit 2e9c885

View full test logs →

To skip connector tests, add the skip-connector-tests label (org members only).

Autogenerated by the connector-tests CI pipeline.

Comment thread metadata-ingestion/tests/unit/powerbi/test_parser.py
The leaf-step test does not constrain which level a step in the middle of
the chain takes -- it passes even with the catalog/schema/table ordering
reversed. Assert the schema step directly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ingestion PR or Issue related to the ingestion of metadata needs-review Label for PRs that need review from a maintainer.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants