Hi,

I'd like to propose the implementation of the XMLNamespaces option for
XMLElement.

XMLNAMESPACES(nsuri AS nsprefix)
XMLNAMESPACES(DEFAULT default-nsuri)
XMLNAMESPACES(NO DEFAULT)

* nsprefix:              Namespace's prefix.
* nsuri:                 Namespace's URI.
* DEFAULT default-nsuri: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT:            Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.

This basically works pretty much like XMLAttributes, but with a few more
restrictions (see SQL/XML:2023, 11.2 <XML lexically scoped options>):

* XML namespace declaration shall contain at most one DEFAULT namespace
declaration item.
* No namespace prefix shall be equivalent to xml or xmlns.
* No namespace URI shall be identical to http://www.w3.org/2000/xmlns/
or to http://www.w3.org/XML/1998/namespace.
* The value of a namespace URI contained in an regular namespace
declaration item (no DEFAULT) shall not be a zero-length string.

Examples:

SELECT xmlelement(NAME "foo", xmlnamespaces('http://x.y' AS bar));
          xmlelement           
-------------------------------
 <foo xmlns:bar="http://x.y"/>

SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http://x.y'));
        xmlelement         
---------------------------
 <foo xmlns="http://x.y"/>

SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
   xmlelement    
-----------------
 <foo xmlns=""/>

In transformXmlExpr() it seemed convenient to use the same parameters to
store the prefixes and URIs as in XMLAttributes (arg_names and
named_args), but I am still not so sure it is the right approach. Is
there perhaps a better way?

Any thoughts? Feedback welcome!

Best, Jim
From 8dee3772be0d89b3d49eff17344ff53440e5f566 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jo...@uni-muenster.de>
Date: Fri, 20 Dec 2024 14:50:51 +0100
Subject: [PATCH v1] Add XMLNamespaces option to XMLElement

This patch adds the scoped option XMLNamespaces to XMLElement,
as described in ISO/IEC 9075-14:2023, 11.2 XML lexically scoped
options:

xmlnamespaces(uri AS prefix, ...)
xmlnamespaces(DEFAULT uri, ...)
xmlnamespaces(NO DEFAULT, ...)

* prefix:         Namespace's prefix.
* uri:            Namespace's URI.
* DEFAULT prefix: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT:     Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.

== Examples ==

SELECT xmlelement(NAME "foo", xmlnamespaces('http:/x.y' AS bar));
          xmlelement
------------------------------
 <foo xmlns:bar="http:/x.y"/>

SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y'));
        xmlelement
--------------------------
 <foo xmlns="http:/x.y"/>

 SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
   xmlelement
-----------------
 <foo xmlns=""/>

Tests and documentation were updated accordingly.
---
 doc/src/sgml/func.sgml              |  57 +++++++-
 src/backend/parser/gram.y           |  73 +++++++---
 src/backend/parser/parse_expr.c     |  73 ++++++++++
 src/backend/utils/adt/xml.c         |  32 ++++-
 src/include/nodes/primnodes.h       |   4 +-
 src/include/utils/xml.h             |   6 +
 src/test/regress/expected/xml.out   | 205 ++++++++++++++++++++++++++++
 src/test/regress/expected/xml_1.out | 151 ++++++++++++++++++++
 src/test/regress/expected/xml_2.out | 205 ++++++++++++++++++++++++++++
 src/test/regress/sql/xml.sql        | 100 ++++++++++++++
 10 files changed, 884 insertions(+), 22 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 47370e581a..b33663bc09 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14482,7 +14482,10 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
    </indexterm>
 
 <synopsis>
-<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> <optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
+<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable>
+    <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional>
+    <optional>, <literal>XMLNAMESPACES</literal> ( {<replaceable>regular-nsuri</replaceable> <literal>AS</literal> <replaceable>nsprefix</replaceable> | DEFAULT <replaceable>default-nsuri</replaceable> | NO DEFAULT} <optional>, ...</optional> ) </optional>
+<optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
 </synopsis>
 
     <para>
@@ -14495,7 +14498,39 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
      yield any <productname>PostgreSQL</productname> data type.  The
      argument(s) within <literal>XMLATTRIBUTES</literal> generate attributes
      of the XML element; the <replaceable>content</replaceable> value(s) are
