88
99from __future__ import annotations
1010
11+ from collections .abc import Callable
1112from typing import TYPE_CHECKING
1213
1314if TYPE_CHECKING :
@@ -121,6 +122,10 @@ def not_sql(self, expression: exp.Expression) -> str:
121122 return f"{ self .sql (child , 'this' )} IS NOT NULL"
122123 if isinstance (child , exp .In ):
123124 return f"{ self .sql (child , 'this' )} NOT IN ({ self .expressions (child )} )"
125+ # sqlglot's Generator.not_sql is typed to take exp.Not; we widen the
126+ # parameter to exp.Expression to match the override signature across
127+ # all custom *_sql methods, and sqlglot's return type is inferred as
128+ # Any from partially-typed stubs.
124129 return super ().not_sql (expression ) # type: ignore[arg-type, no-any-return]
125130
126131
@@ -172,10 +177,19 @@ class NestedResolver:
172177 aliases defined inside nested queries.
173178
174179 :param ast: Root AST node (for body extraction).
180+ :param parser_factory: Callable that constructs a :class:`Parser` from
181+ a SQL string. Injected to break the ``parser.py`` ↔ ``nested_resolver.py``
182+ import cycle — ``parser.py`` already imports this module, so it passes
183+ the ``Parser`` class itself at resolver-construction time.
175184 """
176185
177- def __init__ (self , ast : exp .Expression ):
186+ def __init__ (
187+ self ,
188+ ast : exp .Expression ,
189+ parser_factory : Callable [[str ], "Parser" ],
190+ ) -> None :
178191 self ._ast = ast
192+ self ._parser_factory = parser_factory
179193
180194 # Lazy caches
181195 self ._subqueries_parsers : dict [str , "Parser" ] = {}
@@ -453,11 +467,11 @@ def _lookup_alias_in_nested(
453467 SELECT name FROM (SELECT id FROM users) AS sub
454468 -- "name" not in subquery → returns None
455469 """
456- from sql_metadata .parser import Parser
457-
458470 for nested_name in names :
459471 nested_def = definitions [nested_name ]
460- nested_parser = parser_cache .setdefault (nested_name , Parser (nested_def ))
472+ nested_parser = parser_cache .setdefault (
473+ nested_name , self ._parser_factory (nested_def )
474+ )
461475 if col_name in nested_parser .columns_aliases_names :
462476 # Path 1: alias match — resolve through the full alias chain
463477 # e.g. SELECT col1 AS a ... then SELECT a AS x ...
@@ -479,8 +493,8 @@ def _lookup_alias_in_nested(
479493 # Path 3: not found in any nested query
480494 return None
481495
482- @staticmethod
483496 def _resolve_nested_query (
497+ self ,
484498 subquery_alias : str ,
485499 nested_queries_names : list [str ],
486500 nested_queries : dict [str , str ],
@@ -498,15 +512,15 @@ def _resolve_nested_query(
498512 Resolving ``"sub.id"``: prefix ``"sub"`` matches, column
499513 ``"id"`` is found in the subquery → returns ``["id"]``.
500514 """
501- from sql_metadata .parser import Parser
502-
503515 parts = subquery_alias .split ("." )
504516 if len (parts ) != 2 or parts [0 ] not in nested_queries_names :
505517 # e.g. "table.col" or "schema.table.col" — not a subquery ref
506518 return [subquery_alias ]
507519 sub_query , column_name = parts [0 ], parts [- 1 ]
508520 sub_query_definition = nested_queries [sub_query ]
509- subparser = already_parsed .setdefault (sub_query , Parser (sub_query_definition ))
521+ subparser = already_parsed .setdefault (
522+ sub_query , self ._parser_factory (sub_query_definition )
523+ )
510524 return NestedResolver ._resolve_column_in_subparser (
511525 column_name , subparser , subquery_alias
512526 )
@@ -612,12 +626,11 @@ def _resolve_column_alias(
612626 for x in alias
613627 for item in self ._resolve_column_alias (x , columns_aliases , visited )
614628 ]
615- while alias in columns_aliases and alias not in visited :
629+ if alias in columns_aliases and alias not in visited :
616630 visited .add (alias )
617- alias = columns_aliases [alias ]
618- if isinstance (alias , list ):
619- # e.g. alias mapped to [col1, col2] — resolve list recursively
620- return self ._resolve_column_alias (alias , columns_aliases , visited )
631+ return self ._resolve_column_alias (
632+ columns_aliases [alias ], columns_aliases , visited
633+ )
621634 return [alias ]
622635
623636 # -------------------------------------------------------------------
0 commit comments