Repository: tobymao/sqlglot
Issue category: Bug Report

Issue:
[Postgres] Parse error for XMLElement
Postgres XMLElement returns parse error:
```
SELECT xmlelement(name foo);
# > <foo/>
```

**Fully reproducible code snippet**
```
from sqlglot import parse_one
from sqlglot.optimizer.qualify import qualify

qualify(parse_one(sql = """
 SELECT xmlelement(name "foo")
""", dialect="postgres"), schema=None)

# > sqlglot.errors.ParseError: Expecting ). Line 2, Col: 29.
```

**Official Documentation**
https://www.postgresql.org/docs/current/functions-xml.html#FUNCTIONS-PRODUCING-XML-XMLELEMENT



Hints:

Patch:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
index 39975d7d57..150cbe1af4 100644
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -6665,6 +6665,11 @@ class Week(Func):
     arg_types = {"this": True, "mode": False}
 
 
+class XMLElement(Func):
+    _sql_names = ["XMLELEMENT"]
+    arg_types = {"this": True, "expressions": False}
+
+
 class XMLTable(Func):
     arg_types = {"this": True, "passing": False, "columns": False, "by_ref": False}
 
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
index 635a5f5fa7..5ba3f57ce0 100644
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -4612,3 +4612,7 @@ def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
             include = f"{include} AS {alias}"
 
         return include
+
+    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
+        name = f"NAME {self.sql(expression, 'this')}"
+        return self.func("XMLELEMENT", name, *expression.expressions)
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 4cd3d30560..fbbc8f14d4 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -1143,6 +1143,11 @@ class Parser(metaclass=_Parser):
         "TRIM": lambda self: self._parse_trim(),
         "TRY_CAST": lambda self: self._parse_cast(False, safe=True),
         "TRY_CONVERT": lambda self: self._parse_convert(False, safe=True),
+        "XMLELEMENT": lambda self: self.expression(
+            exp.XMLElement,
+            this=self._match_text_seq("NAME") and self._parse_id_var(),
+            expressions=self._match(TokenType.COMMA) and self._parse_csv(self._parse_expression),
+        ),
     }
 
     QUERY_MODIFIER_PARSERS = {
@@ -5351,18 +5356,21 @@ def _parse_function_call(
                 functions = self.FUNCTIONS
 
             function = functions.get(upper)
+            known_function = function and not anonymous
 
-            alias = upper in self.FUNCTIONS_WITH_ALIASED_ARGS
+            alias = not known_function or upper in self.FUNCTIONS_WITH_ALIASED_ARGS
             args = self._parse_csv(lambda: self._parse_lambda(alias=alias))
 
-            if alias:
+            if alias and known_function:
                 args = self._kv_to_prop_eq(args)
 
-            if function and not anonymous:
-                if "dialect" in function.__code__.co_varnames:
-                    func = function(args, dialect=self.dialect)
+            if known_function:
+                func_builder = t.cast(t.Callable, function)
+
+                if "dialect" in func_builder.__code__.co_varnames:
+                    func = func_builder(args, dialect=self.dialect)
                 else:
-                    func = function(args)
+                    func = func_builder(args)
 
                 func = self.validate_expression(func, args)
                 if self.dialect.PRESERVE_ORIGINAL_NAMES:
@@ -6730,7 +6738,9 @@ def _parse_expressions(self) -> t.List[exp.Expression]:
 
     def _parse_select_or_expression(self, alias: bool = False) -> t.Optional[exp.Expression]:
         return self._parse_select() or self._parse_set_operations(
-            self._parse_expression() if alias else self._parse_assignment()
+            self._parse_alias(self._parse_assignment(), explicit=True)
+            if alias
+            else self._parse_assignment()
         )
 
     def _parse_ddl_select(self) -> t.Optional[exp.Expression]:


Test patch:
diff --git a/tests/dialects/test_postgres.py b/tests/dialects/test_postgres.py
index 66ded239a3..204151ed38 100644
--- a/tests/dialects/test_postgres.py
+++ b/tests/dialects/test_postgres.py
@@ -1289,3 +1289,17 @@ def test_array_length(self):
                 "clickhouse": UnsupportedError,
             },
         )
+
+    def test_xmlelement(self):
+        self.validate_identity("SELECT XMLELEMENT(NAME foo)")
+        self.validate_identity("SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES('xyz' AS bar))")
+        self.validate_identity("SELECT XMLELEMENT(NAME test, XMLATTRIBUTES(a, b)) FROM test")
+        self.validate_identity(
+            "SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES(CURRENT_DATE AS bar), 'cont', 'ent')"
+        )
+        self.validate_identity(
+            """SELECT XMLELEMENT(NAME "foo$bar", XMLATTRIBUTES('xyz' AS "a&b"))"""
+        )
+        self.validate_identity(
+            "SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES('xyz' AS bar), XMLELEMENT(NAME abc), XMLCOMMENT('test'), XMLELEMENT(NAME xyz))"
+        )