Repository: tobymao/sqlglot

Issue:
parsing trino `interval day to second` and `interval year to month` into a type doesn't work
```
In [9]: import sqlglot as sg

In [10]: sg.__version__
Out[10]: '17.9.1'

In [11]: sg.parse_one("interval day to second", into=sg.exp.DataType, read="trino")
...
ParseError: Failed to parse 'interval day to second' into <class 'sqlglot.expressions.DataType'>

In [12]: sg.parse_one("interval year to month", into=sg.exp.DataType, read="trino")
...
ParseError: Failed to parse 'interval year to month' into <class 'sqlglot.expressions.DataType'>
```

**Is your feature request related to a problem? Please describe.**

Not related to a problem

**Describe the solution you'd like**

Ideally these can be parsed into the `INTERVAL` type

**Describe alternatives you've considered**

Not sure what the alternatives would be

**Additional context**

N/A


Discussion:

Patch:
diff --git a/sqlglot/dialects/clickhouse.py b/sqlglot/dialects/clickhouse.py
index fb22b254..53bcf5e7 100644
--- a/sqlglot/dialects/clickhouse.py
+++ b/sqlglot/dialects/clickhouse.py
@@ -37,7 +37,11 @@ class ClickHouse(Dialect):
             "ATTACH": TokenType.COMMAND,
             "DATETIME64": TokenType.DATETIME64,
             "DICTIONARY": TokenType.DICTIONARY,
+            "ENUM": TokenType.ENUM,
+            "ENUM8": TokenType.ENUM8,
+            "ENUM16": TokenType.ENUM16,
             "FINAL": TokenType.FINAL,
+            "FIXEDSTRING": TokenType.FIXEDSTRING,
             "FLOAT32": TokenType.FLOAT,
             "FLOAT64": TokenType.DOUBLE,
             "GLOBAL": TokenType.GLOBAL,
@@ -46,6 +50,7 @@ class ClickHouse(Dialect):
             "INT32": TokenType.INT,
             "INT64": TokenType.BIGINT,
             "INT8": TokenType.TINYINT,
+            "LOWCARDINALITY": TokenType.LOWCARDINALITY,
             "MAP": TokenType.MAP,
             "TUPLE": TokenType.STRUCT,
             "UINT128": TokenType.UINT128,
@@ -293,10 +298,15 @@ class ClickHouse(Dialect):
             exp.DataType.Type.BIGINT: "Int64",
             exp.DataType.Type.DATETIME64: "DateTime64",
             exp.DataType.Type.DOUBLE: "Float64",
+            exp.DataType.Type.ENUM: "Enum",
+            exp.DataType.Type.ENUM8: "Enum8",
+            exp.DataType.Type.ENUM16: "Enum16",
+            exp.DataType.Type.FIXEDSTRING: "FixedString",
             exp.DataType.Type.FLOAT: "Float32",
             exp.DataType.Type.INT: "Int32",
             exp.DataType.Type.INT128: "Int128",
             exp.DataType.Type.INT256: "Int256",
+            exp.DataType.Type.LOWCARDINALITY: "LowCardinality",
             exp.DataType.Type.MAP: "Map",
             exp.DataType.Type.NULLABLE: "Nullable",
             exp.DataType.Type.SMALLINT: "Int16",
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
index edd72ea6..fedf7e36 100644
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -3398,23 +3398,16 @@ class DataType(Expression):
         BOOLEAN = auto()
         CHAR = auto()
         DATE = auto()
+        DATEMULTIRANGE = auto()
+        DATERANGE = auto()
         DATETIME = auto()
         DATETIME64 = auto()
-        ENUM = auto()
-        INT4RANGE = auto()
-        INT4MULTIRANGE = auto()
-        INT8RANGE = auto()
-        INT8MULTIRANGE = auto()
-        NUMRANGE = auto()
-        NUMMULTIRANGE = auto()
-        TSRANGE = auto()
-        TSMULTIRANGE = auto()
-        TSTZRANGE = auto()
-        TSTZMULTIRANGE = auto()
-        DATERANGE = auto()
-        DATEMULTIRANGE = auto()
         DECIMAL = auto()
         DOUBLE = auto()
+        ENUM = auto()
+        ENUM8 = auto()
+        ENUM16 = auto()
+        FIXEDSTRING = auto()
         FLOAT = auto()
         GEOGRAPHY = auto()
         GEOMETRY = auto()
@@ -3422,16 +3415,21 @@ class DataType(Expression):
         HSTORE = auto()
         IMAGE = auto()
         INET = auto()
-        IPADDRESS = auto()
-        IPPREFIX = auto()
         INT = auto()
         INT128 = auto()
         INT256 = auto()