-     concatenated to form its content.
+     concatenated to form its content. The arguments within <literal>XMLNAMESPACES</literal>
+     constuct namespace declarations from values provided in <replaceable>nsuri</replaceable>
+     and <replaceable>nsprefix</replaceable>, which correspond to the URI of a namespace and
+     its prefix, respectively. The option <literal>DEFAULT</literal> can be used to set the
+     default namespace declaration (without a prefix) to the URI provided in <replaceable>default-nsuri</replaceable>.
+     The option <literal>NO DEFAULT</literal> states that a namespace scope has no default namespace. A valid
+     <literal>XMLNAMESPACES</literal> item must fulfill the following conditions:
+
+    <itemizedlist>
+    <listitem>
+     <para>
+      Only a single <literal>DEFAULT</literal> declaration item within the same scope.
+     </para>
+    </listitem>
+    <listitem>
+     <para>
+      No two <replaceable>nsuri</replaceable> can be equal within the same scope.
+     </para>
+    </listitem>
+    <listitem>
+     <para>
+      No <replaceable>nsprefix</replaceable> can be equal to <literal>xml</literal> or <literal>xmlns</literal>,
+      and no <replaceable>nsuri</replaceable> can be equal to <literal>http://www.w3.org/2000/xmlns/</literal>
+      or to <literal>http://www.w3.org/XML/1998/namespace</literal>, as they are already bouned to standard XML declarations.
+     </para>
+    </listitem>
+    <listitem>
+     <para>
+      The value of a <replaceable>regular-nsuri</replaceable> cannot be a zero-length string.
+     </para>
+    </listitem>
+   </itemizedlist>
+
     </para>
 
     <para>
@@ -14518,6 +14553,24 @@ SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');
              xmlelement
 -------------------------------------
  <foo bar="2007-01-26">content</foo>
+
+SELECT xmlelement(NAME "foo:root", xmlnamespaces('http:/foo.bar/' AS foo), 'content');
+
+                       xmlelement
+---------------------------------------------------------
+ <foo:root xmlns:foo="http:/foo.bar/">content</foo:root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/foo.bar/'), 'content');
+
+                 xmlelement
+---------------------------------------------
+ <root xmlns="http:/foo.bar/">content</root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT), 'content');
+
+          xmlelement
+-------------------------------
+ <root xmlns="">content</root>
 ]]></screen>
     </para>
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 67eb96396a..e0020f88bc 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -185,7 +185,7 @@ static Node *makeNotExpr(Node *expr, int location);
 static Node *makeAArrayExpr(List *elements, int location);
 static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod,
 								  int location);
-static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
+static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *xmlnamespaces,
 						 List *args, int location);
 static List *mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner);
 static TypeName *TableFuncTypeName(List *columns);
@@ -610,7 +610,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <list>	xmltable_column_list xmltable_column_option_list
 %type <node>	xmltable_column_el
 %type <defelt>	xmltable_column_option_el
-%type <list>	xml_namespace_list
+%type <list>	xml_namespace_list xml_namespaces
 %type <target>	xml_namespace_el
 
 %type <node>	func_application func_expr_common_subexpr
@@ -14210,6 +14210,15 @@ xml_namespace_el:
 					$$->val = $2;
 					$$->location = @1;
 				}
+			| NO DEFAULT
+				{
+					$$ = makeNode(ResTarget);
+					$$->name = NULL;
+					$$->indirection = NIL;
+					$$->val = makeStringConst("", @1);;
+					$$->location = @1;
+				}
+
 		;
 
 json_table:
@@ -15263,12 +15272,12 @@ a_expr:		c_expr									{ $$ = $1; }
 				}
 			| a_expr IS DOCUMENT_P					%prec IS
 				{
-					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
 									 list_make1($1), @2);
 				}
 			| a_expr IS NOT DOCUMENT_P				%prec IS
 				{
-					$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+					$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
 												 list_make1($1), @2),
 									 @2);
 				}
@@ -15410,12 +15419,12 @@ b_expr:		c_expr
 				}
 			| b_expr IS DOCUMENT_P					%prec IS
 				{
-					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
 									 list_make1($1), @2);
 				}
 			| b_expr IS NOT DOCUMENT_P				%prec IS
 				{
-					$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+					$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
 												 list_make1($1), @2),
 									 @2);
 				}
