Repository: tobymao/sqlglot

Issue:
`make_timestamp` parse for duckdb is incorrect and also doesn't accept the correct number of arguments (6)
**Fully reproducible code snippet**

Not sure what's happening here but all the inputs are being thrown away

```
In [6]: sg.parse_one("select make_timestamp(1,2,3,4,5)", read="duckdb")
Out[6]:
(SELECT expressions:
  (UNIXTOTIME this:
    (LITERAL this: 1, is_string: False), scale:
    (LITERAL this: micros, is_string: True)))

In [8]: sg.parse_one("select make_timestamp(1,2,3,4,5)", read="duckdb").sql('duckdb')
Out[8]: 'SELECT MAKE_TIMESTAMP(1)'
```

Second, this particular overload of DuckDB's `make_timestamp` accepts 6 arguments per the documentation:

![image](https://github.com/tobymao/sqlglot/assets/417981/03a34f38-156c-471b-ab9b-ba8172edd4dd)

```
In [9]: sg.parse_one("select make_timestamp(1,2,3,4,5,6)", read="duckdb")
...
ParseError: The number of provided arguments (6) is greater than the maximum number of supported arguments (5). Line 1, Col: 34.
  select make_timestamp(1,2,3,4,5,6)
```

**Official Documentation**

https://duckdb.org/docs/sql/functions/timestamp#timestamp-functions
https://duckdb.org/docs/sql/functions/timestamptz#icu-timestamp-with-time-zone-functions



Discussion:
These are also issues with `make_timestamptz`
> These are also issues with `make_timestamptz`

@cpcloud what issues?
I meant the ones I reported here, but they actually look fine:

```
In [3]: sg.parse_one("select make_timestamptz(1,2,3,4,5,6,'foo')", read="duckdb")
Out[3]:
(SELECT expressions:
  (ANONYMOUS this: make_timestamptz, expressions:
    (LITERAL this: 1, is_string: False),
    (LITERAL this: 2, is_string: False),
    (LITERAL this: 3, is_string: False),
    (LITERAL this: 4, is_string: False),
    (LITERAL this: 5, is_string: False),
    (LITERAL this: 6, is_string: False),
    (LITERAL this: foo, is_string: True)))
```

so nevermind on `make_timestamptz`!

Resolving patch:
diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py
--- a/sqlglot/dialects/duckdb.py
+++ b/sqlglot/dialects/duckdb.py
@@ -84,6 +84,20 @@ def _parse_date_diff(args: t.List) -> exp.Expression:
     return exp.DateDiff(this=seq_get(args, 2), expression=seq_get(args, 1), unit=seq_get(args, 0))
 
 
+def _parse_make_timestamp(args: t.List) -> exp.Expression:
+    if len(args) == 1:
+        return exp.UnixToTime(this=seq_get(args, 0), scale=exp.UnixToTime.MICROS)
+
+    return exp.TimestampFromParts(
+        year=seq_get(args, 0),
+        month=seq_get(args, 1),
+        day=seq_get(args, 2),
+        hour=seq_get(args, 3),
+        min=seq_get(args, 4),
+        sec=seq_get(args, 5),
+    )
+
+
 def _struct_sql(self: DuckDB.Generator, expression: exp.Struct) -> str:
     args: t.List[str] = []
     for expr in expression.expressions:
@@ -199,9 +213,7 @@ class Parser(parser.Parser):
             "LIST_REVERSE_SORT": _sort_array_reverse,
             "LIST_SORT": exp.SortArray.from_arg_list,
             "LIST_VALUE": exp.Array.from_arg_list,
-            "MAKE_TIMESTAMP": lambda args: exp.UnixToTime(
-                this=seq_get(args, 0), scale=exp.UnixToTime.MICROS
-            ),
+            "MAKE_TIMESTAMP": _parse_make_timestamp,
             "MEDIAN": lambda args: exp.PercentileCont(
                 this=seq_get(args, 0), expression=exp.Literal.number(0.5)
             ),
@@ -349,6 +361,7 @@ class Generator(generator.Generator):
             exp.StrToUnix: lambda self, e: f"EPOCH(STRPTIME({self.sql(e, 'this')}, {self.format_time(e)}))",
             exp.Struct: _struct_sql,
             exp.Timestamp: no_timestamp_sql,
+            exp.TimestampFromParts: rename_func("MAKE_TIMESTAMP"),
             exp.TimestampTrunc: timestamptrunc_sql,
             exp.TimeStrToDate: lambda self, e: f"CAST({self.sql(e, 'this')} AS DATE)",
             exp.TimeStrToTime: timestrtotime_sql,
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -5233,6 +5233,19 @@ class UnixToTimeStr(Func):
     pass
 
 
+class TimestampFromParts(Func):
+    """Constructs a timestamp given its constituent parts."""
+
+    arg_types = {
+        "year": True,
+        "month": True,
+        "day": True,
+        "hour": True,
+        "min": True,
+        "sec": True,
+    }
+
+
 class Upper(Func):
     _sql_names = ["UPPER", "UCASE"]
 


Test patch:
diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py
--- a/tests/dialects/test_duckdb.py
+++ b/tests/dialects/test_duckdb.py
@@ -116,6 +116,8 @@ def test_duckdb(self):
             parse_one("a // b", read="duckdb").assert_is(exp.IntDiv).sql(dialect="duckdb"), "a // b"
         )
 
+        self.validate_identity("MAKE_TIMESTAMP(1992, 9, 20, 13, 34, 27.123456)")
+        self.validate_identity("MAKE_TIMESTAMP(1667810584123456)")
         self.validate_identity("SELECT EPOCH_MS(10) AS t")
         self.validate_identity("SELECT MAKE_TIMESTAMP(10) AS t")
         self.validate_identity("SELECT TO_TIMESTAMP(10) AS t")