+        INT4MULTIRANGE = auto()
+        INT4RANGE = auto()
+        INT8MULTIRANGE = auto()
+        INT8RANGE = auto()
         INTERVAL = auto()
+        IPADDRESS = auto()
+        IPPREFIX = auto()
         JSON = auto()
         JSONB = auto()
         LONGBLOB = auto()
         LONGTEXT = auto()
+        LOWCARDINALITY = auto()
         MAP = auto()
         MEDIUMBLOB = auto()
         MEDIUMTEXT = auto()
@@ -3439,6 +3437,8 @@ class DataType(Expression):
         NCHAR = auto()
         NULL = auto()
         NULLABLE = auto()
+        NUMMULTIRANGE = auto()
+        NUMRANGE = auto()
         NVARCHAR = auto()
         OBJECT = auto()
         ROWVERSION = auto()
@@ -3452,18 +3452,22 @@ class DataType(Expression):
         TEXT = auto()
         TIME = auto()
         TIMESTAMP = auto()
-        TIMESTAMPTZ = auto()
         TIMESTAMPLTZ = auto()
+        TIMESTAMPTZ = auto()
         TINYINT = auto()
+        TSMULTIRANGE = auto()
+        TSRANGE = auto()
+        TSTZMULTIRANGE = auto()
+        TSTZRANGE = auto()
         UBIGINT = auto()
         UINT = auto()
-        USMALLINT = auto()
-        UTINYINT = auto()
-        UNKNOWN = auto()  # Sentinel value, useful for type annotation
         UINT128 = auto()
         UINT256 = auto()
         UNIQUEIDENTIFIER = auto()
+        UNKNOWN = auto()  # Sentinel value, useful for type annotation
         USERDEFINED = "USER-DEFINED"
+        USMALLINT = auto()
+        UTINYINT = auto()
         UUID = auto()
         VARBINARY = auto()
         VARCHAR = auto()
@@ -3859,6 +3863,18 @@ class TimeUnit(Expression):
         super().__init__(**args)
 
 
+# https://www.oracletutorial.com/oracle-basics/oracle-interval/
+# https://trino.io/docs/current/language/types.html#interval-year-to-month
+class IntervalYearToMonthSpan(Expression):
+    arg_types = {}
+
+
+# https://www.oracletutorial.com/oracle-basics/oracle-interval/
+# https://trino.io/docs/current/language/types.html#interval-day-to-second
+class IntervalDayToSecondSpan(Expression):
+    arg_types = {}
+
+
 class Interval(TimeUnit):
     arg_types = {"this": False, "unit": False}
 
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
index 0ab960ac..0c877162 100644
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -71,6 +71,8 @@ class Generator:
         exp.ExternalProperty: lambda self, e: "EXTERNAL",
         exp.HeapProperty: lambda self, e: "HEAP",
         exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
+        exp.IntervalDayToSecondSpan: "DAY TO SECOND",
+        exp.IntervalYearToMonthSpan: "YEAR TO MONTH",
         exp.LanguageProperty: lambda self, e: self.naked_property(e),
         exp.LocationProperty: lambda self, e: self.naked_property(e),
         exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
@@ -848,6 +850,8 @@ class Generator:
                     delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
                     values = self.expressions(expression, key="values", flat=True)
                     values = f"{delimiters[0]}{values}{delimiters[1]}"
+            elif type_value == exp.DataType.Type.INTERVAL:
+                nested = f" {interior}"
             else:
                 nested = f"({interior})"
 
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 18471480..034d8386 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -104,6 +104,7 @@ class Parser(metaclass=_Parser):
 
     NESTED_TYPE_TOKENS = {
         TokenType.ARRAY,
+        TokenType.LOWCARDINALITY,
         TokenType.MAP,
         TokenType.NULLABLE,
         TokenType.STRUCT,
@@ -111,6 +112,8 @@ class Parser(metaclass=_Parser):
 
     ENUM_TYPE_TOKENS = {
         TokenType.ENUM,
+        TokenType.ENUM8,
+        TokenType.ENUM16,
     }
 
     TYPE_TOKENS = {
@@ -128,6 +131,7 @@ class Parser(metaclass=_Parser):
         TokenType.UINT128,
         TokenType.INT256,
         TokenType.UINT256,
+        TokenType.FIXEDSTRING,
         TokenType.FLOAT,
         TokenType.DOUBLE,
         TokenType.CHAR,
@@ -187,7 +191,7 @@ class Parser(metaclass=_Parser):
         TokenType.INET,
         TokenType.IPADDRESS,
         TokenType.IPPREFIX,
-        TokenType.ENUM,
+        *ENUM_TYPE_TOKENS,
         *NESTED_TYPE_TOKENS,
     }
 
@@ -3125,7 +3129,7 @@ class Parser(metaclass=_Parser):
                     lambda: self._parse_types(check_func=check_func, schema=schema)
                 )
             elif type_token in self.ENUM_TYPE_TOKENS:
-                expressions = self._parse_csv(self._parse_primary)
+                expressions = self._parse_csv(self._parse_equality)
             else:
                 expressions = self._parse_csv(self._parse_type_size)
 
@@ -3184,10 +3188,18 @@ class Parser(metaclass=_Parser):
             elif self._match_text_seq("WITHOUT", "TIME", "ZONE"):
                 maybe_func = False
         elif type_token == TokenType.INTERVAL:
-            unit = self._parse_var()
+            if self._match_text_seq("YEAR", "TO", "MONTH"):
+                span: t.Optional[t.List[exp.Expression]] = [exp.IntervalYearToMonthSpan()]
+            elif self._match_text_seq("DAY", "TO", "SECOND"):
+                span = [exp.IntervalDayToSecondSpan()]
+            else:
+                span = None
 
+            unit = not span and self._parse_var()
             if not unit:
-                value = self.expression(exp.DataType, this=exp.DataType.Type.INTERVAL)
+                value = self.expression(
+                    exp.DataType, this=exp.DataType.Type.INTERVAL, expressions=span
+                )
             else:
                 value = self.expression(exp.Interval, unit=unit)
 
diff --git a/sqlglot/tokens.py b/sqlglot/tokens.py
index 3cb8775a..0b427062 100644
--- a/sqlglot/tokens.py
+++ b/sqlglot/tokens.py
@@ -151,6 +151,10 @@ class TokenType(AutoName):
     IPADDRESS = auto()
     IPPREFIX = auto()
     ENUM = auto()
+    ENUM8 = auto()
+    ENUM16 = auto()
+    FIXEDSTRING = auto()
+    LOWCARDINALITY = auto()
 
     # keywords
     ALIAS = auto()


Test patch:
diff --git a/tests/dialects/test_clickhouse.py b/tests/dialects/test_clickhouse.py
index 16c10feb..4b1406df 100644
--- a/tests/dialects/test_clickhouse.py
+++ b/tests/dialects/test_clickhouse.py
@@ -10,6 +10,12 @@ class TestClickhouse(Validator):
         self.assertEqual(expr.sql(dialect="clickhouse"), "COUNT(x)")
         self.assertIsNone(expr._meta)
 
+        self.validate_identity("CAST(x AS Enum('hello' = 1, 'world' = 2))")
+        self.validate_identity("CAST(x AS Enum('hello', 'world'))")
+        self.validate_identity("CAST(x AS Enum('hello' = 1, 'world'))")
+        self.validate_identity("CAST(x AS Enum8('hello' = -123, 'world'))")
+        self.validate_identity("CAST(x AS FixedString(1))")
+        self.validate_identity("CAST(x AS LowCardinality(FixedString))")
         self.validate_identity("SELECT isNaN(1.0)")
         self.validate_identity("SELECT startsWith('Spider-Man', 'Spi')")
         self.validate_identity("SELECT xor(TRUE, FALSE)")
diff --git a/tests/dialects/test_presto.py b/tests/dialects/test_presto.py
index ec1ad307..97263fa2 100644
--- a/tests/dialects/test_presto.py
+++ b/tests/dialects/test_presto.py
@@ -8,6 +8,23 @@ class TestPresto(Validator):
     dialect = "presto"
 
     def test_cast(self):
+        self.validate_identity("CAST(x AS IPADDRESS)")
+        self.validate_identity("CAST(x AS IPPREFIX)")
+
+        self.validate_all(
+            "CAST(x AS INTERVAL YEAR TO MONTH)",
+            write={
+                "oracle": "CAST(x AS INTERVAL YEAR TO MONTH)",
+                "presto": "CAST(x AS INTERVAL YEAR TO MONTH)",
+            },
+        )
+        self.validate_all(
+            "CAST(x AS INTERVAL DAY TO SECOND)",
+            write={
+                "oracle": "CAST(x AS INTERVAL DAY TO SECOND)",
+                "presto": "CAST(x AS INTERVAL DAY TO SECOND)",
+            },
+        )
         self.validate_all(
             "SELECT CAST('10C' AS INTEGER)",
             read={
@@ -109,8 +126,6 @@ class TestPresto(Validator):
                 "spark": "CAST(x AS TIMESTAMP)",
             },
         )
-        self.validate_identity("CAST(x AS IPADDRESS)")
-        self.validate_identity("CAST(x AS IPPREFIX)")
 
     def test_regex(self):
         self.validate_all(