@@ -15950,23 +15959,47 @@ func_expr_common_subexpr:
 				}
 			| XMLCONCAT '(' expr_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1);
+					$$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, NIL, $3, @1);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, NIL, @1);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, NIL, @1);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ',' expr_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, $6, @1);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' expr_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, $8, @1);
+				}
+			| XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' expr_list ')'
+				{
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, $8, @1);
+				}
+			| XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ')'
+				{
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, NIL, @1);
+				}
+			| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' xml_namespaces ')'
+				{
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, NIL, @1);
+				}
+			| XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' xml_attributes ')'
+				{
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $8, $6, NIL, @1);
+				}
+			| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' xml_namespaces ',' expr_list ')'
+				{
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, $10, @1);
+				}
+			| XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' xml_attributes ',' expr_list ')'
+				{
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $8, $6, $10, @1);
 				}
 			| XMLEXISTS '(' c_expr xmlexists_argument ')'
 				{
@@ -15979,12 +16012,12 @@ func_expr_common_subexpr:
 				}
 			| XMLFOREST '(' xml_attribute_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1);
+					$$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, NIL, @1);
 				}
 			| XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')'
 				{
 					XmlExpr *x = (XmlExpr *)
-						makeXmlExpr(IS_XMLPARSE, NULL, NIL,
+						makeXmlExpr(IS_XMLPARSE, NULL, NIL, NIL,
 									list_make2($4, makeBoolAConst($5, -1)),
 									@1);
 
@@ -15993,15 +16026,15 @@ func_expr_common_subexpr:
 				}
 			| XMLPI '(' NAME_P ColLabel ')'
 				{
-					$$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, @1);
+					$$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, NIL, @1);
 				}
 			| XMLPI '(' NAME_P ColLabel ',' a_expr ')'
 				{
-					$$ = makeXmlExpr(IS_XMLPI, $4, NULL, list_make1($6), @1);
+					$$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, list_make1($6), @1);
 				}
 			| XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')'
 				{
-					$$ = makeXmlExpr(IS_XMLROOT, NULL, NIL,
+					$$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, NIL,
 									 list_make3($3, $5, $6), @1);
 				}
 			| XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename xml_indent_option ')'
@@ -16205,6 +16238,9 @@ opt_xml_root_standalone: ',' STANDALONE_P YES_P
 xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')'	{ $$ = $3; }
 		;
 
+xml_namespaces: XMLNAMESPACES '(' xml_namespace_list ')'	{ $$ = $3; }
+		;
+
 xml_attribute_list:	xml_attribute_el					{ $$ = list_make1($1); }
 			| xml_attribute_list ',' xml_attribute_el	{ $$ = lappend($1, $3); }
 		;
@@ -19211,8 +19247,8 @@ makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location)
 }
 
 static Node *
-makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
-			int location)
+makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *xmlnamespaces,
+			List *args, int location)
 {
 	XmlExpr		*x = makeNode(XmlExpr);
 
@@ -19223,6 +19259,7 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
 	 * expression and name lists in transformXmlExpr().
 	 */
 	x->named_args = named_args;
+	x->xmlnamespaces = xmlnamespaces;
 	x->arg_names = NIL;
 	x->args = args;
 	/* xmloption, if relevant, must be filled in by caller */
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index c2806297aa..783184d782 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2347,6 +2347,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
 	XmlExpr    *newx;
 	ListCell   *lc;
 	int			i;
+	bool		has_default_ns = false;
 
 	newx = makeNode(XmlExpr);
 	newx->op = x->op;
@@ -2467,6 +2468,78 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
 		i++;
 	}
 
