Repository: tobymao/sqlglot

Issue:
Possible regression when parsing complex types with databricks dialect
The following code will throw `sqlglot.errors.ParseError: Expecting >`, even though it was previously working.

```python
import sqlglot

sql = """
CREATE TABLE IF NOT EXISTS db.table (
  a ARRAY<
    STRUCT<
      b INT COMMENT 'nested comment',
      c: MAP<STRING,STRING>
    >
  > COMMENT 'complex type'
)
USING DELTA
"""

sqlglot.parse_one(sql, read="databricks")
```

This code works until version `23.16.0` and fails starting from version `23.17.0` (and including the latest version, `24.0.0`).

#### Additional notes

In addition to the behavior described above, version `23.17.0` also changed the result of parsing complex types, even when the parsing does not fail:

```python
from sqlglot import exp, parse_one

sql = """
CREATE TABLE db.table (
  area STRUCT<
    base: INT COMMENT 'a nested int',
    height: INT NOT NULL COMMENT 'another nested int'
  > NOT NULL COMMENT 'a struct column'
)
USING DELTA
"""

parsed_sql = parse_one(sql, read="databricks")
exp_schema = parsed_sql.find(exp.Schema, bfs=True)
column_def = exp_schema.find(exp.ColumnDef, bfs=True)
column_type = column_def.find(exp.DataType, bfs=True)

nested_type = column_type.find(exp.ColumnDef, bfs=True)
print([type(x) for x in nested_type.iter_expressions()])
```

Running the above code, the output with version `23.16.0` is

```
[<class 'sqlglot.expressions.Identifier'>, <class 'sqlglot.expressions.DataType'>, <class 'sqlglot.expressions.ColumnConstraint'>]
```

With version `23.17.0` instead, it is

```
[<class 'sqlglot.expressions.JSONExtract'>, <class 'sqlglot.expressions.ColumnConstraint'>]
```

Discussion:

Patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 3237cd1c..1cd5b79c 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -4060,7 +4060,7 @@ class Parser(metaclass=_Parser):
             return this
         return self.expression(exp.Escape, this=this, expression=self._parse_string())
 
-    def _parse_interval(self, match_interval: bool = True) -> t.Optional[exp.Interval]:
+    def _parse_interval(self, match_interval: bool = True) -> t.Optional[exp.Add | exp.Interval]:
         index = self._index
 
         if not self._match(TokenType.INTERVAL) and match_interval:
@@ -4106,7 +4106,19 @@ class Parser(metaclass=_Parser):
                 exp.IntervalSpan, this=unit, expression=self._parse_var(any_token=True, upper=True)
             )
 
-        return self.expression(exp.Interval, this=this, unit=unit)
+        interval = self.expression(exp.Interval, this=this, unit=unit)
+
+        index = self._index
+        self._match(TokenType.PLUS)
+
+        # Convert INTERVAL 'val_1' unit_1 [+] ... [+] 'val_n' unit_n into a sum of intervals
+        if self._match_set((TokenType.STRING, TokenType.NUMBER), advance=False):
+            return self.expression(
+                exp.Add, this=interval, expression=self._parse_interval(match_interval=False)
+            )
+
+        self._retreat(index)
+        return interval
 
     def _parse_bitwise(self) -> t.Optional[exp.Expression]:
         this = self._parse_term()
@@ -4173,38 +4185,32 @@ class Parser(metaclass=_Parser):
     ) -> t.Optional[exp.Expression]:
         interval = parse_interval and self._parse_interval()
         if interval:
-            # Convert INTERVAL 'val_1' unit_1 [+] ... [+] 'val_n' unit_n into a sum of intervals
-            while True:
-                index = self._index
-                self._match(TokenType.PLUS)
-
-                if not self._match_set((TokenType.STRING, TokenType.NUMBER), advance=False):
-                    self._retreat(index)
-                    break
-
-                interval = self.expression(  # type: ignore
-                    exp.Add, this=interval, expression=self._parse_interval(match_interval=False)
-                )
-
             return interval
 
         index = self._index
         data_type = self._parse_types(check_func=True, allow_identifiers=False)
-        this = self._parse_column()
 
         if data_type:
+            index2 = self._index
+            this = self._parse_primary()
+
             if isinstance(this, exp.Literal):
                 parser = self.TYPE_LITERAL_PARSERS.get(data_type.this)
                 if parser:
                     return parser(self, this, data_type)
+
                 return self.expression(exp.Cast, this=this, to=data_type)
 
-            if not data_type.expressions:
-                self._retreat(index)
-                return self._parse_id_var() if fallback_to_identifier else self._parse_column()
+            if data_type.expressions:
+                self._retreat(index2)
+                return self._parse_column_ops(data_type)
 
-            return self._parse_column_ops(data_type)
+            self._retreat(index)
 
+        if fallback_to_identifier:
+            return self._parse_id_var()
+
+        this = self._parse_column()
         return this and self._parse_column_ops(this)
 
     def _parse_type_size(self) -> t.Optional[exp.DataTypeParam]:


Test patch:
diff --git a/tests/dialects/test_databricks.py b/tests/dialects/test_databricks.py
index f050cfa9..4150d32a 100644
--- a/tests/dialects/test_databricks.py
+++ b/tests/dialects/test_databricks.py
@@ -9,6 +9,7 @@ class TestDatabricks(Validator):
     def test_databricks(self):
         self.validate_identity("DESCRIBE HISTORY a.b")
         self.validate_identity("DESCRIBE history.tbl")
+        self.validate_identity("CREATE TABLE t (a STRUCT<c: MAP<STRING, STRING>>)")
         self.validate_identity("CREATE TABLE t (c STRUCT<interval: DOUBLE COMMENT 'aaa'>)")
         self.validate_identity("CREATE TABLE my_table TBLPROPERTIES (a.b=15)")
         self.validate_identity("CREATE TABLE my_table TBLPROPERTIES ('a.b'=15)")