Repository: tobymao/sqlglot

Issue:
Error with GENERATE_DATE_ARRAY + UNNEST
I get the error "Unknown colum" when I use this query with the optimizer.
```
SELECT
  *
FROM
  table_1
CROSS JOIN
UNNEST(GENERATE_DATE_ARRAY(start_time, date_snapshot, INTERVAL 1 MONTH)) AS time_value

```


Discussion:
@barakalon i took a stab at this, but this is really gnarly and i don't know how to do it T_T

```
select aa from x cross join unnest(array(a)) as aa;
```

the issue is that the unnest is sourcing the from (it grabs column a from table x) so need to call add_sources, but it also is it's own scope because it provides aa as a new column

i almost got it to work, but it turned into a correlated subquery sort of, but i failed checks but it's not a subquery scope...
Reading up a little on this...

Looks like the UNNEST operator is optional for cross joins: https://cloud.google.com/bigquery/docs/reference/standard-sql/arrays

Also, seems like these "correlated joins" can have correlated subqueries: https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#correlated_join

Not to mention "as aa" defines a column, not a source, which is a new thing.

Gnarly indeed.

I can take a look at this, but it might take a few days for me to get to it, if that's OK.
yup, no rush
Some related variants

```
SELECT
  id,
  type,
  unnest_value AS array_ids
FROM
  table_1
  LEFT JOIN
UNNEST
  (array_ids) AS unnest_value
```

```
SELECT
  COUNT(DISTINCT items.id) AS items,
FROM
  table_1,
UNNEST
  (items) AS items
```


Resolving patch:
diff --git a/sqlglot/optimizer/qualify_columns.py b/sqlglot/optimizer/qualify_columns.py
--- a/sqlglot/optimizer/qualify_columns.py
+++ b/sqlglot/optimizer/qualify_columns.py
@@ -154,14 +154,16 @@ def _qualify_columns(scope, schema):
 
             column_table = unambiguous_columns.get(column_name)
 
-            if not scope.is_subquery:
+            if not scope.is_subquery and not scope.is_unnest:
                 if column_name not in all_columns:
                     raise OptimizeError(f"Unknown column: {column_name}")
 
-                if not column_table:
+                if column_table is None:
                     raise OptimizeError(f"Ambiguous column: {column_name}")
 
-            column.set("table", exp.to_identifier(column_table))
+            # column_table can be a '' because bigquery unnest has no table alias
+            if column_table:
+                column.set("table", exp.to_identifier(column_table))
 
 
 def _expand_stars(scope, schema):
@@ -252,7 +254,11 @@ def _qualify_outputs(scope):
 
 
 def _check_unknown_tables(scope):
-    if scope.external_columns and not scope.is_correlated_subquery:
+    if (
+        scope.external_columns
+        and not scope.is_unnest
+        and not scope.is_correlated_subquery
+    ):
         raise OptimizeError(f"Unknown table: {scope.external_columns[0].text('table')}")
 
 
diff --git a/sqlglot/optimizer/scope.py b/sqlglot/optimizer/scope.py
--- a/sqlglot/optimizer/scope.py
+++ b/sqlglot/optimizer/scope.py
@@ -11,6 +11,7 @@ class ScopeType(Enum):
     DERIVED_TABLE = auto()
     CTE = auto()
     UNION = auto()
+    UNNEST = auto()
 
 
 class Scope:
@@ -284,6 +285,11 @@ def is_subquery(self):
         """Determine if this scope is a subquery"""
         return self.scope_type == ScopeType.SUBQUERY
 
+    @property
+    def is_unnest(self):
+        """Determine if this scope is an unnest"""
+        return self.scope_type == ScopeType.UNNEST
+
     @property
     def is_correlated_subquery(self):
         """Determine if this scope is a correlated subquery"""
@@ -365,16 +371,18 @@ def _traverse_union(scope):
 
 def _traverse_derived_tables(derived_tables, scope, scope_type):
     sources = {}
-    chain = scope_type == ScopeType.CTE
+
     for derived_table in derived_tables:
         for child_scope in _traverse_scope(
             scope.branch(
                 derived_table
                 if isinstance(derived_table, (exp.Unnest, exp.Lateral))
                 else derived_table.this,
-                add_sources=sources if chain else None,
+                add_sources=sources if scope_type == ScopeType.CTE else None,
                 outer_column_list=derived_table.alias_column_names,
-                scope_type=scope_type,
+                scope_type=ScopeType.UNNEST
+                if isinstance(derived_table, exp.Unnest)
+                else scope_type,
             )
         ):
             yield child_scope


Test patch:
diff --git a/tests/fixtures/optimizer/qualify_columns.sql b/tests/fixtures/optimizer/qualify_columns.sql
--- a/tests/fixtures/optimizer/qualify_columns.sql
+++ b/tests/fixtures/optimizer/qualify_columns.sql
@@ -126,6 +126,13 @@ SELECT x.a AS a FROM x AS x WHERE x.b IN (SELECT x.b AS b FROM y AS x);
 SELECT a FROM x AS i WHERE b IN (SELECT b FROM y AS j WHERE j.b IN (SELECT c FROM y AS k WHERE k.b = j.b));
 SELECT i.a AS a FROM x AS i WHERE i.b IN (SELECT j.b AS b FROM y AS j WHERE j.b IN (SELECT k.c AS c FROM y AS k WHERE k.b = j.b));
 
+# dialect: bigquery
+SELECT aa FROM x, UNNEST(a) AS aa;
+SELECT aa AS aa FROM x AS x, UNNEST(x.a) AS aa;
+
+SELECT aa FROM x, UNNEST(a) AS t(aa);
+SELECT t.aa AS aa FROM x AS x, UNNEST(x.a) AS t(aa);
+
 --------------------------------------
 -- Expand *
 --------------------------------------