+	/*
+	 * this adds the xmlnamespaces into arg_names and named_args
+	 */
+	foreach (lc, x->xmlnamespaces)
+	{
+		ResTarget *r = lfirst_node(ResTarget, lc);
+		Node *expr;
+		StringInfoData nsbuf;
+
+		expr = transformExprRecurse(pstate, r->val);
+		initStringInfo(&nsbuf);
+
+		if (!r->name)
+		{
+			/*
+			 * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+			 * Syntax Rule 2) <XML namespace declaration> shall contain at most one
+			 * <XML default namespace declaration item>.
+			 */
+			if (has_default_ns)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("XML elements can have only a single [NO] DEFAULT namespace"),
+						 parser_errposition(pstate, r->location)));
+			else
+				appendStringInfo(&nsbuf, NAMESPACE_XMLNS_DEFAULT_PREFIX);
+
+			has_default_ns = true;
+		}
+		/*
+		 * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+		 * Syntax Rule 5) No <XML namespace prefix> shall be equivalent to
+		 * "xml" or "xmlns".
+		 */
+		else if (strcmp(r->name, NAMESPACE_XMLNS_DEFAULT_PREFIX) == 0 ||
+				 strcmp(r->name, NAMESPACE_XML_DEFAULT_PREFIX) == 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("invalid XML namespace prefix \"%s\"", r->name),
+					 errdetail("this prefix is already bounded to a standard namespace URI"),
+					 parser_errposition(pstate, r->location)));
+		else if (r->name)
+			appendStringInfo(&nsbuf, "%s:%s", NAMESPACE_XMLNS_DEFAULT_PREFIX,
+							 map_sql_identifier_to_xml_name(r->name, false, false));
+		else if (IsA(r->val, ColumnRef))
+			appendStringInfo(&nsbuf, "%s:%s", NAMESPACE_XMLNS_DEFAULT_PREFIX,
+							 map_sql_identifier_to_xml_name(FigureColname(r->val), true, false));
+
+		/* reject duplicate argnames in XMLELEMENT only */
+		if (x->op == IS_XMLELEMENT)
+		{
+			ListCell *lc2;
+
+			foreach (lc2, newx->arg_names)
+			{
+				/*
+				* SQL/XML:2023 - 11.2 <XML lexically scoped options>
+				* Syntax Rule 4) No two <XML namespace prefix>es shall be equivalent.
+				*/
+				if (strcmp(nsbuf.data, strVal(lfirst(lc2))) == 0)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("XML namespace \"%s\" appears more than once",
+									nsbuf.data),
+							 parser_errposition(pstate, r->location)));
+			}
+		}
+
+		newx->named_args = lappend(newx->named_args, expr);
+		newx->arg_names = lappend(newx->arg_names, makeString(nsbuf.data));
+	}
+
 	return (Node *) newx;
 }
 
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 0898cb1be4..70fec22e21 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -244,7 +244,6 @@ const TableFuncRoutine XmlTableRoutine =
 #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance";
 #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml";
 
-
 #ifdef USE_LIBXML
 
 static int
