From 93ea0e1b07763bca7ffb1d4cf88cc380640604cb Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Thu, 20 Feb 2025 09:58:34 +0100
Subject: [PATCH v6] 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
-----------------
---
 doc/src/sgml/func.sgml              |  57 +++++-
 src/backend/parser/gram.y           | 100 +++++++++--
 src/backend/parser/parse_clause.c   |   7 +-
 src/backend/parser/parse_expr.c     |  80 +++++++++
 src/backend/utils/adt/ruleutils.c   |  79 +++++++--
 src/backend/utils/adt/xml.c         |  53 +++++-
 src/include/nodes/primnodes.h       |   4 +-
 src/include/utils/xml.h             |   6 +
 src/test/regress/expected/xml.out   | 259 ++++++++++++++++++++++++++++
 src/test/regress/expected/xml_1.out | 197 +++++++++++++++++++++
 src/test/regress/expected/xml_2.out | 259 ++++++++++++++++++++++++++++
 src/test/regress/sql/xml.sql        | 133 ++++++++++++++
 12 files changed, 1200 insertions(+), 34 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index df32ee0bf5..3a59c0d8c2 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14538,7 +14538,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>
@@ -14551,7 +14554,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>
@@ -14574,6 +14609,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 7d99c9355c..399f12b86c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -136,6 +136,12 @@ typedef struct KeyActions
 	KeyAction *deleteAction;
 } KeyActions;
 
+typedef struct XmlElementOpts
+{
+	List	   *xml_attributes;
+	List	   *xml_namespaces;
+} XmlElementOpts;
+
 /* ConstraintAttributeSpec yields an integer bitmask of these flags: */
 #define CAS_NOT_DEFERRABLE			0x01
 #define CAS_DEFERRABLE				0x02
@@ -187,7 +193,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, XmlElementOpts *opts,
 						 List *args, int location);
 static List *mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner);
 static TypeName *TableFuncTypeName(List *columns);
@@ -267,6 +273,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	MergeWhenClause *mergewhen;
 	struct KeyActions *keyactions;
 	struct KeyAction *keyaction;
+	struct XmlElementOpts *xmlelementopts;
 	ReturningClause *retclause;
 	ReturningOptionKind retoptionkind;
 }
@@ -618,8 +625,9 @@ 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 <xmlelementopts> xmlelement_opts
 
 %type <node>	func_application func_expr_common_subexpr
 %type <node>	func_expr func_expr_windowless
@@ -14285,6 +14293,15 @@ xml_namespace_el:
 					$$->val = $2;
 					$$->location = @1;
 				}
+			| NO DEFAULT
+				{
+					$$ = makeNode(ResTarget);
+					$$->name = NULL;
+					$$->indirection = NIL;
+					$$->val = NULL;
+					$$->location = @1;
+				}
+
 		;
 
 json_table:
@@ -15338,12 +15355,12 @@ a_expr:		c_expr									{ $$ = $1; }
 				}
 			| a_expr IS DOCUMENT_P					%prec IS
 				{
-					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NULL,
 									 list_make1($1), @2);
 				}
 			| a_expr IS NOT DOCUMENT_P				%prec IS
 				{
-					$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+					$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NULL,
 												 list_make1($1), @2),
 									 @2);
 				}
@@ -15485,12 +15502,12 @@ b_expr:		c_expr
 				}
 			| b_expr IS DOCUMENT_P					%prec IS
 				{
-					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NULL,
 									 list_make1($1), @2);
 				}
 			| b_expr IS NOT DOCUMENT_P				%prec IS
 				{
-					$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+					$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NULL,
 												 list_make1($1), @2),
 									 @2);
 				}
@@ -16025,21 +16042,21 @@ func_expr_common_subexpr:
 				}
 			| XMLCONCAT '(' expr_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1);
+					$$ = makeXmlExpr(IS_XMLCONCAT, NULL, NULL, $3, @1);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NULL, NIL, @1);
 				}
-			| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ')'
+			| XMLELEMENT '(' NAME_P ColLabel ',' xmlelement_opts ')'
 				{
 					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, @1);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ',' expr_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NULL, $6, @1);
 				}
-			| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' expr_list ')'
+			| XMLELEMENT '(' NAME_P ColLabel ',' xmlelement_opts ',' expr_list ')'
 				{
 					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1);
 				}
