Repository: tobymao/sqlglot

Issue:
Can't parse `trim` in TrinoSQL
**Fully reproducible code snippet**
Please include a fully reproducible code snippet or the input sql, dialect, and expected output.
```python
import sqlglot
print(sqlglot.__version__)
sql = "SELECT trim(',' FROM some_col);"
result = sqlglot.parse(sql, read="trino")
print(repr(result))
```

Expected:
```
23.12.2
[Select(
  expressions=[
    Trim(
      this=Column(
        this=Identifier(this=some_col, quoted=False)),
      expression=Literal(this=,, is_string=True))])]
```

Got:
```
23.12.2
Traceback (most recent call last):
  File "proof.py", line 7, in <module>
    result = sqlglot.parse(sql, read="trino")
  File ".../python3.8/site-packages/sqlglot/__init__.py", line 102, in parse
    return Dialect.get_or_raise(read or dialect).parse(sql, **opts)
  File ".../python3.8/site-packages/sqlglot/dialects/dialect.py", line 506, in parse
    return self.parser(**opts).parse(self.tokenize(sql), sql)
  File ".../python3.8/site-packages/sqlglot/parser.py", line 1175, in parse
    return self._parse(
  File ".../python3.8/site-packages/sqlglot/parser.py", line 1241, in _parse
    expressions.append(parse_method(self))
  File ".../python3.8/site-packages/sqlglot/parser.py", line 1476, in _parse_statement
    expression = self._parse_set_operations(expression) if expression else self._parse_select()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 2532, in _parse_select
    projections = self._parse_projections()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 2480, in _parse_projections
    return self._parse_expressions()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 5695, in _parse_expressions
    return self._parse_csv(self._parse_expression)
  File ".../python3.8/site-packages/sqlglot/parser.py", line 5649, in _parse_csv
    parse_result = parse_method()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 3805, in _parse_expression
    return self._parse_alias(self._parse_conjunction())
  File ".../python3.8/site-packages/sqlglot/parser.py", line 3808, in _parse_conjunction
    return self._parse_tokens(self._parse_equality, self.CONJUNCTION)
  File ".../python3.8/site-packages/sqlglot/parser.py", line 5663, in _parse_tokens
    this = parse_method()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 3811, in _parse_equality
    return self._parse_tokens(self._parse_comparison, self.EQUALITY)
  File ".../python3.8/site-packages/sqlglot/parser.py", line 5663, in _parse_tokens
    this = parse_method()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 3814, in _parse_comparison
    return self._parse_tokens(self._parse_range, self.COMPARISON)
  File ".../python3.8/site-packages/sqlglot/parser.py", line 5663, in _parse_tokens
    this = parse_method()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 3817, in _parse_range
    this = this or self._parse_bitwise()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 3941, in _parse_bitwise
    this = self._parse_term()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 3973, in _parse_term
    return self._parse_tokens(self._parse_factor, self.TERM)
  File ".../python3.8/site-packages/sqlglot/parser.py", line 5663, in _parse_tokens
    this = parse_method()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 3977, in _parse_factor
    this = parse_method()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 3998, in _parse_unary
    return self._parse_at_time_zone(self._parse_type())
  File ".../python3.8/site-packages/sqlglot/parser.py", line 4020, in _parse_type
    this = self._parse_column()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 4220, in _parse_column
    this = self._parse_column_reference()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 4224, in _parse_column_reference
    this = self._parse_field()
  File ".../python3.8/site-packages/sqlglot/parser.py", line 4347, in _parse_field
    field = self._parse_primary() or self._parse_function(
  File ".../python3.8/site-packages/sqlglot/parser.py", line 4370, in _parse_function
    func = self._parse_function_call(
  File ".../python3.8/site-packages/sqlglot/parser.py", line 4458, in _parse_function_call
    self._match_r_paren(this)
  File ".../python3.8/site-packages/sqlglot/parser.py", line 6196, in _match_r_paren
    self.raise_error("Expecting )")
  File ".../python3.8/site-packages/sqlglot/parser.py", line 1285, in raise_error
    raise error
sqlglot.errors.ParseError: Expecting ). Line 1, Col: 20.
  SELECT trim(',' FROM some_col);
```

**Official Documentation**
https://trino.io/docs/current/functions/string.html?highlight=trim#trim


Discussion:

Resolving patch:
diff --git a/sqlglot/dialects/trino.py b/sqlglot/dialects/trino.py
--- a/sqlglot/dialects/trino.py
+++ b/sqlglot/dialects/trino.py
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 from sqlglot import exp
-from sqlglot.dialects.dialect import merge_without_target_sql
+from sqlglot.dialects.dialect import merge_without_target_sql, trim_sql
 from sqlglot.dialects.presto import Presto
 
 
@@ -9,12 +9,19 @@ class Trino(Presto):
     SUPPORTS_USER_DEFINED_TYPES = False
     LOG_BASE_FIRST = True
 
+    class Parser(Presto.Parser):
+        FUNCTION_PARSERS = {
+            **Presto.Parser.FUNCTION_PARSERS,
+            "TRIM": lambda self: self._parse_trim(),
+        }
+
     class Generator(Presto.Generator):
         TRANSFORMS = {
             **Presto.Generator.TRANSFORMS,
             exp.ArraySum: lambda self,
             e: f"REDUCE({self.sql(e, 'this')}, 0, (acc, x) -> acc + x, acc -> acc)",
             exp.Merge: merge_without_target_sql,
+            exp.Trim: trim_sql,
         }
 
         SUPPORTED_JSON_PATH_PARTS = {


Test patch:
diff --git a/tests/dialects/test_trino.py b/tests/dialects/test_trino.py
new file mode 100644
--- /dev/null
+++ b/tests/dialects/test_trino.py
@@ -0,0 +1,18 @@
+from tests.dialects.test_dialect import Validator
+
+
+class TestTrino(Validator):
+    dialect = "trino"
+
+    def test_trim(self):
+        self.validate_identity("SELECT TRIM('!' FROM '!foo!')")
+        self.validate_identity("SELECT TRIM(BOTH '$' FROM '$var$')")
+        self.validate_identity("SELECT TRIM(TRAILING 'ER' FROM UPPER('worker'))")
+        self.validate_identity(
+            "SELECT TRIM(LEADING FROM '  abcd')",
+            "SELECT LTRIM('  abcd')",
+        )
+        self.validate_identity(
+            "SELECT TRIM('!foo!', '!')",
+            "SELECT TRIM('!' FROM '!foo!')",
+        )