@@ -931,6 +930,37 @@ xmlelement(XmlExpr *xexpr,
 			char	   *str = (char *) lfirst(arg);
 			char	   *argname = strVal(lfirst(narg));
 
+			StringInfoData xmlns_prefix;
+			initStringInfo(&xmlns_prefix);
+			appendStringInfo(&xmlns_prefix, "%s:", NAMESPACE_XMLNS_DEFAULT_PREFIX);
+
+			/*
+			 * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+			 * Syntax Rule 6) No <XML namespace URI> shall be identical, as defined
+			 * in XML Namespaces, to http://www.w3.org/2000/xmlns/ or to
+			 * http://www.w3.org/XML/1998/namespace
+			 */
+			if (str && ((strncmp(argname, xmlns_prefix.data, xmlns_prefix.len) == 0 ||
+						 strcmp(argname, NAMESPACE_XMLNS_DEFAULT_PREFIX) == 0) &&
+						(strcmp(str, NAMESPACE_XMLNS) == 0 || strcmp(str, NAMESPACE_XML) == 0)))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("invalid XML namespace URI \"%s\"", str),
+						 errdetail("this URI is already bounded to standard a namespace prefix")));
+
+			/*
+			 * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+			 * Syntax Rule 7) The value of an <XML namespace URI> contained in an
+			 * <XML regular namespace declaration item> shall not be a zero-length string.
+			 */
+			if (strncmp(argname, xmlns_prefix.data, 6) == 0 && (str && strlen(str) == 0))
+				ereport(ERROR,
+						(errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING),
+						 errmsg("invalid XML namespace URI for \"%s\"", argname),
+						 errdetail("a regular XML namespace cannot be a zero-length string")));
+
+			pfree(xmlns_prefix.data);
+
 			if (str)
 				xmlTextWriterWriteAttribute(writer,
 											(xmlChar *) argname,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index b0ef1952e8..2e5ad905a0 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1578,7 +1578,7 @@ typedef struct SQLValueFunction
 typedef enum XmlExprOp
 {
 	IS_XMLCONCAT,				/* XMLCONCAT(args) */
-	IS_XMLELEMENT,				/* XMLELEMENT(name, xml_attributes, args) */
+	IS_XMLELEMENT,				/* XMLELEMENT(name, xml_attributes, xml_namespaces, args) */
 	IS_XMLFOREST,				/* XMLFOREST(xml_attributes) */
 	IS_XMLPARSE,				/* XMLPARSE(text, is_doc, preserve_ws) */
 	IS_XMLPI,					/* XMLPI(name [, args]) */
@@ -1602,6 +1602,8 @@ typedef struct XmlExpr
 	char	   *name pg_node_attr(query_jumble_ignore);
 	/* non-XML expressions for xml_attributes */
 	List	   *named_args;
+	/* non-XML expressions for XMLNAMESPACES */
+	List	   *xmlnamespaces;
 	/* parallel list of String values */
 	List	   *arg_names pg_node_attr(query_jumble_ignore);
 	/* list of expressions */
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index ed20e21375..4cd365d414 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -59,6 +59,12 @@ XmlPGetDatum(const xmltype *X)
 	return PointerGetDatum(X);
 }
 
+/* reserved prefixes and URIs for XMLNamespace() from SQL/XML:2023, 11.2 */
+#define NAMESPACE_XMLNS "http://www.w3.org/2000/xmlns/";
+#define NAMESPACE_XML "http://www.w3.org/XML/1998/namespace";
+#define NAMESPACE_XMLNS_DEFAULT_PREFIX "xmlns"
+#define NAMESPACE_XML_DEFAULT_PREFIX "xml"
+
 #define PG_GETARG_XML_P(n)	DatumGetXmlP(PG_GETARG_DATUM(n))
 #define PG_RETURN_XML_P(x)	PG_RETURN_POINTER(x)
 
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index fb5f345855..c9be4a3d72 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -225,6 +225,211 @@ SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as fun
  <foo funny="&lt;&gt;&amp;&quot;'" funnier="b&lt;a/&gt;r"/>
 (1 row)
 
+-- DEFAULT NULL xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL));
+ xmlelement 
+------------
+ <root/>
+(1 row)
+
+-- DEFAULT xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1'));
+          xmlelement           
+-------------------------------
+ <root xmlns="http:/x.y/ns1"/>
+(1 row)
+
+-- DEFAULT numeric xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73));
+      xmlelement       
+-----------------------
+ <root xmlns="42.73"/>
+(1 row)
+
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT ''));
+    xmlelement    
+------------------
+ <root xmlns=""/>
+(1 row)
+
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
+    xmlelement    
+------------------
+ <root xmlns=""/>
+(1 row)
+
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+ xmlelement 
+------------
+ <root/>
+(1 row)
+
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+            xmlelement             
+-----------------------------------
+ <root xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+                       xmlelement                        
+---------------------------------------------------------
+ <root xmlns="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+                    xmlelement                    
+--------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1">text node</root>
+(1 row)
+
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+                 xmlelement                  
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+                 xmlelement                  
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+                         xmlelement                          
+-------------------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+                              xmlelement                               
+-----------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+                                      xmlelement                                      
+--------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2">text node</root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "ns1:foo",
+      xmlnamespaces('http:/x.y/ns2' AS ns2),
+      xmlelement(NAME "ns2:bar", 'text node'))
+  );
+                                                         xmlelement                                                         
+----------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar></ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "ns1:foo",
+      xmlnamespaces('http:/x.y/ns2' AS ns2),
+      xmlelement(NAME "ns2:bar", 'text node'),
+      'mixed content')
+  );
+                                                               xmlelement                                                                
+-----------------------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar>mixed content</ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "foo",
+      xmlnamespaces(NO DEFAULT),'bar')
+  );
+                              xmlelement                              
+----------------------------------------------------------------------
+ <root att="val" xmlns="http:/x.y/ns1"><foo xmlns="">bar</foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+  xmlelement(NAME "root",
+    xmlattributes(73 AS att),
+    xmlnamespaces('http:/x.y/ns1' AS ns),
+    xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+    xmlcomment(':)'),
+    xmlconcat('foo', 'bar')
+  );
+                                                        xmlelement                                                        
+--------------------------------------------------------------------------------------------------------------------------
+ <root att="73" xmlns:ns="http:/x.y/ns1"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z>&lt;&amp;&gt;</ns:z><!--:)-->foobar</root>
+(1 row)
+
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+ERROR:  XML namespace "xmlns:ns1" appears more than once at character 70
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+ERROR:  syntax error at or near ")" at character 55
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+ERROR:  XML elements can have only a single [NO] DEFAULT namespace at character 71
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+ERROR:  XML elements can have only a single [NO] DEFAULT namespace at character 58
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+ERROR:  invalid XML namespace prefix "xmlns" at character 46
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+ERROR:  invalid XML namespace prefix "xml" at character 46
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+ERROR:  invalid XML namespace URI "http://www.w3.org/2000/xmlns/";
+DETAIL:  this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+ERROR:  invalid XML namespace URI "http://www.w3.org/XML/1998/namespace";
+DETAIL:  this URI is already bounded to standard a namespace prefix
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+ERROR:  invalid XML namespace URI "http://www.w3.org/2000/xmlns/";
+DETAIL:  this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+ERROR:  invalid XML namespace URI "http://www.w3.org/XML/1998/namespace";
+DETAIL:  this URI is already bounded to standard a namespace prefix
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+ERROR:  invalid XML namespace URI for "xmlns:ns"
+DETAIL:  a regular XML namespace cannot be a zero-length string
 SELECT xmlparse(content '');
  xmlparse 
 ----------
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index ef7dc03c69..500e2b4ca3 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -150,6 +150,157 @@ DETAIL:  This functionality requires the server to be built with libxml support.
 SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as funnier));
 ERROR:  unsupported XML feature
 DETAIL:  This functionality requires the server to be built with libxml support.
