Repository: tobymao/sqlglot

Issue:
Regression in interval parsing case after #1413
This seems to be a regression caused by https://github.com/tobymao/sqlglot/pull/1413 which was recently merged.

On sqlglot 11.5.4, if the interval unit is inside the interval value, and there's an expression after it, the expression after is dropped:

```
>>> sqlglot.transpile("select interval '1 days' * 5", read="spark", write="trino")
["SELECT INTERVAL '1' days"]
>>> sqlglot.parse_one("select interval '1 day' * 5", read="spark")
(SELECT expressions:
  (INTERVAL this:
    (LITERAL this: 1, is_string: True), unit:
    (IDENTIFIER this: day, quoted: False)))
```

These work, though! It doesn't happen if the expression is before, or if the unit is outside the quotes.
```
>>> sqlglot.transpile("select interval '1' day * 5", read="spark", write="trino")
["SELECT INTERVAL '1' day * 5"]
>>> sqlglot.transpile("select interval 5 * '1 day'", read="spark", write="trino")
["SELECT INTERVAL '5'"]
>>> sqlglot.transpile("select 5 * interval '1 day'", read="spark", write="trino")
["SELECT 5 * INTERVAL '1' day"]
```

Using sqlglot 11.5.3, the expression is not dropped:

```
>>> sqlglot.transpile("select 5 * interval '1 day'", read="spark", write="trino")
["SELECT 5 * INTERVAL '1 day'"]
>>> sqlglot.transpile("select interval '1 day' * 5", read="spark", write="trino")
["SELECT INTERVAL ('1 day' * 5)"]
```


Discussion:
Again, thanks so much for this excellent tool. Let me know if I can help with the implementation. As you can see, the interval is important part in the queries I'm transpiling 😅 
Thanks for the issues, I'm gonna take a look.
FYI: one of the test cases doesn't seem like valid Spark SQL:

```python
➜  sqlglot git:(main) ✗ spark-sql -e "select interval 5 * '1 day'"
Error in query:
Syntax error at or near '*'(line 1, pos 18)

== SQL ==
select interval 5 * '1 day'
------------------^^^
```

Resolving patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -2573,8 +2573,10 @@ def _parse_interval(self) -> t.Optional[exp.Expression]:
         if not self._match(TokenType.INTERVAL):
             return None
 
-        this = self._parse_term()
-        unit = self._parse_field(tokens=self.INTERVAL_VARS)
+        this = self._parse_primary()
+        unit = self._parse_function() or self._parse_id_var(
+            any_token=False, tokens=self.INTERVAL_VARS
+        )
 
         # Most dialects support, e.g., the form INTERVAL '5' day, thus we try to parse
         # each INTERVAL expression into this canonical form so it's easy to transpile


Test patch:
diff --git a/tests/fixtures/identity.sql b/tests/fixtures/identity.sql
--- a/tests/fixtures/identity.sql
+++ b/tests/fixtures/identity.sql
@@ -129,6 +129,8 @@ INTERVAL '1' MONTH
 INTERVAL '-1' CURRENT_DATE
 INTERVAL '-31' CAST(GETDATE() AS DATE)
 INTERVAL (1 + 3) DAYS
+INTERVAL '1' day * 5
+5 * INTERVAL '1' day
 CASE WHEN TRUE THEN INTERVAL '15' days END
 CAST('45' AS INTERVAL DAYS)
 FILTER(a, x -> x.a.b.c.d.e.f.g)
diff --git a/tests/test_transpile.py b/tests/test_transpile.py
--- a/tests/test_transpile.py
+++ b/tests/test_transpile.py
@@ -333,6 +333,8 @@ def test_alter(self):
 
     def test_time(self):
         self.validate("INTERVAL '1 day'", "INTERVAL '1' day")
+        self.validate("INTERVAL '1 days' * 5", "INTERVAL '1' days * 5")
+        self.validate("5 * INTERVAL '1 day'", "5 * INTERVAL '1' day")
         self.validate("INTERVAL 1 day", "INTERVAL '1' day")
         self.validate("INTERVAL 2 months", "INTERVAL '2' months")
         self.validate("TIMESTAMP '2020-01-01'", "CAST('2020-01-01' AS TIMESTAMP)")