Repository: tobymao/sqlglot

Issue:
Failing to parse `UNNEST ... WITH OFFSET` in BigQuery 
The following code fails to parse on v10.5.2 with `dialect="bigquery"`:
```
WITH table_data AS (
    SELECT 'bob' AS name, ARRAY['banana', 'apple', 'orange'] AS fruit_basket
)
SELECT
    name,
    fruit,
    basket_index
FROM table_data
CROSS JOIN UNNEST(fruit_basket) AS fruit WITH OFFSET basket_index
```

with the following error:
```
ParseError('Invalid expression / Unexpected token. Line 9, Col: 42.\n  LECT\n    name,\n    fruit,\n    basket_index\nFROM table_data\nCROSS JOIN UNNEST(fruit_basket) as fruit \x1b[4mWITH\x1b[0m OFFSET basket_index')
```



Discussion:

Resolving patch:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -1460,6 +1460,7 @@ class Unnest(UDTF):
         "expressions": True,
         "ordinality": False,
         "alias": False,
+        "offset": False,
     }
 
 
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -1036,7 +1036,9 @@ def unnest_sql(self, expression: exp.Unnest) -> str:
             alias = self.sql(expression, "alias")
         alias = f" AS {alias}" if alias else alias
         ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
-        return f"UNNEST({args}){ordinality}{alias}"
+        offset = expression.args.get("offset")
+        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
+        return f"UNNEST({args}){ordinality}{alias}{offset}"
 
     def where_sql(self, expression: exp.Where) -> str:
         this = self.indent(self.sql(expression, "this"))
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -1643,8 +1643,17 @@ def _parse_unnest(self) -> t.Optional[exp.Expression]:
             alias.set("columns", [alias.this])
             alias.set("this", None)
 
+        offset = None
+        if self._match_pair(TokenType.WITH, TokenType.OFFSET):
+            self._match(TokenType.ALIAS)
+            offset = self._parse_conjunction()
+
         return self.expression(
-            exp.Unnest, expressions=expressions, ordinality=ordinality, alias=alias
+            exp.Unnest,
+            expressions=expressions,
+            ordinality=ordinality,
+            alias=alias,
+            offset=offset,
         )
 
     def _parse_derived_table_values(self) -> t.Optional[exp.Expression]:


Test patch:
diff --git a/tests/fixtures/pretty.sql b/tests/fixtures/pretty.sql
--- a/tests/fixtures/pretty.sql
+++ b/tests/fixtures/pretty.sql
@@ -322,3 +322,23 @@ SELECT
   * /* multi
      line
      comment */;
+WITH table_data AS (
+    SELECT 'bob' AS name, ARRAY['banana', 'apple', 'orange'] AS fruit_basket
+)
+SELECT
+    name,
+    fruit,
+    basket_index
+FROM table_data
+CROSS JOIN UNNEST(fruit_basket) AS fruit WITH OFFSET basket_index;
+WITH table_data AS (
+  SELECT
+    'bob' AS name,
+    ARRAY('banana', 'apple', 'orange') AS fruit_basket
+)
+SELECT
+  name,
+  fruit,
+  basket_index
+FROM table_data
+CROSS JOIN UNNEST(fruit_basket) AS fruit WITH OFFSET AS basket_index;