+-- DEFAULT NULL xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- DEFAULT xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1'));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- DEFAULT numeric xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT ''));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "ns1:foo",
+      xmlnamespaces('http:/x.y/ns2' AS ns2),
+      xmlelement(NAME "ns2:bar", 'text node'))
+  );
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "ns1:foo",
+      xmlnamespaces('http:/x.y/ns2' AS ns2),
+      xmlelement(NAME "ns2:bar", 'text node'),
+      'mixed content')
+  );
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "foo",
+      xmlnamespaces(NO DEFAULT),'bar')
+  );
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+  xmlelement(NAME "root",
+    xmlattributes(73 AS att),
+    xmlnamespaces('http:/x.y/ns1' AS ns),
+    xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+    xmlcomment(':)'),
+    xmlconcat('foo', 'bar')
+  );
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+ERROR:  unsupported XML feature
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+ERROR:  syntax error at or near ")" at character 55
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+ERROR:  unsupported XML feature
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+ERROR:  unsupported XML feature
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+ERROR:  unsupported XML feature
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+ERROR:  unsupported XML feature
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
 SELECT xmlparse(content '');
 ERROR:  unsupported XML feature
 DETAIL:  This functionality requires the server to be built with libxml support.
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index 4a9cdd2afe..a47cf27b6e 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -221,6 +221,211 @@ SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as fun
  <foo funny="&lt;&gt;&amp;&quot;'" funnier="b&lt;a/&gt;r"/>
 (1 row)
 
