nebius/SWE-bench-extra huggingface dataset:
Repository: tobymao/sqlglot

Issue:
Empty map error
**Fully reproducible code snippet**
```python
from sqlglot import transpile
transpile("SELECT map()", read="trino", write="spark")
```
Raises the exception:
```
KeyError: 'keys'
```
When it should just transpile as:
```python
'SELECT map()'
```

**Official Documentation**
[Trino map](https://trino.io/docs/current/functions/map.html?highlight=map#map)
[SparkSQL map](https://spark.apache.org/docs/latest/api/sql/index.html#map)

Comments:

Patch:
diff --git a/sqlglot/dialects/spark2.py b/sqlglot/dialects/spark2.py
index b8ba78ba..83d1684c 100644
--- a/sqlglot/dialects/spark2.py
+++ b/sqlglot/dialects/spark2.py
@@ -32,9 +32,13 @@ def _create_sql(self: Hive.Generator, e: exp.Create) -> str:
 
 
 def _map_sql(self: Hive.Generator, expression: exp.Map) -> str:
-    keys = self.sql(expression.args["keys"])
-    values = self.sql(expression.args["values"])
-    return f"MAP_FROM_ARRAYS({keys}, {values})"
+    keys = expression.args.get("keys")
+    values = expression.args.get("values")
+
+    if not keys or not values:
+        return "MAP()"
+
+    return f"MAP_FROM_ARRAYS({self.sql(keys)}, {self.sql(values)})"
 
 
 def _parse_as_cast(to_type: str) -> t.Callable[[t.List], exp.Expression]:


Test patch:
diff --git a/tests/dialects/test_spark.py b/tests/dialects/test_spark.py
index a984025c..1808f531 100644
--- a/tests/dialects/test_spark.py
+++ b/tests/dialects/test_spark.py
@@ -244,6 +244,23 @@ TBLPROPERTIES (
             "SELECT STR_TO_MAP('a:1,b:2,c:3', ',', ':')",
         )
 
+        self.validate_all(
+            "MAP(1, 2, 3, 4)",
+            write={
+                "spark": "MAP(1, 2, 3, 4)",
+                "trino": "MAP(ARRAY[1, 3], ARRAY[2, 4])",
+            },
+        )
+        self.validate_all(
+            "MAP()",
+            read={
+                "spark": "MAP()",
+                "trino": "MAP()",
+            },
+            write={
+                "trino": "MAP(ARRAY[], ARRAY[])",
+            },
+        )
         self.validate_all(
             "SELECT STR_TO_MAP('a:1,b:2,c:3', ',', ':')",
             read={
Repository: tobymao/sqlglot

Issue:
Unable to parse the oracle query when ":OBJECT" bind is present
Hello,

Parser is throwing an error when there is ":OBJECT" bind is present in the oracle query.

**Code for your reference -**

```
import sqlglot.expressions as exp

query ="""
SELECT A.COL1, B.COL2 FROM SCHEMA1.TABLE1 A, SCHEMA2.TABLE2 B WHERE A.COL1 = :OBJECT AND B.COL2 = :SOMEVARIABLE 
"""

expressions = sqlglot.parse_one(query, read='oracle')
x = list(expressions.find_all(exp.Table))

for i in x:
  listTables = str(i).split(' ')[0]
  print(listTables)
```


Above code works fine if Bind name is different that OBJECT. If ':OBJECT' is present in the query, then it fails.

Request you to add support for ':OBJECT' for query parsing.
Unable to parse the oracle query when "#" is present in the column names
Hello,

Parser is throwing an error when there is "#" character is present in the column name.

**Code for your reference -**

```
import sqlglot.expressions as exp

query ="""
SELECT A.COL#, B.COL2 FROM SCHEMA1.TABLE1 A
"""

expressions = sqlglot.parse_one(query, read='oracle')
x = list(expressions.find_all(exp.Table))

for i in x:
  listTables = str(i).split(' ')[0]
  print(listTables)
```


Above code fails as it cannot process '#' and throws the error.
Request you to add support for '#' for query parsing.

Comments:

Patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index c1d86954..14c5e964 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -578,7 +578,7 @@ class Parser(metaclass=_Parser):
         TokenType.PLACEHOLDER: lambda self: self.expression(exp.Placeholder),
         TokenType.PARAMETER: lambda self: self._parse_parameter(),
         TokenType.COLON: lambda self: self.expression(exp.Placeholder, this=self._prev.text)
-        if self._match_set((TokenType.NUMBER, TokenType.VAR))
+        if self._match(TokenType.NUMBER) or self._match_set(self.ID_VAR_TOKENS)
         else None,
     }
 


Test patch:
diff --git a/tests/dialects/test_oracle.py b/tests/dialects/test_oracle.py
index 694cd68b..fb8f8fbb 100644
--- a/tests/dialects/test_oracle.py
+++ b/tests/dialects/test_oracle.py
@@ -6,6 +6,7 @@ class TestOracle(Validator):
     dialect = "oracle"
 
     def test_oracle(self):
+        self.validate_identity("SELECT :OBJECT")
         self.validate_identity("SELECT * FROM t FOR UPDATE")
         self.validate_identity("SELECT * FROM t FOR UPDATE WAIT 5")
         self.validate_identity("SELECT * FROM t FOR UPDATE NOWAIT")
Repository: tobymao/sqlglot

Issue:
duckdb unnest to snowflake generates incorrect code
**Fully reproducible code snippet**

```
In [7]: import sqlglot as sg

In [8]: sg.__version__
Out[8]: '18.0.0'

In [9]: sg.parse_one("select unnest([1, 2, 3])", read="duckdb").sql(dialect="snowflake")
Out[9]: 'SELECT EXPLODE([1, 2, 3])'
```

As far as I can tell, `EXPLODE` is not a function that exists in Snowflake.

Array flattening operations are done with their `FLATTEN` function

**Official Documentation**

https://docs.snowflake.com/en/sql-reference/functions/flatten


Comments:

Patch:
diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py
index 5aa946e7..16a1c23b 100644
--- a/sqlglot/dialects/snowflake.py
+++ b/sqlglot/dialects/snowflake.py
@@ -407,7 +407,9 @@ class Snowflake(Dialect):
             exp.Min: min_or_least,
             exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}",
             exp.RegexpILike: _regexpilike_sql,
-            exp.Select: transforms.preprocess([transforms.eliminate_distinct_on]),
+            exp.Select: transforms.preprocess(
+                [transforms.eliminate_distinct_on, transforms.explode_to_unnest]
+            ),
             exp.StarMap: rename_func("OBJECT_CONSTRUCT"),
             exp.StartsWith: rename_func("STARTSWITH"),
             exp.StrPosition: lambda self, e: self.func(
@@ -449,6 +451,16 @@ class Snowflake(Dialect):
             exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED,
         }
 
+        def unnest_sql(self, expression: exp.Unnest) -> str:
+            subquery = exp.Subquery(
+                this=exp.select("value").from_(
+                    f"TABLE(FLATTEN(INPUT => {self.sql(expression.expressions[0])}))"
+                ),
+            )
+            alias = self.sql(expression, "alias")
+            alias = f" AS {alias}" if alias else ""
+            return f"{self.sql(subquery)}{alias}"
+
         def show_sql(self, expression: exp.Show) -> str:
             scope = self.sql(expression, "scope")
             scope = f" {scope}" if scope else ""


Test patch:
diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py
index 36fca7c2..1415f8b4 100644
--- a/tests/dialects/test_duckdb.py
+++ b/tests/dialects/test_duckdb.py
@@ -61,6 +61,13 @@ class TestDuckDB(Validator):
         self.validate_all("0x1010", write={"": "0 AS x1010"})
         self.validate_all("x ~ y", write={"duckdb": "REGEXP_MATCHES(x, y)"})
         self.validate_all("SELECT * FROM 'x.y'", write={"duckdb": 'SELECT * FROM "x.y"'})
+        self.validate_all(
+            "SELECT UNNEST([1, 2, 3])",
+            write={
+                "duckdb": "SELECT UNNEST([1, 2, 3])",
+                "snowflake": "SELECT col FROM (SELECT value FROM TABLE(FLATTEN(INPUT => [1, 2, 3]))) AS _u(col)",
+            },
+        )
         self.validate_all(
             "VAR_POP(x)",
             read={
Repository: tobymao/sqlglot

Issue:
subquery in limit or offset is compiled to syntactically invalid sql
**Fully reproducible code snippet**

I might be using sqlglot incorrectly, but I am trying to create the sqlglot-builder-equivalent version of this DuckDB query:

```sql
SELECT *
FROM t LIMIT (SELECT 5)
```

I looked at the parse tree:

```
In [8]: import sqlglot as sg

In [9]: sg.__version__
Out[9]: '18.5.2.dev11'

In [10]: sg.parse_one("select * from t limit (select 5)")
Out[10]:
(SELECT expressions:
  (STAR ), limit:
  (LIMIT expression:
    (SUBQUERY this:
      (SELECT expressions:
        (LITERAL this: 5, is_string: False)))), from:
  (FROM this:
    (TABLE this:
      (IDENTIFIER this: t, quoted: False))))
```

and then tried:

```
In [43]: sg.select("*").from_("t").limit(sg.select("5").subquery()).sql("duckdb")
Out[43]: 'SELECT * FROM t(SELECT 5) LIMIT'
```

For now what I am doing is:

```
In [44]: sg.select("*").from_("t").limit(sg.select("5").subquery().sql('duckdb')).sql("duckdb")
Out[44]: 'SELECT * FROM t LIMIT (SELECT 5)'
```

which generates the desired output.

**Official Documentation**

N/A


Comments:

Patch:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
index b83d4eac..5473fb16 100644
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -2861,6 +2861,7 @@ class Select(Subqueryable):
             prefix="LIMIT",
             dialect=dialect,
             copy=copy,
+            into_arg="expression",
             **opts,
         )
 
@@ -5130,10 +5131,11 @@ def _apply_builder(
     prefix=None,
     into=None,
     dialect=None,
+    into_arg="this",
     **opts,
 ):
     if _is_wrong_expression(expression, into):
-        expression = into(this=expression)
+        expression = into(**{into_arg: expression})
     instance = maybe_copy(instance, copy)
     expression = maybe_parse(
         sql_or_expression=expression,


Test patch:
diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py
index 4165c5e9..6b496ecd 100644
--- a/tests/dialects/test_duckdb.py
+++ b/tests/dialects/test_duckdb.py
@@ -6,6 +6,11 @@ class TestDuckDB(Validator):
     dialect = "duckdb"
 
     def test_duckdb(self):
+        self.assertEqual(
+            parse_one("select * from t limit (select 5)").sql(dialect="duckdb"),
+            exp.select("*").from_("t").limit(exp.select("5").subquery()).sql(dialect="duckdb"),
+        )
+
         for join_type in ("SEMI", "ANTI"):
             exists = "EXISTS" if join_type == "SEMI" else "NOT EXISTS"
Repository: tobymao/sqlglot

Issue:
Parser error for Snowflake SQL when "window" is used as an alias
Hi All,

Trying to parse our Snowflake queries to perform some validations. Parser fails when the keyword "window" is used as an alias for a table. The SQL executes fine on Snowflake though. 

Code:

```
parsed_sql = parse_one(
    """
     SELECT
        dd.delivery_id
    FROM prod.public.orders o
    LEFT JOIN orders.public.order_assignment window on o.order_id = window.order_id
    WHERE active_dt BETWEEN '2023-06-28' and '2023-09-27'
  """,
    read="snowflake"
)
print(parsed_sql)
```

Error:

```
sqlglot.errors.ParseError: Invalid expression / Unexpected token. Line 5, Col: 57.
  d.delivery_id
    FROM prod.public.orders o
    LEFT JOIN orders.public.order_assignment window on o.order_id = window.order_id
    WHERE active_dt BETWEEN '2023-06-28' and '2023-09-27'
```

`sqlglot version - 18.8.0`

Comments:

Patch:
diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py
index 0ba6a113..5131726e 100644
--- a/sqlglot/dialects/snowflake.py
+++ b/sqlglot/dialects/snowflake.py
@@ -239,6 +239,8 @@ class Snowflake(Dialect):
     class Parser(parser.Parser):
         IDENTIFY_PIVOT_STRINGS = True
 
+        TABLE_ALIAS_TOKENS = parser.Parser.TABLE_ALIAS_TOKENS | {TokenType.WINDOW}
+
         FUNCTIONS = {
             **parser.Parser.FUNCTIONS,
             "ARRAYAGG": exp.ArrayAgg.from_arg_list,


Test patch:
diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py
index a85dc841..b96b7ee8 100644
--- a/tests/dialects/test_snowflake.py
+++ b/tests/dialects/test_snowflake.py
@@ -50,6 +50,10 @@ class TestSnowflake(Validator):
         self.validate_identity(
             "SELECT state, city, SUM(retail_price * quantity) AS gross_revenue FROM sales GROUP BY ALL"
         )
+        self.validate_identity(
+            "SELECT * FROM foo window",
+            "SELECT * FROM foo AS window",
+        )
         self.validate_identity(
             r"SELECT RLIKE(a, $$regular expression with \ characters: \d{2}-\d{3}-\d{4}$$, 'i') FROM log_source",
             r"SELECT REGEXP_LIKE(a, 'regular expression with \\ characters: \\d{2}-\\d{3}-\\d{4}', 'i') FROM log_source",
Repository: tobymao/sqlglot

Issue:
Parsing columns with reserved names
I often find cases when databases use reserved words as a column name, for example `left` or `key`. Reserved words are permitted as identifiers if you quote them. The library cannot parse such syntax.

**Fully reproducible code snippet**
```python
    sql = """
        CREATE TABLE `oauth_consumer` (
            `key` varchar(32) NOT NULL,
            UNIQUE KEY `OAUTH_CONSUMER_KEY` (`key`),
        );
    """
    parsed_sql_exprs = sqlglot.parse(sql, read="mysql")
```

**Official Documentation**
[Please include links to official SQL documentation related to your issue.](https://dev.mysql.com/doc/refman/8.0/en/keywords.html)


Comments:

Patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 76b6ee51..58b9a1a5 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -3892,7 +3892,9 @@ class Parser(metaclass=_Parser):
     def _parse_unnamed_constraint(
         self, constraints: t.Optional[t.Collection[str]] = None
     ) -> t.Optional[exp.Expression]:
-        if not self._match_texts(constraints or self.CONSTRAINT_PARSERS):
+        if self._match(TokenType.IDENTIFIER, advance=False) or not self._match_texts(
+            constraints or self.CONSTRAINT_PARSERS
+        ):
             return None
 
         constraint = self._prev.text.upper()


Test patch:
diff --git a/tests/dialects/test_mysql.py b/tests/dialects/test_mysql.py
index 11f921ce..14a864be 100644
--- a/tests/dialects/test_mysql.py
+++ b/tests/dialects/test_mysql.py
@@ -29,6 +29,9 @@ class TestMySQL(Validator):
         self.validate_identity("CREATE TABLE foo (a BIGINT, INDEX USING BTREE (b))")
         self.validate_identity("CREATE TABLE foo (a BIGINT, FULLTEXT INDEX (b))")
         self.validate_identity("CREATE TABLE foo (a BIGINT, SPATIAL INDEX (b))")
+        self.validate_identity(
+            "CREATE TABLE `oauth_consumer` (`key` VARCHAR(32) NOT NULL, UNIQUE `OAUTH_CONSUMER_KEY` (`key`))"
+        )
         self.validate_identity(
             "CREATE TABLE `x` (`username` VARCHAR(200), PRIMARY KEY (`username`(16)))"
         )
Repository: tobymao/sqlglot

Issue:
Erroneous handling of redshift's JSON_PARSE
**sqlglot version: 18.16.1**

**Fully reproducible code snippet**
```python
import sqlglot
sql = "SELECT JSON_PARSE('[10001,10002,\"abc\"]');"
parsed = sqlglot.parse_one(sql,dialect="redshift")
parsed.sql(dialect="redshift")
#'SELECT PARSE_JSON(\'[10001,10002,"abc"]\')'
```
The generated sql triggers an error when executed in redshift

>Failed to execute query: ERROR: function parse_json("unknown") does not exist
  Hint: No function matches the given name and argument types. You may need to add explicit type casts.

**Official Documentation**
https://docs.aws.amazon.com/redshift/latest/dg/JSON_PARSE.html


Comments:

Patch:
diff --git a/sqlglot/dialects/redshift.py b/sqlglot/dialects/redshift.py
index 51b91150..df70aa77 100644
--- a/sqlglot/dialects/redshift.py
+++ b/sqlglot/dialects/redshift.py
@@ -175,6 +175,7 @@ class Redshift(Postgres):
             exp.GeneratedAsIdentityColumnConstraint: generatedasidentitycolumnconstraint_sql,
             exp.JSONExtract: _json_sql,
             exp.JSONExtractScalar: _json_sql,
+            exp.ParseJSON: rename_func("JSON_PARSE"),
             exp.SafeConcat: concat_to_dpipe_sql,
             exp.Select: transforms.preprocess(
                 [transforms.eliminate_distinct_on, transforms.eliminate_semi_and_anti_joins]


Test patch:
diff --git a/tests/dialects/test_redshift.py b/tests/dialects/test_redshift.py
index bbe2de23..c8480101 100644
--- a/tests/dialects/test_redshift.py
+++ b/tests/dialects/test_redshift.py
@@ -310,6 +310,7 @@ ORDER BY
         self.validate_identity(
             "SELECT attr AS attr, JSON_TYPEOF(val) AS value_type FROM customer_orders_lineitem AS c, UNPIVOT c.c_orders AS val AT attr WHERE c_custkey = 9451"
         )
+        self.validate_identity("SELECT JSON_PARSE('[]')")
 
     def test_values(self):
         # Test crazy-sized VALUES clause to UNION ALL conversion to ensure we don't get RecursionError
Repository: tobymao/sqlglot

Issue:
Postgres opclass with schema prefix
**Before you file an issue**
In Postgres SQL opclass may include a schema prefix. Now I get an error when parsing such SQL. 

**Fully reproducible code snippet**
```python
    sql = """
    CREATE INDEX index_issues_on_title_trigram ON public.issues USING gin (title public.gin_trgm_ops);
    """
    parsed_sql_exprs = sqlglot.parse_one(sql, read="postgres")
```

**Official Documentation**
https://www.postgresql.org/docs/current/sql-createopclass.html


Comments:

Patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 9aa2313c..3d9602b0 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -879,6 +879,7 @@ class Parser(metaclass=_Parser):
     CLONE_KINDS = {"TIMESTAMP", "OFFSET", "STATEMENT"}
 
     OPCLASS_FOLLOW_KEYWORDS = {"ASC", "DESC", "NULLS"}
+    OPTYPE_FOLLOW_TOKENS = {TokenType.COMMA, TokenType.R_PAREN}
 
     TABLE_INDEX_HINT_TOKENS = {TokenType.FORCE, TokenType.IGNORE, TokenType.USE}
 
@@ -2565,9 +2566,8 @@ class Parser(metaclass=_Parser):
         if self._match_texts(self.OPCLASS_FOLLOW_KEYWORDS, advance=False):
             return this
 
-        opclass = self._parse_var(any_token=True)
-        if opclass:
-            return self.expression(exp.Opclass, this=this, expression=opclass)
+        if not self._match_set(self.OPTYPE_FOLLOW_TOKENS, advance=False):
+            return self.expression(exp.Opclass, this=this, expression=self._parse_table_parts())
 
         return this
 


Test patch:
diff --git a/tests/dialects/test_postgres.py b/tests/dialects/test_postgres.py
index 0e5f1a1b..89360376 100644
--- a/tests/dialects/test_postgres.py
+++ b/tests/dialects/test_postgres.py
@@ -42,6 +42,9 @@ class TestPostgres(Validator):
         self.validate_identity(
             "CREATE INDEX foo ON bar.baz USING btree(col1 varchar_pattern_ops ASC, col2)"
         )
+        self.validate_identity(
+            "CREATE INDEX index_issues_on_title_trigram ON public.issues USING gin(title public.gin_trgm_ops)"
+        )
         self.validate_identity(
             "INSERT INTO x VALUES (1, 'a', 2.0) ON CONFLICT (id) DO NOTHING RETURNING *"
         )
Repository: tobymao/sqlglot

Issue:
ALTER TABLE statement with Drop constraint not parsing properly
I was trying to parse a DROP CONSTRAINT statement in oracle and TSQL, but sqlglot.parse_one is not parsing them properly. it does not return the meta data about the drop constraint as it is giving when we add constraint. specifically i am trying it with foreign key. it works perfectly when i add constraint in alter table but does work in drop constraint

`sqlglot.parse_one(`
        `"ALTER TABLE dbo.DocExe DROP CONSTRAINT FK_Column_B;", "tsql"`
   ` )`
   
   The above example is copied from this docs https://learn.microsoft.com/en-us/sql/relational-databases/tables/delete-foreign-key-relationships?view=sql-server-ver16
   


Comments:

Patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index ee147437..c4d3f62b 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -245,6 +245,7 @@ class Parser(metaclass=_Parser):
 
     CREATABLES = {
         TokenType.COLUMN,
+        TokenType.CONSTRAINT,
         TokenType.FUNCTION,
         TokenType.INDEX,
         TokenType.PROCEDURE,


Test patch:
diff --git a/tests/dialects/test_tsql.py b/tests/dialects/test_tsql.py
index 5c4c3a83..59f936e9 100644
--- a/tests/dialects/test_tsql.py
+++ b/tests/dialects/test_tsql.py
@@ -561,6 +561,13 @@ class TestTSQL(Validator):
         )
 
     def test_ddl(self):
+        expression = parse_one("ALTER TABLE dbo.DocExe DROP CONSTRAINT FK_Column_B", dialect="tsql")
+        self.assertIsInstance(expression, exp.AlterTable)
+        self.assertIsInstance(expression.args["actions"][0], exp.Drop)
+        self.assertEqual(
+            expression.sql(dialect="tsql"), "ALTER TABLE dbo.DocExe DROP CONSTRAINT FK_Column_B"
+        )
+
         for clusterd_keyword in ("CLUSTERED", "NONCLUSTERED"):
             self.validate_identity(
                 'CREATE TABLE "dbo"."benchmark" ('
@@ -630,7 +637,6 @@ class TestTSQL(Validator):
                 "tsql": "CREATE OR ALTER VIEW a.b AS SELECT 1",
             },
         )
-
         self.validate_all(
             "ALTER TABLE a ADD b INTEGER, c INTEGER",
             read={
@@ -641,7 +647,6 @@ class TestTSQL(Validator):
                 "tsql": "ALTER TABLE a ADD b INTEGER, c INTEGER",
             },
         )
-
         self.validate_all(
             "CREATE TABLE #mytemp (a INTEGER, b CHAR(2), c TIME(4), d FLOAT(24))",
             write={
Repository: tobymao/sqlglot

Issue:
[Bug] Transpiling built-in functions is not accurate.
**Before you file an issue**
- updated to 19.0.3

**Fully reproducible code snippet**
I am transpiling Oracle SQL to Postgres. There are multiple functions that fail when executed.
Here is an example.
`sql_in_oracle = "to_char(123)"`
`sqlglot.transpile(sql_in_oracle, read='oracle', write='postgres')`

Output: `['to_char(123)']`
Expected: `['123::text']` or whatever is valid in Postgres
Postgres supports `to_char(expr, format)`, but not `to_char(expr)`.

I have many functions that cannot be tranpiled correctly in my query list, such as,

Truncate datetime to day:
TRUNC(datetime) -- Valid Oracle
Output: TRUNC(datetime) -- Invalid Postgres
Expected: DATE_TRUNC('day', datetime) -- Valid postgres



**Official Documentation**
[sqlines](https://sqlines.com/oracle-to-postgresql) has an online tool I tried once. On its website, it lists many migration conversions for different databases. It might be a helpful resource for you to check out. Thanks.

Comments:

Patch:
diff --git a/sqlglot/dialects/oracle.py b/sqlglot/dialects/oracle.py
index 0505335b..0f27c4b2 100644
--- a/sqlglot/dialects/oracle.py
+++ b/sqlglot/dialects/oracle.py
@@ -3,7 +3,13 @@ from __future__ import annotations
 import typing as t
 
 from sqlglot import exp, generator, parser, tokens, transforms
-from sqlglot.dialects.dialect import Dialect, no_ilike_sql, rename_func, trim_sql
+from sqlglot.dialects.dialect import (
+    Dialect,
+    format_time_lambda,
+    no_ilike_sql,
+    rename_func,
+    trim_sql,
+)
 from sqlglot.helper import seq_get
 from sqlglot.tokens import TokenType
 
@@ -70,6 +76,7 @@ class Oracle(Dialect):
         FUNCTIONS = {
             **parser.Parser.FUNCTIONS,
             "SQUARE": lambda args: exp.Pow(this=seq_get(args, 0), expression=exp.Literal.number(2)),
+            "TO_CHAR": format_time_lambda(exp.TimeToStr, "oracle", default=True),
         }
 
         FUNCTION_PARSERS: t.Dict[str, t.Callable] = {


Test patch:
diff --git a/tests/dialects/test_oracle.py b/tests/dialects/test_oracle.py
index 0aa4311a..6b181153 100644
--- a/tests/dialects/test_oracle.py
+++ b/tests/dialects/test_oracle.py
@@ -62,6 +62,13 @@ class TestOracle(Validator):
             "SELECT * FROM t SAMPLE (0.25)",
         )
 
+        self.validate_all(
+            "SELECT TO_CHAR(TIMESTAMP '1999-12-01 10:00:00')",
+            write={
+                "oracle": "SELECT TO_CHAR(CAST('1999-12-01 10:00:00' AS TIMESTAMP), 'YYYY-MM-DD HH24:MI:SS')",
+                "postgres": "SELECT TO_CHAR(CAST('1999-12-01 10:00:00' AS TIMESTAMP), 'YYYY-MM-DD HH24:MI:SS')",
+            },
+        )
         self.validate_all(
             "SELECT CAST(NULL AS VARCHAR2(2328 CHAR)) AS COL1",
             write={