rebased due to recent changes in doc/src/sgml/func.sgml
--
Jim
From f1b797c15f4214bf654a3ca9c82d51f0fef93cbd Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Thu, 7 Aug 2025 13:57:03 +0200
Subject: [PATCH v10] Add XMLNamespaces option to XMLElement
This patch adds support for the scoped option XMLNamespaces in the
XMLElement() function, as specified in ISO/IEC 9075-14:2023, section 11.2,
"XML lexically scoped options".
The XMLNamespaces clause allows users to declare XML namespace prefixes
and a default namespace within the scope of an XMLElement() call.
These declarations affect both the constructed element and its content,
enabling generation of namespaced XML content in a standards-compliant way.
== Syntax ==
xmlnamespaces(uri AS prefix, ...)
xmlnamespaces(DEFAULT uri, ...)
xmlnamespaces(NO DEFAULT, ...)
* prefix: Namespace prefix to associate with the URI
* uri: The namespace URI
* DEFAULT uri: Specifies the default namespace within the scope
* NO DEFAULT: Specifies that no default namespace is in effect
== Examples ==
SELECT xmlelement(NAME "foo", xmlnamespaces('http:/x.y' AS bar));
SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y'));
SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
This feature enables standards-compliant construction of namespaced XML
directly from SQL, improving interoperability with XML-based applications
and document workflows.
---
doc/src/sgml/func/func-xml.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 | 63 ++++++-
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, 1205 insertions(+), 39 deletions(-)
diff --git a/doc/src/sgml/func/func-xml.sgml b/doc/src/sgml/func/func-xml.sgml
index 21f34467a4..5eb3d44357 100644
--- a/doc/src/sgml/func/func-xml.sgml
+++ b/doc/src/sgml/func/func-xml.sgml
@@ -158,7 +158,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>
@@ -171,7 +174,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>
@@ -194,6 +229,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 db43034b9d..a8499a599d 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
@@ -186,7 +192,7 @@ static Node *makeNotExpr(Node *expr, int location);
static Node *makeAArrayExpr(List *elements, int location, int end_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);
@@ -266,6 +272,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;
}
@@ -617,8 +624,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
@@ -14345,6 +14353,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = NULL;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15399,12 +15416,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);
}
@@ -15546,12 +15563,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);
}
@@ -16086,21 +16103,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);
}
@@ -16115,12 +16132,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);
@@ -16137,7 +16159,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 ')'
@@ -16341,6 +16363,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); }
;
@@ -16377,6 +16402,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
@@ -19299,7 +19362,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);
@@ -19311,7 +19374,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 9f20a70ce1..f6b512505c 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 d66276801c..3306c534fe 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2359,6 +2359,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
XmlExpr *newx;
ListCell *lc;
int i;
+ bool has_default_xmlns = false;
newx = makeNode(XmlExpr);
newx->op = x->op;
@@ -2378,6 +2379,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);
@@ -2409,6 +2484,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),
@@ -2420,6 +2499,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 3d6e6bdbfd..5eba6d0ae8 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10066,6 +10066,7 @@ get_rule_expr(Node *node, deparse_context *context,
bool needcomma = false;
ListCell *arg;
ListCell *narg;
+ ListCell *nsarg;
Const *con;
switch (xexpr->op)
@@ -10109,26 +10110,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 182e8f75db..455ff16dd1 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
@@ -902,6 +901,7 @@ xmlelement(XmlExpr *xexpr,
int i;
ListCell *arg;
ListCell *narg;
+ ListCell *nsarg;
PgXmlErrorContext *xmlerrcxt;
volatile xmlBufferPtr buf = NULL;
volatile xmlTextWriterPtr writer = NULL;
@@ -966,20 +966,61 @@ xmlelement(XmlExpr *xexpr,
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
"could not start xml element");
- 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 *str = (char *)lfirst(arg);
+ char *argname = strVal(lfirst(narg));
+ char *prefix = strVal(lfirst(nsarg));
- if (str)
+ if (str && strlen(prefix) != 0)
{
- if (xmlTextWriterWriteAttribute(writer,
- (xmlChar *) argname,
- (xmlChar *) str) < 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.
+ */
+ if (xmlTextWriterWriteAttributeNS(writer,
+ strlen(argname) == 0 ? NULL : (const xmlChar *)prefix,
+ strlen(argname) != 0 ? (const xmlChar *)argname : (const xmlChar *)prefix,
+ NULL,
+ (const xmlChar *)str) < 0 ||
xmlerrcxt->err_occurred)
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
"could not write xml attribute");
}
+ else if (str && (xmlTextWriterWriteAttribute(writer,
+ (xmlChar *)argname,
+ (xmlChar *)str) < 0 ||
+ xmlerrcxt->err_occurred))
+ xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+ "could not write xml attribute");
}
foreach(arg, arg_strings)
@@ -4837,7 +4878,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 6dfca3cb35..020c963c0e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1589,7 +1589,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]) */
@@ -1613,6 +1613,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 103a22a3b1..8864ed1812 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="<>&"'" funnier="b<a/>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><&></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
----------
@@ -1406,6 +1660,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 73c411118a..48024e9d2d 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.
@@ -1082,6 +1271,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 a85d95358d..4bb296f7c2 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="<>&"'" funnier="b<a/>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><&></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
----------
@@ -1392,6 +1646,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 0ea4f50883..c767a0a3f2 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 ' ');
@@ -457,6 +585,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.43.0