+-- DEFAULT NULL xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL));
+ xmlelement 
+------------
+ <root/>
+(1 row)
+
+-- DEFAULT xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1'));
+          xmlelement           
+-------------------------------
+ <root xmlns="http:/x.y/ns1"/>
+(1 row)
+
+-- DEFAULT numeric xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73));
+      xmlelement       
+-----------------------
+ <root xmlns="42.73"/>
+(1 row)
+
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT ''));
+    xmlelement    
+------------------
+ <root xmlns=""/>
+(1 row)
+
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
+    xmlelement    
+------------------
+ <root xmlns=""/>
+(1 row)
+
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+ xmlelement 
+------------
+ <root/>
+(1 row)
+
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+            xmlelement             
+-----------------------------------
+ <root xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+                       xmlelement                        
+---------------------------------------------------------
+ <root xmlns="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+                    xmlelement                    
+--------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1">text node</root>
+(1 row)
+
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+                 xmlelement                  
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+                 xmlelement                  
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+                         xmlelement                          
+-------------------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+                              xmlelement                               
+-----------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+                                      xmlelement                                      
+--------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2">text node</root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "ns1:foo",
+      xmlnamespaces('http:/x.y/ns2' AS ns2),
+      xmlelement(NAME "ns2:bar", 'text node'))
+  );
+                                                         xmlelement                                                         
+----------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar></ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "ns1:foo",
+      xmlnamespaces('http:/x.y/ns2' AS ns2),
+      xmlelement(NAME "ns2:bar", 'text node'),
+      'mixed content')
+  );
+                                                               xmlelement                                                                
+-----------------------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar>mixed content</ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "foo",
+      xmlnamespaces(NO DEFAULT),'bar')
+  );
+                              xmlelement                              
+----------------------------------------------------------------------
+ <root att="val" xmlns="http:/x.y/ns1"><foo xmlns="">bar</foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+  xmlelement(NAME "root",
+    xmlattributes(73 AS att),
+    xmlnamespaces('http:/x.y/ns1' AS ns),
+    xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+    xmlcomment(':)'),
+    xmlconcat('foo', 'bar')
+  );
+                                                        xmlelement                                                        
+--------------------------------------------------------------------------------------------------------------------------
+ <root att="73" xmlns:ns="http:/x.y/ns1"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z>&lt;&amp;&gt;</ns:z><!--:)-->foobar</root>
+(1 row)
+
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+ERROR:  XML namespace "xmlns:ns1" appears more than once at character 70
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+ERROR:  syntax error at or near ")" at character 55
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+ERROR:  XML elements can have only a single [NO] DEFAULT namespace at character 71
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+ERROR:  XML elements can have only a single [NO] DEFAULT namespace at character 58
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+ERROR:  invalid XML namespace prefix "xmlns" at character 46
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+ERROR:  invalid XML namespace prefix "xml" at character 46
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+ERROR:  invalid XML namespace URI "http://www.w3.org/2000/xmlns/";
+DETAIL:  this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+ERROR:  invalid XML namespace URI "http://www.w3.org/XML/1998/namespace";
+DETAIL:  this URI is already bounded to standard a namespace prefix
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+ERROR:  invalid XML namespace URI "http://www.w3.org/2000/xmlns/";
+DETAIL:  this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+ERROR:  invalid XML namespace URI "http://www.w3.org/XML/1998/namespace";
+DETAIL:  this URI is already bounded to standard a namespace prefix
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+ERROR:  invalid XML namespace URI for "xmlns:ns"
+DETAIL:  a regular XML namespace cannot be a zero-length string
 SELECT xmlparse(content '');
  xmlparse 
 ----------
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index f752ecb142..14493a154f 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -66,6 +66,106 @@ SELECT xmlelement(name foo, xmlattributes('2009-04-09 00:24:37'::timestamp as ba
 SELECT xmlelement(name foo, xmlattributes('infinity'::timestamp as bar));
 SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as funnier));
 
+-- DEFAULT NULL xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL));
+-- DEFAULT xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1'));
+-- DEFAULT numeric xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73));
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT ''));
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "ns1:foo",
+      xmlnamespaces('http:/x.y/ns2' AS ns2),
+      xmlelement(NAME "ns2:bar", 'text node'))
+  );
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "ns1:foo",
+      xmlnamespaces('http:/x.y/ns2' AS ns2),
+      xmlelement(NAME "ns2:bar", 'text node'),
+      'mixed content')
+  );
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+    xmlattributes('val' AS att),
+    xmlelement(NAME "foo",
+      xmlnamespaces(NO DEFAULT),'bar')
+  );
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+  xmlelement(NAME "root",
+    xmlattributes(73 AS att),
+    xmlnamespaces('http:/x.y/ns1' AS ns),
+    xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+    xmlcomment(':)'),
+    xmlconcat('foo', 'bar')
+  );
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+
 
 SELECT xmlparse(content '');
 SELECT xmlparse(content '  ');
-- 
2.34.1

Reply via email to