diff options
Diffstat (limited to 'sqlglot/optimizer/scope.py')
-rw-r--r-- | sqlglot/optimizer/scope.py | 55 |
1 files changed, 32 insertions, 23 deletions
diff --git a/sqlglot/optimizer/scope.py b/sqlglot/optimizer/scope.py index 7dcfb37..b2b4230 100644 --- a/sqlglot/optimizer/scope.py +++ b/sqlglot/optimizer/scope.py @@ -122,7 +122,11 @@ class Scope: self._udtfs.append(node) elif isinstance(node, exp.CTE): self._ctes.append(node) - elif isinstance(node, exp.Subquery) and isinstance(parent, (exp.From, exp.Join)): + elif ( + isinstance(node, exp.Subquery) + and isinstance(parent, (exp.From, exp.Join)) + and _is_subquery_scope(node) + ): self._derived_tables.append(node) elif isinstance(node, exp.Subqueryable): self._subqueries.append(node) @@ -274,6 +278,7 @@ class Scope: not ancestor or column.table or isinstance(ancestor, exp.Select) + or (isinstance(ancestor, exp.Table) and not isinstance(ancestor.this, exp.Func)) or ( isinstance(ancestor, exp.Order) and ( @@ -341,23 +346,6 @@ class Scope: } @property - def selects(self): - """ - Select expressions of this scope. - - For example, for the following expression: - SELECT 1 as a, 2 as b FROM x - - The outputs are the "1 as a" and "2 as b" expressions. - - Returns: - list[exp.Expression]: expressions - """ - if isinstance(self.expression, exp.Union): - return self.expression.unnest().selects - return self.expression.selects - - @property def external_columns(self): """ Columns that appear to reference sources in outer scopes. @@ -548,6 +536,8 @@ def _traverse_scope(scope): yield from _traverse_union(scope) elif isinstance(scope.expression, exp.Subquery): yield from _traverse_subqueries(scope) + elif isinstance(scope.expression, exp.Table): + yield from _traverse_tables(scope) elif isinstance(scope.expression, exp.UDTF): pass else: @@ -620,6 +610,15 @@ def _traverse_ctes(scope): scope.sources.update(sources) +def _is_subquery_scope(expression: exp.Subquery) -> bool: + """ + We represent (tbl1 JOIN tbl2) as a Subquery, but it's not really a new scope. + If an alias is present, it shadows all names under the Subquery, so that's an + exception to this rule. + """ + return bool(not isinstance(expression.unnest(), exp.Table) or expression.alias) + + def _traverse_tables(scope): sources = {} @@ -629,9 +628,8 @@ def _traverse_tables(scope): if from_: expressions.append(from_.this) - for expression in (scope.expression, *scope.find_all(exp.Table)): - for join in expression.args.get("joins") or []: - expressions.append(join.this) + for join in scope.expression.args.get("joins") or []: + expressions.append(join.this) if isinstance(scope.expression, exp.Table): expressions.append(scope.expression) @@ -655,6 +653,8 @@ def _traverse_tables(scope): sources[find_new_name(sources, table_name)] = expression else: sources[source_name] = expression + + expressions.extend(join.this for join in expression.args.get("joins") or []) continue if not isinstance(expression, exp.DerivedTable): @@ -664,10 +664,15 @@ def _traverse_tables(scope): lateral_sources = sources scope_type = ScopeType.UDTF scopes = scope.udtf_scopes - else: + elif _is_subquery_scope(expression): lateral_sources = None scope_type = ScopeType.DERIVED_TABLE scopes = scope.derived_table_scopes + else: + # Makes sure we check for possible sources in nested table constructs + expressions.append(expression.this) + expressions.extend(join.this for join in expression.args.get("joins") or []) + continue for child_scope in _traverse_scope( scope.branch( @@ -728,7 +733,11 @@ def walk_in_scope(expression, bfs=True): continue if ( isinstance(node, exp.CTE) - or (isinstance(node, exp.Subquery) and isinstance(parent, (exp.From, exp.Join))) + or ( + isinstance(node, exp.Subquery) + and isinstance(parent, (exp.From, exp.Join)) + and _is_subquery_scope(node) + ) or isinstance(node, exp.UDTF) or isinstance(node, exp.Subqueryable) ): |