summaryrefslogtreecommitdiffstats
path: root/sqlglot/optimizer/scope.py
diff options
context:
space:
mode:
Diffstat (limited to 'sqlglot/optimizer/scope.py')
-rw-r--r--sqlglot/optimizer/scope.py55
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)
):