Skip to content

Commit 37739a4

Browse files
fix(quicksight): handle iterable _parent and fix checkstyle violation also fix bot comments
1 parent 1c5f127 commit 37739a4

2 files changed

Lines changed: 85 additions & 9 deletions

File tree

ingestion/src/metadata/ingestion/source/dashboard/quicksight/metadata.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -273,16 +273,38 @@ def _build_column_lineage_from_parser(
273273
src_col = col_pair[0]
274274
tgt_col = col_pair[-1]
275275

276-
# Multi-table safety: filter by parent table to avoid resolving
277-
# a shared column name (e.g. 'id') to the wrong upstream table.
278-
if src_col._parent:
279-
# _parent may be qualified: '<default>.table' or 'schema.table'
280-
parent_str = str(src_col._parent).replace("<default>.", "")
281-
# Compare only the table name portion (last segment)
282-
parent_table = parent_str.split(".")[-1].lower()
276+
parent = getattr(src_col, "_parent", None)
277+
if parent is not None:
278+
# _parent may be a single Table or an iterable of Tables.
279+
# Normalise to a list so we handle both cases uniformly.
280+
parents = (
281+
list(parent)
282+
if hasattr(parent, "__iter__")
283+
and not isinstance(parent, str)
284+
else [parent]
285+
)
283286
entity_table = from_entity.name.root.lower()
284-
if parent_table != entity_table:
287+
# Accept the pair only when at least one parent table
288+
# matches the current upstream entity being processed.
289+
if not any(
290+
str(p)
291+
.replace("<default>.", "")
292+
.split(".")[-1]
293+
.lower()
294+
== entity_table
295+
for p in parents
296+
):
285297
continue
298+
else:
299+
# _parent is None — parser could not determine source
300+
# table. Log for visibility but allow the pair through;
301+
# get_column_fqn provides a secondary guard (column must
302+
# exist in entity).
303+
logger.debug(
304+
"No parent table info for column %s; "
305+
"skipping parent-table filter",
306+
src_col.raw_name,
307+
)
286308

287309
# raw_name may be fully-qualified (e.g. 'schema.table.col')
288310
# Extract just the column name portion.
@@ -306,7 +328,8 @@ def _build_column_lineage_from_parser(
306328
)
307329
except Exception as exc: # pylint: disable=broad-except
308330
logger.debug(
309-
f"Failed to build column lineage for {src_col_name} -> {tgt_col_name}: {exc}"
331+
f"Failed to build column lineage "
332+
f"for {src_col_name} -> {tgt_col_name}: {exc}"
310333
)
311334
logger.debug(traceback.format_exc())
312335

ingestion/tests/unit/topology/dashboard/test_quicksight.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,59 @@ def test_build_column_lineage_no_fallback_when_parser_has_global_lineage(self):
526526
assert result == []
527527

528528
@pytest.mark.order(13)
529+
def test_build_column_lineage_from_parser_iterable_parent(self):
530+
"""
531+
When src_col._parent is an iterable of parent tables (as some
532+
parser outputs produce), _build_column_lineage_from_parser must
533+
correctly match against from_entity when any parent matches.
534+
Issue #26670.
535+
"""
536+
# Parent is an iterable (list) of Table objects
537+
parent_table_mock = MagicMock()
538+
parent_table_mock.__str__ = MagicMock(
539+
return_value="relation_table"
540+
)
541+
542+
src_col = MagicMock()
543+
src_col.raw_name = "id"
544+
# _parent is a list — simulates iterable parser output
545+
src_col._parent = [parent_table_mock]
546+
547+
tgt_col = MagicMock()
548+
tgt_col.raw_name = "relation_id"
549+
550+
mock_parser = MagicMock()
551+
mock_parser.column_lineage = [(src_col, tgt_col)]
552+
553+
src_fqn = "postgres.public.relation_table.id"
554+
alias_fqn = "quicksight_service.dataset.relation_id"
555+
556+
mock_from_entity = MagicMock()
557+
mock_from_entity.name.root = "relation_table"
558+
mock_data_model = MagicMock()
559+
560+
with patch(
561+
"metadata.ingestion.source.dashboard.quicksight.metadata.get_column_fqn",
562+
return_value=src_fqn,
563+
):
564+
with patch.object(
565+
self.quicksight,
566+
"_get_data_model_column_fqn",
567+
return_value=alias_fqn,
568+
):
569+
result = (
570+
self.quicksight._build_column_lineage_from_parser(
571+
mock_parser,
572+
mock_from_entity,
573+
mock_data_model,
574+
)
575+
)
576+
577+
assert len(result) == 1
578+
assert result[0].fromColumns == [src_fqn]
579+
assert result[0].toColumn == alias_fqn
580+
581+
@pytest.mark.order(14)
529582
def test_build_column_lineage_from_parser_falls_back_when_empty(self):
530583
"""
531584
When lineage_parser.column_lineage is empty (parser failed or

0 commit comments

Comments
 (0)