@@ -16054,12 +16071,17 @@ func_expr_common_subexpr:
 				}
 			| XMLFOREST '(' xml_attribute_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1);
+					XmlElementOpts opts;
+
+					opts.xml_attributes = $3;
+					opts.xml_namespaces = NIL;
+
+					$$ = makeXmlExpr(IS_XMLFOREST, NULL, &opts, NIL, @1);
 				}
 			| XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')'
 				{
 					XmlExpr *x = (XmlExpr *)
-						makeXmlExpr(IS_XMLPARSE, NULL, NIL,
+						makeXmlExpr(IS_XMLPARSE, NULL, NULL,
 									list_make2($4, makeBoolAConst($5, -1)),
 									@1);
 
@@ -16076,7 +16098,7 @@ func_expr_common_subexpr:
 				}
 			| XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')'
 				{
-					$$ = makeXmlExpr(IS_XMLROOT, NULL, NIL,
+					$$ = makeXmlExpr(IS_XMLROOT, NULL, NULL,
 									 list_make3($3, $5, $6), @1);
 				}
 			| XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename xml_indent_option ')'
@@ -16280,6 +16302,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); }
 		;
@@ -16316,6 +16341,44 @@ xml_whitespace_option: PRESERVE WHITESPACE_P		{ $$ = true; }
 			| /*EMPTY*/								{ $$ = false; }
 		;
 
+xmlelement_opts: xml_attributes
+				{
+					XmlElementOpts *n = palloc(sizeof(XmlElementOpts));
+
+					n->xml_attributes = $1;
+					n->xml_namespaces = NIL;
+					$$ = n;
+				}
+			| xml_namespaces
+				{
+					XmlElementOpts *n = palloc(sizeof(XmlElementOpts));
+
+					n->xml_attributes = NIL;
+					n->xml_namespaces = $1;
+					$$ = n;
+				}
+			| xmlelement_opts ',' xml_attributes
+				{
+					if ($$->xml_attributes)
+						ereport(ERROR,
+							(errcode(ERRCODE_DUPLICATE_OBJECT),
+							 errmsg("duplicate XMLATTRIBUTES specified"),
+							 parser_errposition(@3)));
+
+					$$->xml_attributes = $3;
+				}
+			| xmlelement_opts ',' xml_namespaces
+				{
+					if ($$->xml_namespaces)
+						ereport(ERROR,
+							(errcode(ERRCODE_DUPLICATE_OBJECT),
+							 errmsg("duplicate XMLNAMESACES specified"),
+							 parser_errposition(@3)));
+
+					$$->xml_namespaces = $3;
+				}
+		;
+
 /* We allow several variants for SQL and other compatibility. */
 xmlexists_argument:
 			PASSING c_expr
@@ -19290,7 +19353,7 @@ makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location)
 }
 
 static Node *
-makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
+makeXmlExpr(XmlExprOp op, char *name, XmlElementOpts *opts, List *args,
 			int location)
 {
 	XmlExpr    *x = makeNode(XmlExpr);
@@ -19302,7 +19365,12 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
 	 * named_args is a list of ResTarget; it'll be split apart into separate
 	 * expression and name lists in transformXmlExpr().
 	 */
-	x->named_args = named_args;
+	if (opts)
+	{
+		x->named_args = opts->xml_attributes;
+		x->xmlnamespaces = opts->xml_namespaces;
+	}
+
 	x->arg_names = NIL;
 	x->args = args;
 	/* xmloption, if relevant, must be filled in by caller */
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 2e64fcae7b..a4c04fa886 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -842,7 +842,12 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
 			Node	   *ns_uri;
 
 			Assert(IsA(r, ResTarget));
-			ns_uri = transformExpr(pstate, r->val, EXPR_KIND_FROM_FUNCTION);
+			/* Create an empty String for NO DEFAULT namespaces */
+			if (!r->name && !r->val)
+				ns_uri = transformExpr(pstate, makeStringConst("", r->location), EXPR_KIND_FROM_FUNCTION);
+			else
+				ns_uri = transformExpr(pstate, r->val, EXPR_KIND_FROM_FUNCTION);
+
 			ns_uri = coerce_to_specific_type(pstate, ns_uri,
 											 TEXTOID, constructName);
 			assign_expr_collations(pstate, ns_uri);
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index bad1df732e..93eeabdb9c 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_xmlns = false;
 
 	newx = makeNode(XmlExpr);
 	newx->op = x->op;
@@ -2366,6 +2367,80 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
 	newx->named_args = NIL;
 	newx->arg_names = NIL;
 
+	/*
+	 * this adds the xmlnamespaces into arg_names and named_args
+	 */
+	foreach (lc, x->xmlnamespaces)
+	{
+		ResTarget *r = lfirst_node(ResTarget, lc);
+		Node *expr;
+		ListCell *lc2;
+		char	   *argname = NULL;
+
+		/*
+		 * 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 (!r->name)
+		{
+			if (has_default_xmlns)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("XML elements can have only a single [NO] DEFAULT namespace"),
+						 parser_errposition(pstate, r->location)));
+
+			has_default_xmlns = 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)
+			argname = map_sql_identifier_to_xml_name(r->name, false, false);
+
+		else if (IsA(r->val, ColumnRef))
+			argname = map_sql_identifier_to_xml_name(FigureColname(r->val),
+													 true, false);
+
+		/*
+		 * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+		 * Syntax Rule 4) No two <XML namespace prefix>es shall be equivalent.
+		 */
+		if (x->op == IS_XMLELEMENT && argname)
+		{
+			foreach(lc2, newx->arg_names)
+			{
+				if (!strVal(lfirst(lc2)))
+					continue;
+
+				if (strVal(lfirst(lc2)) && strcmp(argname, strVal(lfirst(lc2))) == 0)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("XML namespace name \"%s\" appears more than once",
+									argname),
+							 parser_errposition(pstate, r->location)));
+			}
+		}
+
+		if(r->val)
+			expr = transformExprRecurse(pstate, r->val);
+		else
+			expr = transformExprRecurse(pstate, makeStringConst("", newx->location));
+
+		newx->named_args = lappend(newx->named_args, expr);
+		newx->arg_names = lappend(newx->arg_names, makeString(!argname ? "" : argname));
+		newx->xmlnamespaces = lappend(newx->xmlnamespaces, makeString(NAMESPACE_XMLNS_DEFAULT_PREFIX));
+	}
+
 	foreach(lc, x->named_args)
 	{
 		ResTarget  *r = lfirst_node(ResTarget, lc);
@@ -2397,6 +2472,10 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
 
 			foreach(lc2, newx->arg_names)
 			{
+
+				if (!strVal(lfirst(lc2)))
+					continue;
+
 				if (strcmp(argname, strVal(lfirst(lc2))) == 0)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2408,6 +2487,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
 
 		newx->named_args = lappend(newx->named_args, expr);
 		newx->arg_names = lappend(newx->arg_names, makeString(argname));
+		newx->xmlnamespaces = lappend(newx->xmlnamespaces, makeString(""));
 	}
 
 	/* The other arguments are of varying types depending on the function */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 54dad97555..2a05ea8648 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10004,6 +10004,7 @@ get_rule_expr(Node *node, deparse_context *context,
 				bool		needcomma = false;
 				ListCell   *arg;
 				ListCell   *narg;
+				ListCell   *nsarg;
 				Const	   *con;
 
 				switch (xexpr->op)
@@ -10047,26 +10048,84 @@ get_rule_expr(Node *node, deparse_context *context,
 				}
 				if (xexpr->named_args)
 				{
-					if (xexpr->op != IS_XMLFOREST)
+					bool hasFunctCall = false;
+
+					forthree(arg, xexpr->named_args, narg, xexpr->arg_names, nsarg, xexpr->xmlnamespaces)
 					{
+						Node *e = (Node *)lfirst(arg);
+						char *argname = strVal(lfirst(narg));
+						char *prefix = strVal(lfirst(nsarg));
+
+						/* we skip this entry, as it is not a XMLATTRIBUTES argument */
+						if (strlen(prefix) != 0)
+							continue;
+
+						if (!hasFunctCall)
+						{
+							if (xexpr->op != IS_XMLFOREST)
+							{
+								if (needcomma)
+									appendStringInfoString(buf, ", ");
+								appendStringInfoString(buf, "XMLATTRIBUTES(");
+								needcomma = false;
+							}
+
+							hasFunctCall = true;
+						}
+
 						if (needcomma)
 							appendStringInfoString(buf, ", ");
-						appendStringInfoString(buf, "XMLATTRIBUTES(");
-						needcomma = false;
+
+						get_rule_expr((Node *)e, context, true);
+						appendStringInfo(buf, " AS %s",
+										 quote_identifier(map_xml_name_to_sql_identifier(argname)));
+						needcomma = true;
 					}
-					forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
+					if (xexpr->op != IS_XMLFOREST && hasFunctCall)
+						appendStringInfoChar(buf, ')');
+
+					hasFunctCall = false;
+
+					forthree(arg, xexpr->named_args, narg, xexpr->arg_names, nsarg, xexpr->xmlnamespaces)
 					{
-						Node	   *e = (Node *) lfirst(arg);
-						char	   *argname = strVal(lfirst(narg));
+						Node *e = (Node *)lfirst(arg);
+						char *argname = strVal(lfirst(narg));
+						char *prefix = strVal(lfirst(nsarg));
+
+						/* we skip this entry, as it is not a XMLNAMESPACES argument */
+						if (strlen(prefix) == 0)
+							continue;
+
+						if (!hasFunctCall)
+						{
+							if (xexpr->op != IS_XMLFOREST)
+							{
+								if (needcomma)
+									appendStringInfoString(buf, ", ");
+								appendStringInfoString(buf, "XMLNAMESPACES(");
+								needcomma = false;
+							}
+
+							hasFunctCall = true;
+						}
 
 						if (needcomma)
 							appendStringInfoString(buf, ", ");
-						get_rule_expr((Node *) e, context, true);
-						appendStringInfo(buf, " AS %s",
-										 quote_identifier(map_xml_name_to_sql_identifier(argname)));
+
+						if (strlen(argname) == 0)
+						{
+							appendStringInfo(buf, "DEFAULT ");
+							get_rule_expr((Node *)e, context, true);
+						}
+						else
+						{
+							get_rule_expr((Node *)e, context, true);
+							appendStringInfo(buf, " AS %s",
+											 quote_identifier(map_xml_name_to_sql_identifier(argname)));
+						}
 						needcomma = true;
 					}
-					if (xexpr->op != IS_XMLFOREST)
+					if (xexpr->op != IS_XMLFOREST && hasFunctCall)
 						appendStringInfoChar(buf, ')');
 				}
 				if (xexpr->args)
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index db8d0d6a7e..2cdb85cce7 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
@@ -865,6 +864,7 @@ xmlelement(XmlExpr *xexpr,
 	int			i;
 	ListCell   *arg;
 	ListCell   *narg;
+	ListCell   *nsarg;
 	PgXmlErrorContext *xmlerrcxt;
 	volatile xmlBufferPtr buf = NULL;
 	volatile xmlTextWriterPtr writer = NULL;
@@ -926,12 +926,53 @@ xmlelement(XmlExpr *xexpr,
 
 		xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name);
 
-		forboth(arg, named_arg_strings, narg, xexpr->arg_names)
+		forthree(arg, named_arg_strings, narg, xexpr->arg_names, nsarg, xexpr->xmlnamespaces)
 		{
 			char	   *str = (char *) lfirst(arg);
 			char	   *argname = strVal(lfirst(narg));
+			char	   *prefix = strVal(lfirst(nsarg));
 
-			if (str)
+			if (str && strlen(prefix) != 0)
+			{
+				/*
+				 * 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 (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 (strlen(argname) != 0 && 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")));
+
+				/*
+				 * xmlTextWriterWriteAttributeNS
+				 *   prefix       - Namespace prefix for the attribute. Pass NULL for no prefix,
+				 *				   which means DEFAULT namespace.
+				 *   name         - Local name of the attribute (without prefix). This is the
+				 *				   actual attribute name.
+				 *   namespaceURI - Namespace URI associated with the prefix (NULL for none).
+				 *   content      - Value of the attribute.
+				 */
+				xmlTextWriterWriteAttributeNS(writer,
+											  strlen(argname) == 0 ? NULL : (const xmlChar *) prefix,
+											  strlen(argname) != 0 ? (const xmlChar *) argname : (const xmlChar *) prefix,
+											  NULL,
+											  (const xmlChar *) str);
+			}
+			else if (str)
 				xmlTextWriterWriteAttribute(writer,
 											(xmlChar *) argname,
 											(xmlChar *) str);
@@ -4789,7 +4830,11 @@ XmlTableSetNamespace(TableFuncScanState *state, const char *name, const char *ur
 #ifdef USE_LIBXML
 	XmlTableBuilderData *xtCxt;
 
-	if (name == NULL)
+	if (name == NULL && strlen(uri) == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("NO DEFAULT namespace is not supported")));
+	else if (name == NULL)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("DEFAULT namespace is not supported")));
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 839e71d52f..287e4bc749 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1583,7 +1583,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]) */
@@ -1607,6 +1607,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 0d7a816b9f..4a714dbca9 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 2e9616acda..4b300eba7e 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -225,6 +225,260 @@ 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)
+
+-- Testing xmlnamespace with subqueries
+SELECT
+  xmlelement(NAME root, xmlnamespaces(DEFAULT 'ns'),
+    (SELECT xmlelement(NAME child1, xmlnamespaces(NO DEFAULT))));
+                 xmlelement                 
+--------------------------------------------
+ <root xmlns="ns"><child1 xmlns=""/></root>
+(1 row)
+
+-- Testing xmlnamespace url from ColumnRef
+CREATE TABLE xmlns (url text);
+INSERT INTO xmlns VALUES ('http:/x.y/ns1');
+SELECT
+  xmlserialize(DOCUMENT
+  xmlelement(NAME root, xmlnamespaces(DEFAULT 'foo', url AS ns),
+    xmlelement(NAME root,
+      xmlelement(NAME child2, xmlnamespaces(url AS ns, NO DEFAULT))))
+  AS text INDENT)
+FROM xmlns;
+                  xmlserialize                   
+-------------------------------------------------
+ <root xmlns="foo" xmlns:ns="http:/x.y/ns1">    +
+   <root>                                       +
+     <child2 xmlns:ns="http:/x.y/ns1" xmlns=""/>+
+   </root>                                      +
+ </root>
+(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 xmlns:ns1="http:/x.y/ns1" att="val"/>
+(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 xmlns:ns1="http:/x.y/ns1" att="val"/>
+(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 xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2" att="val"/>
+(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 xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2" att="val">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 xmlns:ns1="http:/x.y/ns1" att="val"><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 xmlns:ns1="http:/x.y/ns1" att="val"><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 xmlns="http:/x.y/ns1" att="val"><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 xmlns:ns="http:/x.y/ns1" att="73"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z>&lt;&amp;&gt;</ns:z><!--:)-->foobar</root>
+(1 row)
+
+-- test xmlnamespaces within views
+CREATE VIEW view_xmlnamespaces AS
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), xmlattributes('val' AS att),
+    xmlelement(NAME "child1", xmlnamespaces('http:/x.y/ns2' AS ns2, NO DEFAULT)),
+    xmlcomment(':)'),
+    xmlconcat('foo', 'bar'),
+    xmlelement(NAME "child2", xmlnamespaces(DEFAULT NULL))) AS xmldoc;
+SELECT * FROM view_xmlnamespaces;
+                                                                   xmldoc                                                                    
+---------------------------------------------------------------------------------------------------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1" xmlns="http:/x.y/def" att="val"><child1 xmlns:ns2="http:/x.y/ns2" xmlns=""/><!--:)-->foobar<child2/></root>
+(1 row)
+
+\sv view_xmlnamespaces
+CREATE OR REPLACE VIEW public.view_xmlnamespaces AS
+ SELECT XMLELEMENT(NAME root, XMLATTRIBUTES('val' AS att), XMLNAMESPACES('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), XMLELEMENT(NAME child1, XMLNAMESPACES('http:/x.y/ns2' AS ns2, DEFAULT '')), xmlcomment(':)'::text), XMLCONCAT('foo'::xml, 'bar'::xml), XMLELEMENT(NAME child2, XMLNAMESPACES(DEFAULT NULL::unknown))) AS xmldoc
+\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 name "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
+-- duplicate xmlnamespace calls
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlnamespaces('http:/x.y/ns2' AS ns2));
+ERROR:  duplicate XMLNAMESACES specified at character 71
+\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 "ns"
+DETAIL:  a regular XML namespace cannot be a zero-length string
 SELECT xmlparse(content '');
  xmlparse 
 ----------
@@ -1398,6 +1652,11 @@ SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'),
                       PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
                       COLUMNS a int PATH 'a');
 ERROR:  DEFAULT namespace is not supported
+SELECT * FROM XMLTABLE(XMLNAMESPACES(NO DEFAULT),
+                      '/rows/row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'a');
+ERROR:  NO DEFAULT namespace is not supported
 SELECT * FROM XMLTABLE('.'
                        PASSING '<foo/>'
                        COLUMNS a text PATH 'foo/namespace::node()');
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 7505a14077..788d5fdb5f 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -150,6 +150,195 @@ 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.
+-- Testing xmlnamespace with subqueries
+SELECT
+  xmlelement(NAME root, xmlnamespaces(DEFAULT 'ns'),
+    (SELECT xmlelement(NAME child1, xmlnamespaces(NO DEFAULT))));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+-- Testing xmlnamespace url from ColumnRef
+CREATE TABLE xmlns (url text);
+INSERT INTO xmlns VALUES ('http:/x.y/ns1');
+SELECT
+  xmlserialize(DOCUMENT
+  xmlelement(NAME root, xmlnamespaces(DEFAULT 'foo', url AS ns),
+    xmlelement(NAME root,
+      xmlelement(NAME child2, xmlnamespaces(url AS ns, NO DEFAULT))))
+  AS text INDENT)
+FROM xmlns;
+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.
+-- test xmlnamespaces within views
+CREATE VIEW view_xmlnamespaces AS
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), xmlattributes('val' AS att),
+    xmlelement(NAME "child1", xmlnamespaces('http:/x.y/ns2' AS ns2, NO DEFAULT)),
+    xmlcomment(':)'),
+    xmlconcat('foo', 'bar'),
+    xmlelement(NAME "child2", xmlnamespaces(DEFAULT NULL))) AS xmldoc;
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT * FROM view_xmlnamespaces;
+ERROR:  relation "view_xmlnamespaces" does not exist
+LINE 1: SELECT * FROM view_xmlnamespaces;
+                      ^
+\sv view_xmlnamespaces
+ERROR:  relation "view_xmlnamespaces" does not exist
+\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
+-- duplicate xmlnamespace calls
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlnamespaces('http:/x.y/ns2' AS ns2));
+ERROR:  duplicate XMLNAMESACES specified at character 71
+\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.
@@ -1068,6 +1257,14 @@ ERROR:  unsupported XML feature
 LINE 3:                       PASSING '<rows xmlns="http://x.y"><row...
                                       ^
 DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT * FROM XMLTABLE(XMLNAMESPACES(NO DEFAULT),
+                      '/rows/row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'a');
+ERROR:  unsupported XML feature
+LINE 3:                       PASSING '<rows xmlns="http://x.y"><row...
+                                      ^
+DETAIL:  This functionality requires the server to be built with libxml support.
 SELECT * FROM XMLTABLE('.'
                        PASSING '<foo/>'
                        COLUMNS a text PATH 'foo/namespace::node()');
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index c07ed2b269..a2abcf89b5 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -221,6 +221,260 @@ 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)
+
+-- Testing xmlnamespace with subqueries
+SELECT
+  xmlelement(NAME root, xmlnamespaces(DEFAULT 'ns'),
+    (SELECT xmlelement(NAME child1, xmlnamespaces(NO DEFAULT))));
+                 xmlelement                 
+--------------------------------------------
+ <root xmlns="ns"><child1 xmlns=""/></root>
+(1 row)
+
+-- Testing xmlnamespace url from ColumnRef
+CREATE TABLE xmlns (url text);
+INSERT INTO xmlns VALUES ('http:/x.y/ns1');
+SELECT
+  xmlserialize(DOCUMENT
+  xmlelement(NAME root, xmlnamespaces(DEFAULT 'foo', url AS ns),
+    xmlelement(NAME root,
+      xmlelement(NAME child2, xmlnamespaces(url AS ns, NO DEFAULT))))
+  AS text INDENT)
+FROM xmlns;
+                  xmlserialize                   
+-------------------------------------------------
+ <root xmlns="foo" xmlns:ns="http:/x.y/ns1">    +
+   <root>                                       +
+     <child2 xmlns:ns="http:/x.y/ns1" xmlns=""/>+
+   </root>                                      +
+ </root>
+(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 xmlns:ns1="http:/x.y/ns1" att="val"/>
+(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 xmlns:ns1="http:/x.y/ns1" att="val"/>
+(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 xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2" att="val"/>
+(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 xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2" att="val">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 xmlns:ns1="http:/x.y/ns1" att="val"><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 xmlns:ns1="http:/x.y/ns1" att="val"><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 xmlns="http:/x.y/ns1" att="val"><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 xmlns:ns="http:/x.y/ns1" att="73"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z>&lt;&amp;&gt;</ns:z><!--:)-->foobar</root>
+(1 row)
+
+-- test xmlnamespaces within views
+CREATE VIEW view_xmlnamespaces AS
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), xmlattributes('val' AS att),
+    xmlelement(NAME "child1", xmlnamespaces('http:/x.y/ns2' AS ns2, NO DEFAULT)),
+    xmlcomment(':)'),
+    xmlconcat('foo', 'bar'),
+    xmlelement(NAME "child2", xmlnamespaces(DEFAULT NULL))) AS xmldoc;
+SELECT * FROM view_xmlnamespaces;
+                                                                   xmldoc                                                                    
+---------------------------------------------------------------------------------------------------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1" xmlns="http:/x.y/def" att="val"><child1 xmlns:ns2="http:/x.y/ns2" xmlns=""/><!--:)-->foobar<child2/></root>
+(1 row)
+
+\sv view_xmlnamespaces
+CREATE OR REPLACE VIEW public.view_xmlnamespaces AS
+ SELECT XMLELEMENT(NAME root, XMLATTRIBUTES('val' AS att), XMLNAMESPACES('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), XMLELEMENT(NAME child1, XMLNAMESPACES('http:/x.y/ns2' AS ns2, DEFAULT '')), xmlcomment(':)'::text), XMLCONCAT('foo'::xml, 'bar'::xml), XMLELEMENT(NAME child2, XMLNAMESPACES(DEFAULT NULL::unknown))) AS xmldoc
+\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 name "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
+-- duplicate xmlnamespace calls
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlnamespaces('http:/x.y/ns2' AS ns2));
+ERROR:  duplicate XMLNAMESACES specified at character 71
+\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 "ns"
+DETAIL:  a regular XML namespace cannot be a zero-length string
 SELECT xmlparse(content '');
  xmlparse 
 ----------
@@ -1384,6 +1638,11 @@ SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'),
                       PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
                       COLUMNS a int PATH 'a');
 ERROR:  DEFAULT namespace is not supported
+SELECT * FROM XMLTABLE(XMLNAMESPACES(NO DEFAULT),
+                      '/rows/row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'a');
+ERROR:  NO DEFAULT namespace is not supported
 SELECT * FROM XMLTABLE('.'
                        PASSING '<foo/>'
                        COLUMNS a text PATH 'foo/namespace::node()');
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index bac0388ac1..0a14a3deac 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -66,6 +66,134 @@ 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));
+-- Testing xmlnamespace with subqueries
+SELECT
+  xmlelement(NAME root, xmlnamespaces(DEFAULT 'ns'),
+    (SELECT xmlelement(NAME child1, xmlnamespaces(NO DEFAULT))));
+-- Testing xmlnamespace url from ColumnRef
+CREATE TABLE xmlns (url text);
+INSERT INTO xmlns VALUES ('http:/x.y/ns1');
+SELECT
+  xmlserialize(DOCUMENT
+  xmlelement(NAME root, xmlnamespaces(DEFAULT 'foo', url AS ns),
+    xmlelement(NAME root,
+      xmlelement(NAME child2, xmlnamespaces(url AS ns, NO DEFAULT))))
+  AS text INDENT)
+FROM xmlns;
+-- 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')
+  );
+-- test xmlnamespaces within views
+CREATE VIEW view_xmlnamespaces AS
+SELECT
+  xmlelement(NAME "root",
+    xmlnamespaces('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), xmlattributes('val' AS att),
+    xmlelement(NAME "child1", xmlnamespaces('http:/x.y/ns2' AS ns2, NO DEFAULT)),
+    xmlcomment(':)'),
+    xmlconcat('foo', 'bar'),
+    xmlelement(NAME "child2", xmlnamespaces(DEFAULT NULL))) AS xmldoc;
+SELECT * FROM view_xmlnamespaces;
+\sv view_xmlnamespaces
+
+\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));
+-- duplicate xmlnamespace calls
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlnamespaces('http:/x.y/ns2' AS ns2));
+\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 '  ');
@@ -453,6 +581,11 @@ SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'),
                       PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
                       COLUMNS a int PATH 'a');
 
+SELECT * FROM XMLTABLE(XMLNAMESPACES(NO DEFAULT),
+                      '/rows/row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'a');
+
 SELECT * FROM XMLTABLE('.'
                        PASSING '<foo/>'
                        COLUMNS a text PATH 'foo/namespace::node()');
-- 
2.34.1

