Skip to content

Commit 0e48e25

Browse files
authored
fix(optimizer): qualify columns with circular dependency (#6873)
* fix(optimizer): qualify columns with circular dependency * fix style * add clear cache for column index
1 parent acec48e commit 0e48e25

3 files changed

Lines changed: 43 additions & 0 deletions

File tree

sqlglot/optimizer/qualify_columns.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,13 @@ def _qualify_columns(
610610
# column_table can be a '' because bigquery unnest has no table alias
611611
column_table = resolver.get_table(column)
612612

613+
if (
614+
column_table
615+
and isinstance(source := scope.sources.get(column_table.name), Scope)
616+
and id(column) in source.column_index
617+
):
618+
continue
619+
613620
if column_table:
614621
column.set("table", column_table)
615622
elif (

sqlglot/optimizer/scope.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def clear_cache(self):
103103
self._pivots = None
104104
self._references = None
105105
self._semi_anti_join_tables = None
106+
self._column_index = None
106107

107108
def branch(
108109
self, expression, scope_type, sources=None, cte_sources=None, lateral_sources=None, **kwargs
@@ -131,6 +132,7 @@ def _collect(self):
131132
self._stars = []
132133
self._join_hints = []
133134
self._semi_anti_join_tables = set()
135+
self._column_index = set()
134136

135137
for node in self.walk(bfs=False):
136138
if node is self.expression:
@@ -139,6 +141,8 @@ def _collect(self):
139141
if isinstance(node, exp.Dot) and node.is_star:
140142
self._stars.append(node)
141143
elif isinstance(node, exp.Column) and not isinstance(node, exp.Pseudocolumn):
144+
self._column_index.add(id(node))
145+
142146
if isinstance(node.this, exp.Star):
143147
self._stars.append(node)
144148
else:
@@ -259,6 +263,14 @@ def stars(self) -> t.List[exp.Column | exp.Dot]:
259263
self._ensure_collected()
260264
return self._stars
261265

266+
@property
267+
def column_index(self) -> t.Set[int]:
268+
"""
269+
Set of column object IDs that belong to this scope's expression.
270+
"""
271+
self._ensure_collected()
272+
return self._column_index
273+
262274
@property
263275
def columns(self):
264276
"""

tests/test_optimizer.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,30 @@ def test_qualify_columns(self, logger):
574574
)
575575
self.check_file("qualify_columns_ddl", qualify_columns, schema=self.schema)
576576

577+
self.assertEqual(
578+
optimizer.qualify.qualify(
579+
parse_one(
580+
"""
581+
SELECT
582+
(
583+
SELECT
584+
col_st.value
585+
FROM UNNEST(col_st) AS col_st
586+
) AS vcol1
587+
FROM t AS b
588+
""",
589+
read="bigquery",
590+
),
591+
schema={
592+
"t": {
593+
"col_st": "ARRAY<STRUCT<key STRING, value INT>>",
594+
}
595+
},
596+
dialect="bigquery",
597+
).sql(dialect="bigquery"),
598+
"SELECT (SELECT `col_st`.`value` AS `value` FROM UNNEST(`b`.`col_st`) AS `col_st`) AS `vcol1` FROM `t` AS `b`",
599+
)
600+
577601
def test_validate_columns(self):
578602
with self.assertRaisesRegex(
579603
OptimizeError, "Column 'foo' could not be resolved. Line: 1, Col: 10"

0 commit comments

Comments
 (0)