Repository: tobymao/sqlglot

Issue:
Trino/Presto doesn't have plural interval units
It's not valid to say `interval '2' days` in Trino, it has to be `interval '1' day`. Postgres and others support both `day` and `days`, however, but this is not (yet) handled by SQLGlot.

So ideally this would work
```python
assert "SELECT INTERVAL '2' day" == sqlglot.transpile("SELECT INTERVAL '2' days", read="postgres", write="trino")
```


Discussion:

Resolving patch:
diff --git a/sqlglot/dialects/bigquery.py b/sqlglot/dialects/bigquery.py
--- a/sqlglot/dialects/bigquery.py
+++ b/sqlglot/dialects/bigquery.py
@@ -206,6 +206,10 @@ class Parser(parser.Parser):
         LOG_DEFAULTS_TO_LN = True
 
     class Generator(generator.Generator):
+        EXPLICIT_UNION = True
+        INTERVAL_ALLOWS_PLURAL_FORM = False
+        LIMIT_FETCH = "LIMIT"
+
         TRANSFORMS = {
             **generator.Generator.TRANSFORMS,  # type: ignore
             **transforms.REMOVE_PRECISION_PARAMETERIZED_TYPES,  # type: ignore
@@ -263,14 +267,12 @@ class Generator(generator.Generator):
             exp.DataType.Type.TINYINT: "INT64",
             exp.DataType.Type.VARCHAR: "STRING",
         }
+
         PROPERTIES_LOCATION = {
             **generator.Generator.PROPERTIES_LOCATION,  # type: ignore
             exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA,
         }
 
-        EXPLICIT_UNION = True
-        LIMIT_FETCH = "LIMIT"
-
         def array_sql(self, expression: exp.Array) -> str:
             first_arg = seq_get(expression.expressions, 0)
             if isinstance(first_arg, exp.Subqueryable):
diff --git a/sqlglot/dialects/presto.py b/sqlglot/dialects/presto.py
--- a/sqlglot/dialects/presto.py
+++ b/sqlglot/dialects/presto.py
@@ -231,6 +231,7 @@ class Parser(parser.Parser):
         FUNCTION_PARSERS.pop("TRIM")
 
     class Generator(generator.Generator):
+        INTERVAL_ALLOWS_PLURAL_FORM = False
         STRUCT_DELIMITER = ("(", ")")
 
         PROPERTIES_LOCATION = {
@@ -315,7 +316,7 @@ class Generator(generator.Generator):
 
         def interval_sql(self, expression: exp.Interval) -> str:
             unit = self.sql(expression, "unit")
-            if expression.this and unit == "week":
+            if expression.this and unit.lower().startswith("week"):
                 return f"({expression.this.name} * INTERVAL '7' day)"
             return super().interval_sql(expression)
 
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -114,6 +114,9 @@ class Generator:
     # Whether or not the INTERVAL expression works only with values like '1 day'
     SINGLE_STRING_INTERVAL = False
 
+    # Whether or not the plural form of date parts like day (i.e. "days") is supported in INTERVALs
+    INTERVAL_ALLOWS_PLURAL_FORM = True
+
     # Whether or not the TABLESAMPLE clause supports a method name, like BERNOULLI
     TABLESAMPLE_WITH_METHOD = True
 
@@ -138,6 +141,18 @@ class Generator:
         "replace": "REPLACE",
     }
 
+    TIME_PART_SINGULARS = {
+        "microseconds": "microsecond",
+        "seconds": "second",
+        "minutes": "minute",
+        "hours": "hour",
+        "days": "day",
+        "weeks": "week",
+        "months": "month",
+        "quarters": "quarter",
+        "years": "year",
+    }
+
     TOKEN_MAPPING: t.Dict[TokenType, str] = {}
 
     STRUCT_DELIMITER = ("<", ">")
@@ -1677,6 +1692,8 @@ def in_unnest_op(self, unnest: exp.Unnest) -> str:
 
     def interval_sql(self, expression: exp.Interval) -> str:
         unit = self.sql(expression, "unit")
+        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
+            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
         unit = f" {unit}" if unit else ""
 
         if self.SINGLE_STRING_INTERVAL:


Test patch:
diff --git a/tests/dialects/test_postgres.py b/tests/dialects/test_postgres.py
--- a/tests/dialects/test_postgres.py
+++ b/tests/dialects/test_postgres.py
@@ -157,8 +157,8 @@ def test_postgres(self):
             "GENERATE_SERIES(a, b, '  2   days  ')",
             write={
                 "postgres": "GENERATE_SERIES(a, b, INTERVAL '2' days)",
-                "presto": "SEQUENCE(a, b, INTERVAL '2' days)",
-                "trino": "SEQUENCE(a, b, INTERVAL '2' days)",
+                "presto": "SEQUENCE(a, b, INTERVAL '2' day)",
+                "trino": "SEQUENCE(a, b, INTERVAL '2' day)",
             },
         )
         self.validate_all(
diff --git a/tests/dialects/test_presto.py b/tests/dialects/test_presto.py
--- a/tests/dialects/test_presto.py
+++ b/tests/dialects/test_presto.py
@@ -106,7 +106,33 @@ def test_regex(self):
             },
         )
 
+    def test_interval_plural_to_singular(self):
+        # Microseconds, weeks and quarters are not supported in Presto/Trino INTERVAL literals
+        unit_to_expected = {
+            "SeCoNds": "second",
+            "minutes": "minute",
+            "hours": "hour",
+            "days": "day",
+            "months": "month",
+            "years": "year",
+        }
+
+        for unit, expected in unit_to_expected.items():
+            self.validate_all(
+                f"SELECT INTERVAL '1' {unit}",
+                write={
+                    "bigquery": f"SELECT INTERVAL '1' {expected}",
+                    "presto": f"SELECT INTERVAL '1' {expected}",
+                    "trino": f"SELECT INTERVAL '1' {expected}",
+                },
+            )
+
     def test_time(self):
+        self.validate_identity("FROM_UNIXTIME(a, b)")
+        self.validate_identity("FROM_UNIXTIME(a, b, c)")
+        self.validate_identity("TRIM(a, b)")
+        self.validate_identity("VAR_POP(a)")
+
         self.validate_all(
             "DATE_FORMAT(x, '%Y-%m-%d %H:%i:%S')",
             write={
@@ -158,10 +184,6 @@ def test_time(self):
                 "spark": "FROM_UNIXTIME(x)",
             },
         )
-        self.validate_identity("FROM_UNIXTIME(a, b)")
-        self.validate_identity("FROM_UNIXTIME(a, b, c)")
-        self.validate_identity("TRIM(a, b)")
-        self.validate_identity("VAR_POP(a)")
         self.validate_all(
             "TO_UNIXTIME(x)",
             write={
@@ -379,6 +401,7 @@ def test_presto(self):
 
         self.validate_all("INTERVAL '1 day'", write={"trino": "INTERVAL '1' day"})
         self.validate_all("(5 * INTERVAL '7' day)", read={"": "INTERVAL '5' week"})
+        self.validate_all("(5 * INTERVAL '7' day)", read={"": "INTERVAL '5' WEEKS"})
         self.validate_all(
             "SELECT JSON_OBJECT(KEY 'key1' VALUE 1, KEY 'key2' VALUE TRUE)",
             write={