Repository navigation
Fixes 37258: Map column lineage through STRUCT and ARRAY<STRUCT> fields - #37259
Khairajani wants to merge 5 commits into
Conversation
✅ PR checks passedThe linked issue has a description and all required Shipping project fields set. Thanks! |
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
|
The Python checkstyle failed. Please run You can install the pre-commit hooks with |
1 similar comment
|
The Python checkstyle failed. Please run You can install the pre-commit hooks with |
Code Review ✅ Approved 1 closed / 1 findings🟡 Medium risk · Adding nested-field lineage enables tag and description propagation, and an incorrect edge could attach metadata to the wrong downstream fields. Maps query and view column lineage through STRUCT and ARRAY<STRUCT> fields so tag and description propagation reaches nested columns, including field paths like ✅ 1 closed✅ Edge Case: STRUCT field paths through a UDF source can get lineage from the wrong table
VerificationRules No rules evaluated Functional validation Not enabledAuto-approval Not enabledFor more verification details, comment OptionsDisplay: compact → Showing compact verification results. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source |
|



Describe your changes:
Fixes #37258
Query and view column lineage now reaches the fields of STRUCT and ARRAY columns. Before this change it stopped at the top-level column, so tag and description propagation over lineage never reached nested fields.
Struct selected as-is (
SELECT customer FROM t): thecustomer → customeredge now also carries one edge per nested field the two columns share by name, at every depth (customer.address.zip → customer.address.zip).Field paths (
SELECT customer.*,SELECT customer.email AS e): the parser readscustomeras a table that the query never reads. These entries are now resolved against the source table's STRUCT. Before, they produced no lineage.No guessing: the parser drops a field path's qualifier, so a path is resolved only when nothing else can own its name:
Otherwise the path gets no lineage instead of lineage from the wrong column. This rule is recorded in ADR:2026-11-06-sql-column-lineage-follows-nested-fields.
Type of change:
Behaviour change / breaking changes:
Not breaking. Edges are only added. Every edge produced today is still produced, and no schema, API or config changes.
On by default, with no flag. It fills in lineage the SQL already implies, so there is nothing to opt into.
Applies to every connector that ingests nested columns. Covered by tests: BigQuery, Databricks, Spark, Hive, Trino, Athena and Snowflake. The next lineage run adds the nested edges; tables with wide STRUCTs get noticeably more column edges.
Propagation reaches nested fields. Tag and description propagation now reaches nested fields in downstream views, which is the point of the change. Users who propagate tags will see them land on those fields after the next lineage run.
Still no field-level lineage for:
UNNEST-based array access;col:fieldpaths.These behave the same as before the change.
High-level design:
N/A — small change in
sql_lineage.py, plus aLineageParser.has_subqueriesproperty inparser.py.Tests:
Use cases covered
struct.*(unqualified and aliased) maps each field to its view column.struct.field AS aliasandstruct.inner.field AS aliasmap to the alias.Unit tests
ingestion/tests/unit/lineage/test_nested_column_lineage.py(22 tests). Each of them fails onmainor fails when its guard is removed.sql_lineageorparser, plustests/unit/lineage/. 1013 passed, 0 failed (now also coverstests/unit/test_query_parser.py).test_pgspider_lineage_unit.py, because the local env has no pgspider SQLAlchemy dialect.sql_lineage.pyis executed by the tests.sql_lineage.pyandparser.py; it reports 0 new errors. The ruff suppression check also passes.Backend integration tests
Ingestion integration tests
tests/integration/postgres/test_lineage.pyagainst a local server: 4 passed.tests/integration/sql_server/test_lineage.py(needs the SQL Server container; it runs in the merge queue). The profiler and classifier integration tests on the affected list don't exercise column lineage.Playwright (UI) tests
Manual testing performed
End to end, against a local server, I created a BigQuery-type service with a
userstable holdingcustomer STRUCT<email, address STRUCT<zip, city>>andevents ARRAY<STRUCT<ip>>, withPII.Sensitiveoncustomer.email,customer.address.zipandevents.ip. I added three views and ran view lineage (get_view_lineage), then lineage-based tag propagation fromusers. Results, before → after:SELECT id, customer, events:PII.Sensitiveoncustomer.email,customer.address.zipandevents.ip.SELECT id, customer.*:PII.Sensitiveonemailandaddress.zip.SELECT customer.email AS customer_email, customer.address.zip AS zip:PII.Sensitiveon both columns.The untagged fields (
id,address.city) stayed untagged. The server stored every nested column FQN, since lineage validation already resolves nested children.UI screen recording:
Not applicable — ingestion-only fix with no UI change.
Checklist:
Fixes <issue-number>: <short explanation>Fixes #<issue-number>above.REVIEW.mdand fixed every 🔴 finding. Reviewed at17c0321ee96; the bot follow-up isfae55f39005.fae55f39005; field paths now need a table source.🤖 Generated with Claude Code