Repository: tobymao/sqlglot

Issue:
ClickHouse doesn't allow plurals for interval units
We currently have the following transformation:

```py
        elif isinstance(node, exp.Interval) and isinstance(node.unit, exp.Var) and node.unit.this.endswith(('s', 'S')):
            # clickhouse requires interval units not to be plural, note this wouldn't work for "centuries",
            # but CH doesn't support that anyway
            return exp.Interval(this=node.this, unit=exp.Literal.string(node.unit.this[:-1]))
```

[Clickhouse documentation](https://clickhouse.com/docs/en/sql-reference/data-types/special-data-types/interval)

types supported:
* `NANOSECOND`
* `MICROSECOND`
* `MILLISECOND`
* `SECOND`
* `MINUTE`
* `HOUR`
* `DAY`
* `WEEK`
* `MONTH`
* `QUARTER`
* `YEAR`

[Postgres documentation](https://www.postgresql.org/docs/current/datatype-datetime.html#:~:text=unit%20is%20microsecond%2C%20millisecond%2C%20second%2C%20minute%2C%20hour%2C%20day%2C%20week%2C%20month%2C%20year%2C%20decade%2C%20century%2C%20millennium%2C%20or%20abbreviations%20or%20plurals%20of%20these%20units):

> `unit` is `microsecond`, `millisecond`, `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `decade`, `century`, `millennium`, or abbreviations or plurals of these units

Technically there's also the issue that `decade`, `century` and `millennium` won't be supported by Clickhouse although they are else where, but that's not a big issue for us.


Discussion:

Patch:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
index 70bb0e35..2d1ebc2d 100644
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -4069,10 +4069,25 @@ class TimeUnit(Expression):
 
     arg_types = {"unit": False}
 
+    UNABBREVIATED_UNIT_NAME = {
+        "d": "day",
+        "h": "hour",
+        "m": "minute",
+        "ms": "millisecond",
+        "ns": "nanosecond",
+        "q": "quarter",
+        "s": "second",
+        "us": "microsecond",
+        "w": "week",
+        "y": "year",
+    }
+
+    VAR_LIKE = (Column, Literal, Var)
+
     def __init__(self, **args):
         unit = args.get("unit")
-        if isinstance(unit, (Column, Literal)):
-            args["unit"] = Var(this=unit.name)
+        if isinstance(unit, self.VAR_LIKE):
+            args["unit"] = Var(this=self.UNABBREVIATED_UNIT_NAME.get(unit.name) or unit.name)
         elif isinstance(unit, Week):
             unit.set("this", Var(this=unit.this.name))
 


Test patch:
diff --git a/tests/dialects/test_clickhouse.py b/tests/dialects/test_clickhouse.py
index e50eee68..93d1cede 100644
--- a/tests/dialects/test_clickhouse.py
+++ b/tests/dialects/test_clickhouse.py
@@ -101,6 +101,13 @@ class TestClickhouse(Validator):
             "CREATE MATERIALIZED VIEW test_view (id UInt8) TO db.table1 AS SELECT * FROM test_data"
         )
 
+        self.validate_all(
+            "SELECT CAST('2020-01-01' AS TIMESTAMP) + INTERVAL '500' microsecond",
+            read={
+                "duckdb": "SELECT TIMESTAMP '2020-01-01' + INTERVAL '500 us'",
+                "postgres": "SELECT TIMESTAMP '2020-01-01' + INTERVAL '500 us'",
+            },
+        )
         self.validate_all(
             "SELECT CURRENT_DATE()",
             read={
diff --git a/tests/test_expressions.py b/tests/test_expressions.py
index 85922e19..6c489433 100644
--- a/tests/test_expressions.py
+++ b/tests/test_expressions.py
@@ -632,6 +632,11 @@ class TestExpressions(unittest.TestCase):
         week = unit.find(exp.Week)
         self.assertEqual(week.this, exp.var("thursday"))
 
+        for abbreviated_unit, unnabreviated_unit in exp.TimeUnit.UNABBREVIATED_UNIT_NAME.items():
+            interval = parse_one(f"interval '500 {abbreviated_unit}'")
+            self.assertIsInstance(interval.unit, exp.Var)
+            self.assertEqual(interval.unit.name, unnabreviated_unit)
+
     def test_identifier(self):
         self.assertTrue(exp.to_identifier('"x"').quoted)
         self.assertFalse(exp.to_identifier("x").quoted)