Repository: tobymao/sqlglot

Issue:
window frame generates incorrect sql when aliased
Parsing an empty window frame and then generating the SQL from it produces a reference to a non-existent named window frame.

```
In [1]: import sqlglot as sg

In [2]: sg.alias(sg.parse_one("LAG(x) over ()"), "a").sql()
Out[2]: 'LAG(x) OVER a'
```

When the window frame has a `PARTITION BY` or `ORDER BY` or both, the result is also incorrect but for a different reason

Only `PARTITION BY`:

```
In [3]: sg.alias(sg.parse_one("LAG(x) over (partition by y)"), "a").sql()
Out[3]: 'LAG(x) OVER (aPARTITION BY y)'
```

Only `ORDER BY`:

```
In [4]: sg.alias(sg.parse_one("LAG(x) over (order by y)"), "a").sql()
Out[4]: 'LAG(x) OVER (aORDER BY y)'
```

Both `PARTITION BY` and `ORDER BY`:

```
In [5]: sg.alias(sg.parse_one("LAG(x) over (partition by y order by z)"), "a").sql()
Out[5]: 'LAG(x) OVER (aPARTITION BY y ORDER BY z)'
```

The desired result is:

```
In [5]: sg.alias(sg.parse_one("LAG(x) over (partition by y order by z)"), "a").sql()
Out[5]: 'LAG(x) OVER (PARTITION BY y ORDER BY z) AS a'
```

Discussion:

Patch:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
index def3eda1..126adfdb 100644
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -3150,7 +3150,7 @@ def alias_(expression, alias, table=False, dialect=None, quoted=None, **opts):
     alias = to_identifier(alias, quoted=quoted)
     alias = TableAlias(this=alias) if table else alias
 
-    if "alias" in exp.arg_types:
+    if "alias" in exp.arg_types and not isinstance(exp, Window):
         exp = exp.copy()
         exp.set("alias", alias)
         return exp
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 6afe86bf..799e3bee 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -1726,7 +1726,7 @@ class Parser:
     def _parse_field(self, any_token=False):
         return self._parse_primary() or self._parse_function() or self._parse_id_var(any_token)
 
-    def _parse_function(self):
+    def _parse_function(self, functions=None):
         if not self._curr:
             return None
 
@@ -1762,7 +1762,9 @@ class Parser:
                 self._match_r_paren()
                 return this
 
-            function = self.FUNCTIONS.get(upper)
+            if functions is None:
+                functions = self.FUNCTIONS
+            function = functions.get(upper)
             args = self._parse_csv(self._parse_lambda)
 
             if function:


Test patch:
diff --git a/tests/dialects/test_bigquery.py b/tests/dialects/test_bigquery.py
index 3f84dad8..89219241 100644
--- a/tests/dialects/test_bigquery.py
+++ b/tests/dialects/test_bigquery.py
@@ -152,6 +152,10 @@ class TestBigQuery(Validator):
             "SELECT ROW() OVER (y ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM x WINDOW y AS (PARTITION BY CATEGORY)"
         )
 
+        self.validate_identity(
+            "SELECT item, purchases, LAST_VALUE(item) OVER (item_window ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING) AS most_popular FROM Produce WINDOW item_window AS (ORDER BY purchases)"
+        )
+
         self.validate_identity(
             "SELECT LAST_VALUE(a IGNORE NULLS) OVER y FROM x WINDOW y AS (PARTITION BY CATEGORY)",
         )
diff --git a/tests/test_build.py b/tests/test_build.py
index 3b24b4b4..fa9e7f8f 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -1,6 +1,7 @@
 import unittest
 
 from sqlglot import (
+    alias,
     and_,
     condition,
     except_,
@@ -364,6 +365,22 @@ class TestBuild(unittest.TestCase):
                 lambda: parse_one("(SELECT * FROM foo)").union("SELECT * FROM bla", distinct=False),
                 "(SELECT * FROM foo) UNION ALL SELECT * FROM bla",
             ),
+            (
+                lambda: alias(parse_one("LAG(x) OVER (PARTITION BY y)"), "a"),
+                "LAG(x) OVER (PARTITION BY y) AS a",
+            ),
+            (
+                lambda: alias(parse_one("LAG(x) OVER (ORDER BY z)"), "a"),
+                "LAG(x) OVER (ORDER BY z) AS a",
+            ),
+            (
+                lambda: alias(parse_one("LAG(x) OVER (PARTITION BY y ORDER BY z)"), "a"),
+                "LAG(x) OVER (PARTITION BY y ORDER BY z) AS a",
+            ),
+            (
+                lambda: alias(parse_one("LAG(x) OVER ()"), "a"),
+                "LAG(x) OVER () AS a",
+            ),
         ]:
             with self.subTest(sql):
                 self.assertEqual(expression().sql(dialect[0] if dialect else None), sql)