Add XMLNamespaces to XMLElement
Hi,
I'd like to propose the implementation of the XMLNamespaces option for
XMLElement.
XMLNAMESPACES(nsuri AS nsprefix)
XMLNAMESPACES(DEFAULT default-nsuri)
XMLNAMESPACES(NO DEFAULT)
* nsprefix: Namespace's prefix.
* nsuri: Namespace's URI.
* DEFAULT default-nsuri: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT: Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.
This basically works pretty much like XMLAttributes, but with a few more
restrictions (see SQL/XML:2023, 11.2 <XML lexically scoped options>):
* XML namespace declaration shall contain at most one DEFAULT namespace
declaration item.
* No namespace prefix shall be equivalent to xml or xmlns.
* No namespace URI shall be identical to http://www.w3.org/2000/xmlns/
or to http://www.w3.org/XML/1998/namespace.
* The value of a namespace URI contained in an regular namespace
declaration item (no DEFAULT) shall not be a zero-length string.
Examples:
SELECT xmlelement(NAME "foo", xmlnamespaces('http://x.y' AS bar));
xmlelement
-------------------------------
<foo xmlns:bar="http://x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http://x.y'));
xmlelement
---------------------------
<foo xmlns="http://x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
xmlelement
-----------------
<foo xmlns=""/>
In transformXmlExpr() it seemed convenient to use the same parameters to
store the prefixes and URIs as in XMLAttributes (arg_names and
named_args), but I am still not so sure it is the right approach. Is
there perhaps a better way?
Any thoughts? Feedback welcome!
Best, Jim
Attachments:
v1-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v1-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From 8dee3772be0d89b3d49eff17344ff53440e5f566 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Fri, 20 Dec 2024 14:50:51 +0100
Subject: [PATCH v1] Add XMLNamespaces option to XMLElement
This patch adds the scoped option XMLNamespaces to XMLElement,
as described in ISO/IEC 9075-14:2023, 11.2 XML lexically scoped
options:
xmlnamespaces(uri AS prefix, ...)
xmlnamespaces(DEFAULT uri, ...)
xmlnamespaces(NO DEFAULT, ...)
* prefix: Namespace's prefix.
* uri: Namespace's URI.
* DEFAULT prefix: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT: Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.
== Examples ==
SELECT xmlelement(NAME "foo", xmlnamespaces('http:/x.y' AS bar));
xmlelement
------------------------------
<foo xmlns:bar="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y'));
xmlelement
--------------------------
<foo xmlns="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
xmlelement
-----------------
<foo xmlns=""/>
Tests and documentation were updated accordingly.
---
doc/src/sgml/func.sgml | 57 +++++++-
src/backend/parser/gram.y | 73 +++++++---
src/backend/parser/parse_expr.c | 73 ++++++++++
src/backend/utils/adt/xml.c | 32 ++++-
src/include/nodes/primnodes.h | 4 +-
src/include/utils/xml.h | 6 +
src/test/regress/expected/xml.out | 205 ++++++++++++++++++++++++++++
src/test/regress/expected/xml_1.out | 151 ++++++++++++++++++++
src/test/regress/expected/xml_2.out | 205 ++++++++++++++++++++++++++++
src/test/regress/sql/xml.sql | 100 ++++++++++++++
10 files changed, 884 insertions(+), 22 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 47370e581a..b33663bc09 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14482,7 +14482,10 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
</indexterm>
<synopsis>
-<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> <optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
+<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable>
+ <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional>
+ <optional>, <literal>XMLNAMESPACES</literal> ( {<replaceable>regular-nsuri</replaceable> <literal>AS</literal> <replaceable>nsprefix</replaceable> | DEFAULT <replaceable>default-nsuri</replaceable> | NO DEFAULT} <optional>, ...</optional> ) </optional>
+<optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
</synopsis>
<para>
@@ -14495,7 +14498,39 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
yield any <productname>PostgreSQL</productname> data type. The
argument(s) within <literal>XMLATTRIBUTES</literal> generate attributes
of the XML element; the <replaceable>content</replaceable> value(s) are
- concatenated to form its content.
+ concatenated to form its content. The arguments within <literal>XMLNAMESPACES</literal>
+ constuct namespace declarations from values provided in <replaceable>nsuri</replaceable>
+ and <replaceable>nsprefix</replaceable>, which correspond to the URI of a namespace and
+ its prefix, respectively. The option <literal>DEFAULT</literal> can be used to set the
+ default namespace declaration (without a prefix) to the URI provided in <replaceable>default-nsuri</replaceable>.
+ The option <literal>NO DEFAULT</literal> states that a namespace scope has no default namespace. A valid
+ <literal>XMLNAMESPACES</literal> item must fulfill the following conditions:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Only a single <literal>DEFAULT</literal> declaration item within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No two <replaceable>nsuri</replaceable> can be equal within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No <replaceable>nsprefix</replaceable> can be equal to <literal>xml</literal> or <literal>xmlns</literal>,
+ and no <replaceable>nsuri</replaceable> can be equal to <literal>http://www.w3.org/2000/xmlns/</literal>
+ or to <literal>http://www.w3.org/XML/1998/namespace</literal>, as they are already bouned to standard XML declarations.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The value of a <replaceable>regular-nsuri</replaceable> cannot be a zero-length string.
+ </para>
+ </listitem>
+ </itemizedlist>
+
</para>
<para>
@@ -14518,6 +14553,24 @@ SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');
xmlelement
-------------------------------------
<foo bar="2007-01-26">content</foo>
+
+SELECT xmlelement(NAME "foo:root", xmlnamespaces('http:/foo.bar/' AS foo), 'content');
+
+ xmlelement
+---------------------------------------------------------
+ <foo:root xmlns:foo="http:/foo.bar/">content</foo:root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/foo.bar/'), 'content');
+
+ xmlelement
+---------------------------------------------
+ <root xmlns="http:/foo.bar/">content</root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT), 'content');
+
+ xmlelement
+-------------------------------
+ <root xmlns="">content</root>
]]></screen>
</para>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 67eb96396a..e0020f88bc 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -185,7 +185,7 @@ static Node *makeNotExpr(Node *expr, int location);
static Node *makeAArrayExpr(List *elements, int location);
static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod,
int location);
-static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
+static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *xmlnamespaces,
List *args, int location);
static List *mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner);
static TypeName *TableFuncTypeName(List *columns);
@@ -610,7 +610,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> xmltable_column_list xmltable_column_option_list
%type <node> xmltable_column_el
%type <defelt> xmltable_column_option_el
-%type <list> xml_namespace_list
+%type <list> xml_namespace_list xml_namespaces
%type <target> xml_namespace_el
%type <node> func_application func_expr_common_subexpr
@@ -14210,6 +14210,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = makeStringConst("", @1);;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15263,12 +15272,12 @@ a_expr: c_expr { $$ = $1; }
}
| a_expr IS DOCUMENT_P %prec IS
{
- $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+ $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
list_make1($1), @2);
}
| a_expr IS NOT DOCUMENT_P %prec IS
{
- $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+ $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
list_make1($1), @2),
@2);
}
@@ -15410,12 +15419,12 @@ b_expr: c_expr
}
| b_expr IS DOCUMENT_P %prec IS
{
- $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+ $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
list_make1($1), @2);
}
| b_expr IS NOT DOCUMENT_P %prec IS
{
- $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+ $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
list_make1($1), @2),
@2);
}
@@ -15950,23 +15959,47 @@ func_expr_common_subexpr:
}
| XMLCONCAT '(' expr_list ')'
{
- $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1);
+ $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, NIL, $3, @1);
}
| XMLELEMENT '(' NAME_P ColLabel ')'
{
- $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1);
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, NIL, @1);
}
| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ')'
{
- $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, @1);
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, NIL, @1);
}
| XMLELEMENT '(' NAME_P ColLabel ',' expr_list ')'
{
- $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, @1);
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, $6, @1);
}
| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' expr_list ')'
{
- $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1);
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, $8, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' expr_list ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, $8, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, NIL, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' xml_namespaces ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, NIL, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' xml_attributes ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $8, $6, NIL, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' xml_namespaces ',' expr_list ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, $10, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' xml_attributes ',' expr_list ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $8, $6, $10, @1);
}
| XMLEXISTS '(' c_expr xmlexists_argument ')'
{
@@ -15979,12 +16012,12 @@ func_expr_common_subexpr:
}
| XMLFOREST '(' xml_attribute_list ')'
{
- $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1);
+ $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, NIL, @1);
}
| XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')'
{
XmlExpr *x = (XmlExpr *)
- makeXmlExpr(IS_XMLPARSE, NULL, NIL,
+ makeXmlExpr(IS_XMLPARSE, NULL, NIL, NIL,
list_make2($4, makeBoolAConst($5, -1)),
@1);
@@ -15993,15 +16026,15 @@ func_expr_common_subexpr:
}
| XMLPI '(' NAME_P ColLabel ')'
{
- $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, @1);
+ $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, NIL, @1);
}
| XMLPI '(' NAME_P ColLabel ',' a_expr ')'
{
- $$ = makeXmlExpr(IS_XMLPI, $4, NULL, list_make1($6), @1);
+ $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, list_make1($6), @1);
}
| XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')'
{
- $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL,
+ $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, NIL,
list_make3($3, $5, $6), @1);
}
| XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename xml_indent_option ')'
@@ -16205,6 +16238,9 @@ opt_xml_root_standalone: ',' STANDALONE_P YES_P
xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; }
;
+xml_namespaces: XMLNAMESPACES '(' xml_namespace_list ')' { $$ = $3; }
+ ;
+
xml_attribute_list: xml_attribute_el { $$ = list_make1($1); }
| xml_attribute_list ',' xml_attribute_el { $$ = lappend($1, $3); }
;
@@ -19211,8 +19247,8 @@ makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location)
}
static Node *
-makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
- int location)
+makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *xmlnamespaces,
+ List *args, int location)
{
XmlExpr *x = makeNode(XmlExpr);
@@ -19223,6 +19259,7 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
* expression and name lists in transformXmlExpr().
*/
x->named_args = named_args;
+ x->xmlnamespaces = xmlnamespaces;
x->arg_names = NIL;
x->args = args;
/* xmloption, if relevant, must be filled in by caller */
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index c2806297aa..783184d782 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2347,6 +2347,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
XmlExpr *newx;
ListCell *lc;
int i;
+ bool has_default_ns = false;
newx = makeNode(XmlExpr);
newx->op = x->op;
@@ -2467,6 +2468,78 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
i++;
}
+ /*
+ * this adds the xmlnamespaces into arg_names and named_args
+ */
+ foreach (lc, x->xmlnamespaces)
+ {
+ ResTarget *r = lfirst_node(ResTarget, lc);
+ Node *expr;
+ StringInfoData nsbuf;
+
+ expr = transformExprRecurse(pstate, r->val);
+ initStringInfo(&nsbuf);
+
+ if (!r->name)
+ {
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 2) <XML namespace declaration> shall contain at most one
+ * <XML default namespace declaration item>.
+ */
+ if (has_default_ns)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("XML elements can have only a single [NO] DEFAULT namespace"),
+ parser_errposition(pstate, r->location)));
+ else
+ appendStringInfo(&nsbuf, NAMESPACE_XMLNS_DEFAULT_PREFIX);
+
+ has_default_ns = true;
+ }
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 5) No <XML namespace prefix> shall be equivalent to
+ * "xml" or "xmlns".
+ */
+ else if (strcmp(r->name, NAMESPACE_XMLNS_DEFAULT_PREFIX) == 0 ||
+ strcmp(r->name, NAMESPACE_XML_DEFAULT_PREFIX) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid XML namespace prefix \"%s\"", r->name),
+ errdetail("this prefix is already bounded to a standard namespace URI"),
+ parser_errposition(pstate, r->location)));
+ else if (r->name)
+ appendStringInfo(&nsbuf, "%s:%s", NAMESPACE_XMLNS_DEFAULT_PREFIX,
+ map_sql_identifier_to_xml_name(r->name, false, false));
+ else if (IsA(r->val, ColumnRef))
+ appendStringInfo(&nsbuf, "%s:%s", NAMESPACE_XMLNS_DEFAULT_PREFIX,
+ map_sql_identifier_to_xml_name(FigureColname(r->val), true, false));
+
+ /* reject duplicate argnames in XMLELEMENT only */
+ if (x->op == IS_XMLELEMENT)
+ {
+ ListCell *lc2;
+
+ foreach (lc2, newx->arg_names)
+ {
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 4) No two <XML namespace prefix>es shall be equivalent.
+ */
+ if (strcmp(nsbuf.data, strVal(lfirst(lc2))) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("XML namespace \"%s\" appears more than once",
+ nsbuf.data),
+ parser_errposition(pstate, r->location)));
+ }
+ }
+
+ newx->named_args = lappend(newx->named_args, expr);
+ newx->arg_names = lappend(newx->arg_names, makeString(nsbuf.data));
+ }
+
return (Node *) newx;
}
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 0898cb1be4..70fec22e21 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -244,7 +244,6 @@ const TableFuncRoutine XmlTableRoutine =
#define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
#define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
-
#ifdef USE_LIBXML
static int
@@ -931,6 +930,37 @@ xmlelement(XmlExpr *xexpr,
char *str = (char *) lfirst(arg);
char *argname = strVal(lfirst(narg));
+ StringInfoData xmlns_prefix;
+ initStringInfo(&xmlns_prefix);
+ appendStringInfo(&xmlns_prefix, "%s:", NAMESPACE_XMLNS_DEFAULT_PREFIX);
+
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 6) No <XML namespace URI> shall be identical, as defined
+ * in XML Namespaces, to http://www.w3.org/2000/xmlns/ or to
+ * http://www.w3.org/XML/1998/namespace
+ */
+ if (str && ((strncmp(argname, xmlns_prefix.data, xmlns_prefix.len) == 0 ||
+ strcmp(argname, NAMESPACE_XMLNS_DEFAULT_PREFIX) == 0) &&
+ (strcmp(str, NAMESPACE_XMLNS) == 0 || strcmp(str, NAMESPACE_XML) == 0)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid XML namespace URI \"%s\"", str),
+ errdetail("this URI is already bounded to standard a namespace prefix")));
+
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 7) The value of an <XML namespace URI> contained in an
+ * <XML regular namespace declaration item> shall not be a zero-length string.
+ */
+ if (strncmp(argname, xmlns_prefix.data, 6) == 0 && (str && strlen(str) == 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING),
+ errmsg("invalid XML namespace URI for \"%s\"", argname),
+ errdetail("a regular XML namespace cannot be a zero-length string")));
+
+ pfree(xmlns_prefix.data);
+
if (str)
xmlTextWriterWriteAttribute(writer,
(xmlChar *) argname,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index b0ef1952e8..2e5ad905a0 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1578,7 +1578,7 @@ typedef struct SQLValueFunction
typedef enum XmlExprOp
{
IS_XMLCONCAT, /* XMLCONCAT(args) */
- IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, args) */
+ IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, xml_namespaces, args) */
IS_XMLFOREST, /* XMLFOREST(xml_attributes) */
IS_XMLPARSE, /* XMLPARSE(text, is_doc, preserve_ws) */
IS_XMLPI, /* XMLPI(name [, args]) */
@@ -1602,6 +1602,8 @@ typedef struct XmlExpr
char *name pg_node_attr(query_jumble_ignore);
/* non-XML expressions for xml_attributes */
List *named_args;
+ /* non-XML expressions for XMLNAMESPACES */
+ List *xmlnamespaces;
/* parallel list of String values */
List *arg_names pg_node_attr(query_jumble_ignore);
/* list of expressions */
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index ed20e21375..4cd365d414 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -59,6 +59,12 @@ XmlPGetDatum(const xmltype *X)
return PointerGetDatum(X);
}
+/* reserved prefixes and URIs for XMLNamespace() from SQL/XML:2023, 11.2 */
+#define NAMESPACE_XMLNS "http://www.w3.org/2000/xmlns/"
+#define NAMESPACE_XML "http://www.w3.org/XML/1998/namespace"
+#define NAMESPACE_XMLNS_DEFAULT_PREFIX "xmlns"
+#define NAMESPACE_XML_DEFAULT_PREFIX "xml"
+
#define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n))
#define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x)
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index fb5f345855..c9be4a3d72 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -225,6 +225,211 @@ SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as fun
<foo funny="<>&"'" 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)
+
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+ xmlelement
+------------
+ <root/>
+(1 row)
+
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+ xmlelement
+-----------------------------------
+ <root xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+ xmlelement
+---------------------------------------------------------
+ <root xmlns="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+ xmlelement
+--------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1">text node</root>
+(1 row)
+
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+ xmlelement
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+ xmlelement
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+ xmlelement
+-------------------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+ xmlelement
+-----------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+ xmlelement
+--------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2">text node</root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'))
+ );
+ xmlelement
+----------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar></ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'),
+ 'mixed content')
+ );
+ xmlelement
+-----------------------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar>mixed content</ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "foo",
+ xmlnamespaces(NO DEFAULT),'bar')
+ );
+ xmlelement
+----------------------------------------------------------------------
+ <root att="val" xmlns="http:/x.y/ns1"><foo xmlns="">bar</foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+ xmlelement(NAME "root",
+ xmlattributes(73 AS att),
+ xmlnamespaces('http:/x.y/ns1' AS ns),
+ xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+ xmlcomment(':)'),
+ xmlconcat('foo', 'bar')
+ );
+ xmlelement
+--------------------------------------------------------------------------------------------------------------------------
+ <root att="73" xmlns:ns="http:/x.y/ns1"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z><&></ns:z><!--:)-->foobar</root>
+(1 row)
+
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+ERROR: XML namespace "xmlns:ns1" appears more than once at character 70
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+ERROR: syntax error at or near ")" at character 55
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 71
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 58
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+ERROR: invalid XML namespace prefix "xmlns" at character 46
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+ERROR: invalid XML namespace prefix "xml" at character 46
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/"
+DETAIL: this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace"
+DETAIL: this URI is already bounded to standard a namespace prefix
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/"
+DETAIL: this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace"
+DETAIL: this URI is already bounded to standard a namespace prefix
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+ERROR: invalid XML namespace URI for "xmlns:ns"
+DETAIL: a regular XML namespace cannot be a zero-length string
SELECT xmlparse(content '');
xmlparse
----------
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index ef7dc03c69..500e2b4ca3 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -150,6 +150,157 @@ DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as funnier));
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT NULL xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1'));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT numeric xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT ''));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'))
+ );
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'),
+ 'mixed content')
+ );
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "foo",
+ xmlnamespaces(NO DEFAULT),'bar')
+ );
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+ xmlelement(NAME "root",
+ xmlattributes(73 AS att),
+ xmlnamespaces('http:/x.y/ns1' AS ns),
+ xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+ xmlcomment(':)'),
+ xmlconcat('foo', 'bar')
+ );
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+ERROR: unsupported XML feature
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+ERROR: syntax error at or near ")" at character 55
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+ERROR: unsupported XML feature
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+ERROR: unsupported XML feature
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+ERROR: unsupported XML feature
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+ERROR: unsupported XML feature
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlparse(content '');
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index 4a9cdd2afe..a47cf27b6e 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -221,6 +221,211 @@ SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as fun
<foo funny="<>&"'" 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)
+
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+ xmlelement
+------------
+ <root/>
+(1 row)
+
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+ xmlelement
+-----------------------------------
+ <root xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+ xmlelement
+---------------------------------------------------------
+ <root xmlns="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+ xmlelement
+--------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1">text node</root>
+(1 row)
+
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+ xmlelement
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+ xmlelement
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+ xmlelement
+-------------------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+ xmlelement
+-----------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+ xmlelement
+--------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2">text node</root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'))
+ );
+ xmlelement
+----------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar></ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'),
+ 'mixed content')
+ );
+ xmlelement
+-----------------------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar>mixed content</ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "foo",
+ xmlnamespaces(NO DEFAULT),'bar')
+ );
+ xmlelement
+----------------------------------------------------------------------
+ <root att="val" xmlns="http:/x.y/ns1"><foo xmlns="">bar</foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+ xmlelement(NAME "root",
+ xmlattributes(73 AS att),
+ xmlnamespaces('http:/x.y/ns1' AS ns),
+ xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+ xmlcomment(':)'),
+ xmlconcat('foo', 'bar')
+ );
+ xmlelement
+--------------------------------------------------------------------------------------------------------------------------
+ <root att="73" xmlns:ns="http:/x.y/ns1"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z><&></ns:z><!--:)-->foobar</root>
+(1 row)
+
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+ERROR: XML namespace "xmlns:ns1" appears more than once at character 70
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+ERROR: syntax error at or near ")" at character 55
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 71
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 58
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+ERROR: invalid XML namespace prefix "xmlns" at character 46
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+ERROR: invalid XML namespace prefix "xml" at character 46
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/"
+DETAIL: this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace"
+DETAIL: this URI is already bounded to standard a namespace prefix
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/"
+DETAIL: this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace"
+DETAIL: this URI is already bounded to standard a namespace prefix
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+ERROR: invalid XML namespace URI for "xmlns:ns"
+DETAIL: a regular XML namespace cannot be a zero-length string
SELECT xmlparse(content '');
xmlparse
----------
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index f752ecb142..14493a154f 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -66,6 +66,106 @@ SELECT xmlelement(name foo, xmlattributes('2009-04-09 00:24:37'::timestamp as ba
SELECT xmlelement(name foo, xmlattributes('infinity'::timestamp as bar));
SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as funnier));
+-- DEFAULT NULL xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL));
+-- DEFAULT xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1'));
+-- DEFAULT numeric xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73));
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT ''));
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'))
+ );
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'),
+ 'mixed content')
+ );
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "foo",
+ xmlnamespaces(NO DEFAULT),'bar')
+ );
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+ xmlelement(NAME "root",
+ xmlattributes(73 AS att),
+ xmlnamespaces('http:/x.y/ns1' AS ns),
+ xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+ xmlcomment(':)'),
+ xmlconcat('foo', 'bar')
+ );
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+
SELECT xmlparse(content '');
SELECT xmlparse(content ' ');
--
2.34.1
Hi
so 21. 12. 2024 v 0:51 odesílatel Jim Jones <jim.jones@uni-muenster.de>
napsal:
Hi,
I'd like to propose the implementation of the XMLNamespaces option for
XMLElement.XMLNAMESPACES(nsuri AS nsprefix)
XMLNAMESPACES(DEFAULT default-nsuri)
XMLNAMESPACES(NO DEFAULT)* nsprefix: Namespace's prefix.
* nsuri: Namespace's URI.
* DEFAULT default-nsuri: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT: Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.This basically works pretty much like XMLAttributes, but with a few more
restrictions (see SQL/XML:2023, 11.2 <XML lexically scoped options>):* XML namespace declaration shall contain at most one DEFAULT namespace
declaration item.
* No namespace prefix shall be equivalent to xml or xmlns.
* No namespace URI shall be identical to http://www.w3.org/2000/xmlns/
or to http://www.w3.org/XML/1998/namespace.
* The value of a namespace URI contained in an regular namespace
declaration item (no DEFAULT) shall not be a zero-length string.Examples:
SELECT xmlelement(NAME "foo", xmlnamespaces('http://x.y' AS bar));
xmlelement
-------------------------------
<foo xmlns:bar="http://x.y"/>SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http://x.y'));
xmlelement
---------------------------
<foo xmlns="http://x.y"/>SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
xmlelement
-----------------
<foo xmlns=""/>In transformXmlExpr() it seemed convenient to use the same parameters to
store the prefixes and URIs as in XMLAttributes (arg_names and
named_args), but I am still not so sure it is the right approach. Is
there perhaps a better way?Any thoughts? Feedback welcome!
+1
Pavel
Show quoted text
Best, Jim
Hi,
+1 for the enhancement.
I haven't compiled and reviewed the full patch yet, please see a few
comments from my side based on static analysis.
1. Though changes are targeted for XMLNAMESPACES for XMLElement but in
my opinion it will affect XMLTABLE as well because the
'xml_namespace_list' rule is shared now.
Adding 'NO DEFAULT' in xml_namespace_list will allow users to use it
with XMLTABLE XMLNAMESPACES as well.PostgreSQL grammar allow to
specify DEFAULT in NAMESPACE but resulting in following error:
"ERROR: DEFAULT namespace is not supported"
What would be behavior with this change for XMLTABLE, should this be
allowed and the error messages need to be updated (may be this will
not be an error at all) or we need to restrict users to not use 'NO
DEFAULT' with XMLTable.
2. Should we reuse the 'xml_namespaces' rule for XMLTable, as the
definition is the same.
3. In this patch 'NO DEFAULT' behavior is like DEFAULT '<blank>'
(empty uri) , should not it be more like 'DEFAULT NULL' to result in
the following ?
SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
xmlelement
------------------
<root/>
instead of
SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
xmlelement
------------------
<root xmlns=""/>
Regards
Umar Hayat
On Sat, 21 Dec 2024 at 14:57, Pavel Stehule <pavel.stehule@gmail.com> wrote:
Hi
so 21. 12. 2024 v 0:51 odesílatel Jim Jones <jim.jones@uni-muenster.de> napsal:
Hi,
I'd like to propose the implementation of the XMLNamespaces option for
XMLElement.XMLNAMESPACES(nsuri AS nsprefix)
XMLNAMESPACES(DEFAULT default-nsuri)
XMLNAMESPACES(NO DEFAULT)* nsprefix: Namespace's prefix.
* nsuri: Namespace's URI.
* DEFAULT default-nsuri: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT: Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.This basically works pretty much like XMLAttributes, but with a few more
restrictions (see SQL/XML:2023, 11.2 <XML lexically scoped options>):* XML namespace declaration shall contain at most one DEFAULT namespace
declaration item.
* No namespace prefix shall be equivalent to xml or xmlns.
* No namespace URI shall be identical to http://www.w3.org/2000/xmlns/
or to http://www.w3.org/XML/1998/namespace.
* The value of a namespace URI contained in an regular namespace
declaration item (no DEFAULT) shall not be a zero-length string.Examples:
SELECT xmlelement(NAME "foo", xmlnamespaces('http://x.y' AS bar));
xmlelement
-------------------------------
<foo xmlns:bar="http://x.y"/>SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http://x.y'));
xmlelement
---------------------------
<foo xmlns="http://x.y"/>SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
xmlelement
-----------------
<foo xmlns=""/>In transformXmlExpr() it seemed convenient to use the same parameters to
store the prefixes and URIs as in XMLAttributes (arg_names and
named_args), but I am still not so sure it is the right approach. Is
there perhaps a better way?Any thoughts? Feedback welcome!
+1
Pavel
Best, Jim
--
Umar Hayat
Bitnine (https://bitnine.net/)
Hi Umar, hi Pavel,
Thanks for taking a look at this patch!
On 26.12.24 05:15, Umar Hayat wrote:
Hi,
+1 for the enhancement.I haven't compiled and reviewed the full patch yet, please see a few
comments from my side based on static analysis.1. Though changes are targeted for XMLNAMESPACES for XMLElement but in
my opinion it will affect XMLTABLE as well because the
'xml_namespace_list' rule is shared now.
Adding 'NO DEFAULT' in xml_namespace_list will allow users to use it
with XMLTABLE XMLNAMESPACES as well.PostgreSQL grammar allow to
specify DEFAULT in NAMESPACE but resulting in following error:
"ERROR: DEFAULT namespace is not supported"
I also considered creating a new rule to avoid any conflict with
XMLTable, but as it didn't break any regression test and the result
would be pretty much the same as with "DEFAULT 'str'", I thought that
extending the existing rule would be the way to go.
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: DEFAULT namespace is not supported
What do you think?
What would be behavior with this change for XMLTABLE, should this be
allowed and the error messages need to be updated (may be this will
not be an error at all) or we need to restrict users to not use 'NO
DEFAULT' with XMLTable.
Perhaps updating the error message would suffice?
2. Should we reuse the 'xml_namespaces' rule for XMLTable, as the
definition is the same.
That would be good. I'm just afraid it would deviate a bit from the
scope of this patch - here I mean touching other function. Would you
suggest to add it to a patch series?
3. In this patch 'NO DEFAULT' behavior is like DEFAULT '<blank>'
(empty uri) , should not it be more like 'DEFAULT NULL' to result in
the following ?
SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
xmlelement
------------------
<root/>instead of
SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
xmlelement
------------------
<root xmlns=""/>
The idea of NO DEFAULT is pretty much to free an element (and its
children) from a previous DEFAULT in the same scope.
SELECT
xmlserialize(DOCUMENT
xmlelement(NAME "root",
xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
xmlelement(NAME "foo",
xmlnamespaces(NO DEFAULT))
) AS text INDENT);
xmlserialize
------------------------------
<root xmlns="http:/x.y/ns1">+
<foo xmlns=""/> +
</root>
(1 row)
I believe this behaviour might be confusing if NO DEFAULT is used in the
root element, as it there is no previously declared namespace. Perhaps
making NO DEFAULT behave like DEFAULT NULL only in the root element
would make things clearer? The SQL/XML spec doesn't say anything
specific about it, but DB2 had the same thought[1]. For reference, here
are the regress tests[2] of this patch tested with the DB2 implementation.
On Sat, 21 Dec 2024 at 14:57, Pavel Stehule <pavel.stehule@gmail.com> wrote:
+1
Pavel
rebase in v2 attached - due to changes in gram.y
Thanks a lot
Best, Jim
1 - https://dbfiddle.uk/0QsWlfZR
2 - https://dbfiddle.uk/SyiDfXod
Attachments:
v2-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v2-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From 93422b076b2d270ca9293cf4e0be3da62bb6af1c Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Thu, 26 Dec 2024 14:40:26 +0100
Subject: [PATCH v2] Add XMLNamespaces option to XMLElement
This patch adds the scoped option XMLNamespaces to XMLElement,
as described in ISO/IEC 9075-14:2023, 11.2 XML lexically scoped
options:
xmlnamespaces(uri AS prefix, ...)
xmlnamespaces(DEFAULT uri, ...)
xmlnamespaces(NO DEFAULT, ...)
* prefix: Namespace's prefix.
* uri: Namespace's URI.
* DEFAULT prefix: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT: Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.
== Examples ==
SELECT xmlelement(NAME "foo", xmlnamespaces('http:/x.y' AS bar));
xmlelement
------------------------------
<foo xmlns:bar="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y'));
xmlelement
--------------------------
<foo xmlns="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
xmlelement
-----------------
<foo xmlns=""/>
Tests and documentation were updated accordingly.
---
doc/src/sgml/func.sgml | 57 +++++++-
src/backend/parser/gram.y | 73 +++++++---
src/backend/parser/parse_expr.c | 73 ++++++++++
src/backend/utils/adt/xml.c | 32 ++++-
src/include/nodes/primnodes.h | 4 +-
src/include/utils/xml.h | 6 +
src/test/regress/expected/xml.out | 205 ++++++++++++++++++++++++++++
src/test/regress/expected/xml_1.out | 151 ++++++++++++++++++++
src/test/regress/expected/xml_2.out | 205 ++++++++++++++++++++++++++++
src/test/regress/sql/xml.sql | 100 ++++++++++++++
10 files changed, 884 insertions(+), 22 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 47370e581a..b33663bc09 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14482,7 +14482,10 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
</indexterm>
<synopsis>
-<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> <optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
+<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable>
+ <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional>
+ <optional>, <literal>XMLNAMESPACES</literal> ( {<replaceable>regular-nsuri</replaceable> <literal>AS</literal> <replaceable>nsprefix</replaceable> | DEFAULT <replaceable>default-nsuri</replaceable> | NO DEFAULT} <optional>, ...</optional> ) </optional>
+<optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
</synopsis>
<para>
@@ -14495,7 +14498,39 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
yield any <productname>PostgreSQL</productname> data type. The
argument(s) within <literal>XMLATTRIBUTES</literal> generate attributes
of the XML element; the <replaceable>content</replaceable> value(s) are
- concatenated to form its content.
+ concatenated to form its content. The arguments within <literal>XMLNAMESPACES</literal>
+ constuct namespace declarations from values provided in <replaceable>nsuri</replaceable>
+ and <replaceable>nsprefix</replaceable>, which correspond to the URI of a namespace and
+ its prefix, respectively. The option <literal>DEFAULT</literal> can be used to set the
+ default namespace declaration (without a prefix) to the URI provided in <replaceable>default-nsuri</replaceable>.
+ The option <literal>NO DEFAULT</literal> states that a namespace scope has no default namespace. A valid
+ <literal>XMLNAMESPACES</literal> item must fulfill the following conditions:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Only a single <literal>DEFAULT</literal> declaration item within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No two <replaceable>nsuri</replaceable> can be equal within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No <replaceable>nsprefix</replaceable> can be equal to <literal>xml</literal> or <literal>xmlns</literal>,
+ and no <replaceable>nsuri</replaceable> can be equal to <literal>http://www.w3.org/2000/xmlns/</literal>
+ or to <literal>http://www.w3.org/XML/1998/namespace</literal>, as they are already bouned to standard XML declarations.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The value of a <replaceable>regular-nsuri</replaceable> cannot be a zero-length string.
+ </para>
+ </listitem>
+ </itemizedlist>
+
</para>
<para>
@@ -14518,6 +14553,24 @@ SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');
xmlelement
-------------------------------------
<foo bar="2007-01-26">content</foo>
+
+SELECT xmlelement(NAME "foo:root", xmlnamespaces('http:/foo.bar/' AS foo), 'content');
+
+ xmlelement
+---------------------------------------------------------
+ <foo:root xmlns:foo="http:/foo.bar/">content</foo:root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/foo.bar/'), 'content');
+
+ xmlelement
+---------------------------------------------
+ <root xmlns="http:/foo.bar/">content</root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT), 'content');
+
+ xmlelement
+-------------------------------
+ <root xmlns="">content</root>
]]></screen>
</para>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index bd5ebb35c4..d991796a66 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -185,7 +185,7 @@ static Node *makeNotExpr(Node *expr, int location);
static Node *makeAArrayExpr(List *elements, int location);
static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod,
int location);
-static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
+static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *xmlnamespaces,
List *args, int location);
static List *mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner);
static TypeName *TableFuncTypeName(List *columns);
@@ -610,7 +610,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> xmltable_column_list xmltable_column_option_list
%type <node> xmltable_column_el
%type <defelt> xmltable_column_option_el
-%type <list> xml_namespace_list
+%type <list> xml_namespace_list xml_namespaces
%type <target> xml_namespace_el
%type <node> func_application func_expr_common_subexpr
@@ -14210,6 +14210,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = makeStringConst("", @1);;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15263,12 +15272,12 @@ a_expr: c_expr { $$ = $1; }
}
| a_expr IS DOCUMENT_P %prec IS
{
- $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+ $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
list_make1($1), @2);
}
| a_expr IS NOT DOCUMENT_P %prec IS
{
- $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+ $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
list_make1($1), @2),
@2);
}
@@ -15410,12 +15419,12 @@ b_expr: c_expr
}
| b_expr IS DOCUMENT_P %prec IS
{
- $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+ $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
list_make1($1), @2);
}
| b_expr IS NOT DOCUMENT_P %prec IS
{
- $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
+ $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL,
list_make1($1), @2),
@2);
}
@@ -15950,23 +15959,47 @@ func_expr_common_subexpr:
}
| XMLCONCAT '(' expr_list ')'
{
- $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1);
+ $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, NIL, $3, @1);
}
| XMLELEMENT '(' NAME_P ColLabel ')'
{
- $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1);
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, NIL, @1);
}
| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ')'
{
- $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, @1);
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, NIL, @1);
}
| XMLELEMENT '(' NAME_P ColLabel ',' expr_list ')'
{
- $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, @1);
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, $6, @1);
}
| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' expr_list ')'
{
- $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1);
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, $8, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' expr_list ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, $8, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, NIL, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' xml_namespaces ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, NIL, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' xml_attributes ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $8, $6, NIL, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' xml_namespaces ',' expr_list ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, $10, @1);
+ }
+ | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' xml_attributes ',' expr_list ')'
+ {
+ $$ = makeXmlExpr(IS_XMLELEMENT, $4, $8, $6, $10, @1);
}
| XMLEXISTS '(' c_expr xmlexists_argument ')'
{
@@ -15979,12 +16012,12 @@ func_expr_common_subexpr:
}
| XMLFOREST '(' xml_attribute_list ')'
{
- $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1);
+ $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, NIL, @1);
}
| XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')'
{
XmlExpr *x = (XmlExpr *)
- makeXmlExpr(IS_XMLPARSE, NULL, NIL,
+ makeXmlExpr(IS_XMLPARSE, NULL, NIL, NIL,
list_make2($4, makeBoolAConst($5, -1)),
@1);
@@ -15993,15 +16026,15 @@ func_expr_common_subexpr:
}
| XMLPI '(' NAME_P ColLabel ')'
{
- $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, @1);
+ $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, NIL, @1);
}
| XMLPI '(' NAME_P ColLabel ',' a_expr ')'
{
- $$ = makeXmlExpr(IS_XMLPI, $4, NULL, list_make1($6), @1);
+ $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, list_make1($6), @1);
}
| XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')'
{
- $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL,
+ $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, NIL,
list_make3($3, $5, $6), @1);
}
| XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename xml_indent_option ')'
@@ -16205,6 +16238,9 @@ opt_xml_root_standalone: ',' STANDALONE_P YES_P
xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; }
;
+xml_namespaces: XMLNAMESPACES '(' xml_namespace_list ')' { $$ = $3; }
+ ;
+
xml_attribute_list: xml_attribute_el { $$ = list_make1($1); }
| xml_attribute_list ',' xml_attribute_el { $$ = lappend($1, $3); }
;
@@ -19211,8 +19247,8 @@ makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location)
}
static Node *
-makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
- int location)
+makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *xmlnamespaces,
+ List *args, int location)
{
XmlExpr *x = makeNode(XmlExpr);
@@ -19224,6 +19260,7 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
* expression and name lists in transformXmlExpr().
*/
x->named_args = named_args;
+ x->xmlnamespaces = xmlnamespaces;
x->arg_names = NIL;
x->args = args;
/* xmloption, if relevant, must be filled in by caller */
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index c2806297aa..783184d782 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2347,6 +2347,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
XmlExpr *newx;
ListCell *lc;
int i;
+ bool has_default_ns = false;
newx = makeNode(XmlExpr);
newx->op = x->op;
@@ -2467,6 +2468,78 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
i++;
}
+ /*
+ * this adds the xmlnamespaces into arg_names and named_args
+ */
+ foreach (lc, x->xmlnamespaces)
+ {
+ ResTarget *r = lfirst_node(ResTarget, lc);
+ Node *expr;
+ StringInfoData nsbuf;
+
+ expr = transformExprRecurse(pstate, r->val);
+ initStringInfo(&nsbuf);
+
+ if (!r->name)
+ {
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 2) <XML namespace declaration> shall contain at most one
+ * <XML default namespace declaration item>.
+ */
+ if (has_default_ns)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("XML elements can have only a single [NO] DEFAULT namespace"),
+ parser_errposition(pstate, r->location)));
+ else
+ appendStringInfo(&nsbuf, NAMESPACE_XMLNS_DEFAULT_PREFIX);
+
+ has_default_ns = true;
+ }
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 5) No <XML namespace prefix> shall be equivalent to
+ * "xml" or "xmlns".
+ */
+ else if (strcmp(r->name, NAMESPACE_XMLNS_DEFAULT_PREFIX) == 0 ||
+ strcmp(r->name, NAMESPACE_XML_DEFAULT_PREFIX) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid XML namespace prefix \"%s\"", r->name),
+ errdetail("this prefix is already bounded to a standard namespace URI"),
+ parser_errposition(pstate, r->location)));
+ else if (r->name)
+ appendStringInfo(&nsbuf, "%s:%s", NAMESPACE_XMLNS_DEFAULT_PREFIX,
+ map_sql_identifier_to_xml_name(r->name, false, false));
+ else if (IsA(r->val, ColumnRef))
+ appendStringInfo(&nsbuf, "%s:%s", NAMESPACE_XMLNS_DEFAULT_PREFIX,
+ map_sql_identifier_to_xml_name(FigureColname(r->val), true, false));
+
+ /* reject duplicate argnames in XMLELEMENT only */
+ if (x->op == IS_XMLELEMENT)
+ {
+ ListCell *lc2;
+
+ foreach (lc2, newx->arg_names)
+ {
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 4) No two <XML namespace prefix>es shall be equivalent.
+ */
+ if (strcmp(nsbuf.data, strVal(lfirst(lc2))) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("XML namespace \"%s\" appears more than once",
+ nsbuf.data),
+ parser_errposition(pstate, r->location)));
+ }
+ }
+
+ newx->named_args = lappend(newx->named_args, expr);
+ newx->arg_names = lappend(newx->arg_names, makeString(nsbuf.data));
+ }
+
return (Node *) newx;
}
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 0898cb1be4..70fec22e21 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -244,7 +244,6 @@ const TableFuncRoutine XmlTableRoutine =
#define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance"
#define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml"
-
#ifdef USE_LIBXML
static int
@@ -931,6 +930,37 @@ xmlelement(XmlExpr *xexpr,
char *str = (char *) lfirst(arg);
char *argname = strVal(lfirst(narg));
+ StringInfoData xmlns_prefix;
+ initStringInfo(&xmlns_prefix);
+ appendStringInfo(&xmlns_prefix, "%s:", NAMESPACE_XMLNS_DEFAULT_PREFIX);
+
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 6) No <XML namespace URI> shall be identical, as defined
+ * in XML Namespaces, to http://www.w3.org/2000/xmlns/ or to
+ * http://www.w3.org/XML/1998/namespace
+ */
+ if (str && ((strncmp(argname, xmlns_prefix.data, xmlns_prefix.len) == 0 ||
+ strcmp(argname, NAMESPACE_XMLNS_DEFAULT_PREFIX) == 0) &&
+ (strcmp(str, NAMESPACE_XMLNS) == 0 || strcmp(str, NAMESPACE_XML) == 0)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid XML namespace URI \"%s\"", str),
+ errdetail("this URI is already bounded to standard a namespace prefix")));
+
+ /*
+ * SQL/XML:2023 - 11.2 <XML lexically scoped options>
+ * Syntax Rule 7) The value of an <XML namespace URI> contained in an
+ * <XML regular namespace declaration item> shall not be a zero-length string.
+ */
+ if (strncmp(argname, xmlns_prefix.data, 6) == 0 && (str && strlen(str) == 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING),
+ errmsg("invalid XML namespace URI for \"%s\"", argname),
+ errdetail("a regular XML namespace cannot be a zero-length string")));
+
+ pfree(xmlns_prefix.data);
+
if (str)
xmlTextWriterWriteAttribute(writer,
(xmlChar *) argname,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index b0ef1952e8..2e5ad905a0 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1578,7 +1578,7 @@ typedef struct SQLValueFunction
typedef enum XmlExprOp
{
IS_XMLCONCAT, /* XMLCONCAT(args) */
- IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, args) */
+ IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, xml_namespaces, args) */
IS_XMLFOREST, /* XMLFOREST(xml_attributes) */
IS_XMLPARSE, /* XMLPARSE(text, is_doc, preserve_ws) */
IS_XMLPI, /* XMLPI(name [, args]) */
@@ -1602,6 +1602,8 @@ typedef struct XmlExpr
char *name pg_node_attr(query_jumble_ignore);
/* non-XML expressions for xml_attributes */
List *named_args;
+ /* non-XML expressions for XMLNAMESPACES */
+ List *xmlnamespaces;
/* parallel list of String values */
List *arg_names pg_node_attr(query_jumble_ignore);
/* list of expressions */
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index ed20e21375..4cd365d414 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -59,6 +59,12 @@ XmlPGetDatum(const xmltype *X)
return PointerGetDatum(X);
}
+/* reserved prefixes and URIs for XMLNamespace() from SQL/XML:2023, 11.2 */
+#define NAMESPACE_XMLNS "http://www.w3.org/2000/xmlns/"
+#define NAMESPACE_XML "http://www.w3.org/XML/1998/namespace"
+#define NAMESPACE_XMLNS_DEFAULT_PREFIX "xmlns"
+#define NAMESPACE_XML_DEFAULT_PREFIX "xml"
+
#define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n))
#define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x)
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index fb5f345855..c9be4a3d72 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -225,6 +225,211 @@ SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as fun
<foo funny="<>&"'" 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)
+
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+ xmlelement
+------------
+ <root/>
+(1 row)
+
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+ xmlelement
+-----------------------------------
+ <root xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+ xmlelement
+---------------------------------------------------------
+ <root xmlns="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+ xmlelement
+--------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1">text node</root>
+(1 row)
+
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+ xmlelement
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+ xmlelement
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+ xmlelement
+-------------------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+ xmlelement
+-----------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+ xmlelement
+--------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2">text node</root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'))
+ );
+ xmlelement
+----------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar></ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'),
+ 'mixed content')
+ );
+ xmlelement
+-----------------------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar>mixed content</ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "foo",
+ xmlnamespaces(NO DEFAULT),'bar')
+ );
+ xmlelement
+----------------------------------------------------------------------
+ <root att="val" xmlns="http:/x.y/ns1"><foo xmlns="">bar</foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+ xmlelement(NAME "root",
+ xmlattributes(73 AS att),
+ xmlnamespaces('http:/x.y/ns1' AS ns),
+ xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+ xmlcomment(':)'),
+ xmlconcat('foo', 'bar')
+ );
+ xmlelement
+--------------------------------------------------------------------------------------------------------------------------
+ <root att="73" xmlns:ns="http:/x.y/ns1"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z><&></ns:z><!--:)-->foobar</root>
+(1 row)
+
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+ERROR: XML namespace "xmlns:ns1" appears more than once at character 70
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+ERROR: syntax error at or near ")" at character 55
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 71
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 58
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+ERROR: invalid XML namespace prefix "xmlns" at character 46
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+ERROR: invalid XML namespace prefix "xml" at character 46
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/"
+DETAIL: this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace"
+DETAIL: this URI is already bounded to standard a namespace prefix
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/"
+DETAIL: this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace"
+DETAIL: this URI is already bounded to standard a namespace prefix
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+ERROR: invalid XML namespace URI for "xmlns:ns"
+DETAIL: a regular XML namespace cannot be a zero-length string
SELECT xmlparse(content '');
xmlparse
----------
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index ef7dc03c69..500e2b4ca3 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -150,6 +150,157 @@ DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as funnier));
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT NULL xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1'));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT numeric xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT ''));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'))
+ );
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'),
+ 'mixed content')
+ );
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "foo",
+ xmlnamespaces(NO DEFAULT),'bar')
+ );
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+ xmlelement(NAME "root",
+ xmlattributes(73 AS att),
+ xmlnamespaces('http:/x.y/ns1' AS ns),
+ xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+ xmlcomment(':)'),
+ xmlconcat('foo', 'bar')
+ );
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+ERROR: unsupported XML feature
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+ERROR: syntax error at or near ")" at character 55
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+ERROR: unsupported XML feature
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+ERROR: unsupported XML feature
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+ERROR: unsupported XML feature
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+ERROR: unsupported XML feature
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
SELECT xmlparse(content '');
ERROR: unsupported XML feature
DETAIL: This functionality requires the server to be built with libxml support.
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index 4a9cdd2afe..a47cf27b6e 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -221,6 +221,211 @@ SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as fun
<foo funny="<>&"'" 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)
+
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+ xmlelement
+------------
+ <root/>
+(1 row)
+
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+ xmlelement
+-----------------------------------
+ <root xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+ xmlelement
+---------------------------------------------------------
+ <root xmlns="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+ xmlelement
+--------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1">text node</root>
+(1 row)
+
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+ xmlelement
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+ xmlelement
+---------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+ xmlelement
+-------------------------------------------------------------
+ <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+ xmlelement
+-----------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/>
+(1 row)
+
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+ xmlelement
+--------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2">text node</root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'))
+ );
+ xmlelement
+----------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar></ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'),
+ 'mixed content')
+ );
+ xmlelement
+-----------------------------------------------------------------------------------------------------------------------------------------
+ <root att="val" xmlns:ns1="http:/x.y/ns1"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar>mixed content</ns1:foo></root>
+(1 row)
+
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "foo",
+ xmlnamespaces(NO DEFAULT),'bar')
+ );
+ xmlelement
+----------------------------------------------------------------------
+ <root att="val" xmlns="http:/x.y/ns1"><foo xmlns="">bar</foo></root>
+(1 row)
+
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+ xmlelement(NAME "root",
+ xmlattributes(73 AS att),
+ xmlnamespaces('http:/x.y/ns1' AS ns),
+ xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+ xmlcomment(':)'),
+ xmlconcat('foo', 'bar')
+ );
+ xmlelement
+--------------------------------------------------------------------------------------------------------------------------
+ <root att="73" xmlns:ns="http:/x.y/ns1"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z><&></ns:z><!--:)-->foobar</root>
+(1 row)
+
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+ERROR: XML namespace "xmlns:ns1" appears more than once at character 70
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+ERROR: syntax error at or near ")" at character 55
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 71
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 58
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+ERROR: invalid XML namespace prefix "xmlns" at character 46
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+ERROR: invalid XML namespace prefix "xml" at character 46
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/"
+DETAIL: this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace"
+DETAIL: this URI is already bounded to standard a namespace prefix
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/"
+DETAIL: this URI is already bounded to standard a namespace prefix
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace"
+DETAIL: this URI is already bounded to standard a namespace prefix
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+ERROR: invalid XML namespace URI for "xmlns:ns"
+DETAIL: a regular XML namespace cannot be a zero-length string
SELECT xmlparse(content '');
xmlparse
----------
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index f752ecb142..14493a154f 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -66,6 +66,106 @@ SELECT xmlelement(name foo, xmlattributes('2009-04-09 00:24:37'::timestamp as ba
SELECT xmlelement(name foo, xmlattributes('infinity'::timestamp as bar));
SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as funnier));
+-- DEFAULT NULL xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL));
+-- DEFAULT xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1'));
+-- DEFAULT numeric xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73));
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT ''));
+-- DEFAULT empty xmlnamespace
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
+-- NULL xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns));
+-- empty xmlelement containing one namespace
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1));
+-- empty xmlelement with DEFAULT and regular xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2));
+-- xmlelement containing a namespace and a text node
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node');
+-- empty xmlelement containing a) xmlnamespace and b) xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att));
+-- empty xmlelement containing a) xmlattribute and b) xmlnamespace
+SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1));
+-- empty xmlelement containing two xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2));
+-- empty xmlelement containing two xmlnamespaces and one xmlattribute
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att));
+-- xmlelement containing two xmlnamespaces, one xmlattribute,
+-- and a text node.
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node');
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one
+-- xmlelement child (ns2:bar).
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'))
+ );
+-- empty root xmlelement containing one xmlnamespace, one xmlattribute,
+-- and one xmlelement child (ns1:foo).
+-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement
+-- child (ns2:bar), and a text node.
+-- xmlelement child "ns2:bar" containing a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces('http:/x.y/ns1' AS ns1),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "ns1:foo",
+ xmlnamespaces('http:/x.y/ns2' AS ns2),
+ xmlelement(NAME "ns2:bar", 'text node'),
+ 'mixed content')
+ );
+-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute,
+-- and one xmlelement child (foo).
+-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node.
+SELECT
+ xmlelement(NAME "root",
+ xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
+ xmlattributes('val' AS att),
+ xmlelement(NAME "foo",
+ xmlnamespaces(NO DEFAULT),'bar')
+ );
+-- empty root xmlelement containing one xmlattribute, one xmlnamespace,
+-- three child nodes generated by xmlforest and xmltext, one xmlcomment,
+-- and a text node generated by xmlconcat.
+SELECT
+ xmlelement(NAME "root",
+ xmlattributes(73 AS att),
+ xmlnamespaces('http:/x.y/ns1' AS ns),
+ xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"),
+ xmlcomment(':)'),
+ xmlconcat('foo', 'bar')
+ );
+\set VERBOSITY terse
+-- duplicate xmlnamespace entry (ns1)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1));
+-- invalid xmlnamespace syntax
+SELECT xmlelement(NAME "root", xmlnamespaces('invalid'));
+-- multiple DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2'));
+-- multiple NO DEFAULT xmlnamespaces
+SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT));
+-- invalid xmlnamespace prefix (xmlns)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns));
+-- invalid xmlnamespace prefix (xml)
+SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml));
+\set VERBOSITY default
+-- invalid xmlnamespaces URIs
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar));
+SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar));
+-- invalid DEFAULT xmlnamespace URIs
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/'));
+SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace'));
+-- empty xmlnamespace uri
+SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns));
+
SELECT xmlparse(content '');
SELECT xmlparse(content ' ');
--
2.34.1
čt 26. 12. 2024 v 14:46 odesílatel Jim Jones <jim.jones@uni-muenster.de>
napsal:
Hi Umar, hi Pavel,
Thanks for taking a look at this patch!
On 26.12.24 05:15, Umar Hayat wrote:
Hi,
+1 for the enhancement.I haven't compiled and reviewed the full patch yet, please see a few
comments from my side based on static analysis.1. Though changes are targeted for XMLNAMESPACES for XMLElement but in
my opinion it will affect XMLTABLE as well because the
'xml_namespace_list' rule is shared now.
Adding 'NO DEFAULT' in xml_namespace_list will allow users to use it
with XMLTABLE XMLNAMESPACES as well.PostgreSQL grammar allow to
specify DEFAULT in NAMESPACE but resulting in following error:
"ERROR: DEFAULT namespace is not supported"I also considered creating a new rule to avoid any conflict with
XMLTable, but as it didn't break any regression test and the result
would be pretty much the same as with "DEFAULT 'str'", I thought that
extending the existing rule would be the way to go.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: DEFAULT namespace is not supportedWhat do you think?
What would be behavior with this change for XMLTABLE, should this be
allowed and the error messages need to be updated (may be this will
not be an error at all) or we need to restrict users to not use 'NO
DEFAULT' with XMLTable.Perhaps updating the error message would suffice?
2. Should we reuse the 'xml_namespaces' rule for XMLTable, as the
definition is the same.That would be good. I'm just afraid it would deviate a bit from the
scope of this patch - here I mean touching other function. Would you
suggest to add it to a patch series?3. In this patch 'NO DEFAULT' behavior is like DEFAULT '<blank>'
(empty uri) , should not it be more like 'DEFAULT NULL' to result in
the following ?
SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
xmlelement
------------------
<root/>instead of
SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT));
xmlelement
------------------
<root xmlns=""/>The idea of NO DEFAULT is pretty much to free an element (and its
children) from a previous DEFAULT in the same scope.SELECT
xmlserialize(DOCUMENT
xmlelement(NAME "root",
xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
xmlelement(NAME "foo",
xmlnamespaces(NO DEFAULT))
) AS text INDENT);xmlserialize
------------------------------
<root xmlns="http:/x.y/ns1">+
<foo xmlns=""/> +
</root>
(1 row)I believe this behaviour might be confusing if NO DEFAULT is used in the
root element, as it there is no previously declared namespace. Perhaps
making NO DEFAULT behave like DEFAULT NULL only in the root element
would make things clearer? The SQL/XML spec doesn't say anything
specific about it, but DB2 had the same thought[1]. For reference, here
are the regress tests[2] of this patch tested with the DB2 implementation.
You can check Oracle too.
On Sat, 21 Dec 2024 at 14:57, Pavel Stehule <pavel.stehule@gmail.com>
wrote:
+1
Pavel
rebase in v2 attached - due to changes in gram.y
I checked this patch
The parser part looks a little bit dirty - it multiplies numbers of
XMLELEMENT rules. Maybe xmlattributes and xml_namespaces can be processed
elsewhere like list of xml_element_options?
Regards
Pavel
Show quoted text
Thanks a lot
Best, Jim
1 - https://dbfiddle.uk/0QsWlfZR
2 - https://dbfiddle.uk/SyiDfXod
Hi Umar, Hi Pavel,
On 26.12.24 14:46, Jim Jones wrote:
The idea of NO DEFAULT is pretty much to free an element (and its
children) from a previous DEFAULT in the same scope.SELECT
xmlserialize(DOCUMENT
xmlelement(NAME "root",
xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
xmlelement(NAME "foo",
xmlnamespaces(NO DEFAULT))
) AS text INDENT);xmlserialize
------------------------------
<root xmlns="http:/x.y/ns1">+
<foo xmlns=""/> +
</root>
(1 row)
v3 is attached, now using xmlTextWriterWriteAttributeNS from libxml2 for
managing XML namespaces, instead of using xmlTextWriterWriteAttribute.
Libxml2 is quite lenient, allowing the duplication of default namespaces
within the same scope and even permitting NO DEFAULT namespaces when no
previous DEFAULT declaration has been made - both are semantically valid.
The crux now is finding the appropriate balance between accuracy and
user intent. In the context of PostgreSQL's xmlelement and
xmlnamespaces, I would argue that explicitly declared namespaces,
redundant or not, ought to be preserved. A user who intentionally
repeats a namespace declaration might have sound reasons for doing so,
like ensuring clarity, preserving compatibility with external XML
processors, or sticking to a specific schema. Silently omitting these
declarations could lead to confusion.
Pavel has tidied up the parser modifications - it's looking much neater
now. Many thanks for that!
Best, Jim
Attachments:
v3-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v3-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From be391fc95c16e2b9197257553310f68415a9f3d3 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Wed, 15 Jan 2025 20:21:27 +0100
Subject: [PATCH v3] Add XMLNamespaces option to XMLElement
This patch adds the scoped option XMLNamespaces to XMLElement,
as described in ISO/IEC 9075-14:2023, 11.2 XML lexically scoped
options:
xmlnamespaces(uri AS prefix, ...)
xmlnamespaces(DEFAULT uri, ...)
xmlnamespaces(NO DEFAULT, ...)
* prefix: Namespace's prefix.
* uri: Namespace's URI.
* DEFAULT prefix: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT: Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.
== Examples ==
SELECT xmlelement(NAME "foo", xmlnamespaces('http:/x.y' AS bar));
xmlelement
------------------------------
<foo xmlns:bar="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y'));
xmlelement
--------------------------
<foo xmlns="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
xmlelement
-----------------
<foo xmlns=""/>
Tests and documentation were updated accordingly.
---
doc/src/sgml/func.sgml | 57 ++++++-
src/backend/parser/gram.y | 100 ++++++++++--
src/backend/parser/parse_expr.c | 80 ++++++++++
src/backend/utils/adt/xml.c | 47 +++++-
src/include/nodes/primnodes.h | 4 +-
src/include/utils/xml.h | 6 +
src/test/regress/expected/xml.out | 236 ++++++++++++++++++++++++++++
src/test/regress/expected/xml_1.out | 172 ++++++++++++++++++++
src/test/regress/expected/xml_2.out | 236 ++++++++++++++++++++++++++++
src/test/regress/sql/xml.sql | 116 ++++++++++++++
10 files changed, 1032 insertions(+), 22 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 47370e581a..b33663bc09 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14482,7 +14482,10 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
</indexterm>
<synopsis>
-<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> <optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
+<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable>
+ <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional>
+ <optional>, <literal>XMLNAMESPACES</literal> ( {<replaceable>regular-nsuri</replaceable> <literal>AS</literal> <replaceable>nsprefix</replaceable> | DEFAULT <replaceable>default-nsuri</replaceable> | NO DEFAULT} <optional>, ...</optional> ) </optional>
+<optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
</synopsis>
<para>
@@ -14495,7 +14498,39 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
yield any <productname>PostgreSQL</productname> data type. The
argument(s) within <literal>XMLATTRIBUTES</literal> generate attributes
of the XML element; the <replaceable>content</replaceable> value(s) are
- concatenated to form its content.
+ concatenated to form its content. The arguments within <literal>XMLNAMESPACES</literal>
+ constuct namespace declarations from values provided in <replaceable>nsuri</replaceable>
+ and <replaceable>nsprefix</replaceable>, which correspond to the URI of a namespace and
+ its prefix, respectively. The option <literal>DEFAULT</literal> can be used to set the
+ default namespace declaration (without a prefix) to the URI provided in <replaceable>default-nsuri</replaceable>.
+ The option <literal>NO DEFAULT</literal> states that a namespace scope has no default namespace. A valid
+ <literal>XMLNAMESPACES</literal> item must fulfill the following conditions:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Only a single <literal>DEFAULT</literal> declaration item within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No two <replaceable>nsuri</replaceable> can be equal within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No <replaceable>nsprefix</replaceable> can be equal to <literal>xml</literal> or <literal>xmlns</literal>,
+ and no <replaceable>nsuri</replaceable> can be equal to <literal>http://www.w3.org/2000/xmlns/</literal>
+ or to <literal>http://www.w3.org/XML/1998/namespace</literal>, as they are already bouned to standard XML declarations.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The value of a <replaceable>regular-nsuri</replaceable> cannot be a zero-length string.
+ </para>
+ </listitem>
+ </itemizedlist>
+
</para>
<para>
@@ -14518,6 +14553,24 @@ SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');
xmlelement
-------------------------------------
<foo bar="2007-01-26">content</foo>
+
+SELECT xmlelement(NAME "foo:root", xmlnamespaces('http:/foo.bar/' AS foo), 'content');
+
+ xmlelement
+---------------------------------------------------------
+ <foo:root xmlns:foo="http:/foo.bar/">content</foo:root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/foo.bar/'), 'content');
+
+ xmlelement
+---------------------------------------------
+ <root xmlns="http:/foo.bar/">content</root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT), 'content');
+
+ xmlelement
+-------------------------------
+ <root xmlns="">content</root>
]]></screen>
</para>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6079de70e0..9bf3af484c 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;
}
%type <node> stmt toplevel_stmt schema_stmt routine_body_stmt
@@ -612,8 +619,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
@@ -14235,6 +14243,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = NULL;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15288,12 +15305,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);
}
@@ -15435,12 +15452,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);
}
@@ -15975,21 +15992,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);
}
@@ -16004,12 +16021,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);
@@ -16026,7 +16048,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 ')'
@@ -16230,6 +16252,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); }
;
@@ -16266,6 +16291,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
@@ -19238,7 +19301,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);
@@ -19250,7 +19313,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_expr.c b/src/backend/parser/parse_expr.c
index d1f64f8f0a..322e07e22c 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));
+ 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/xml.c b/src/backend/utils/adt/xml.c
index db8d0d6a7e..ce79d97a5b 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 (argname && 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,
+ !argname ? NULL : (const xmlChar *) prefix,
+ argname ? (const xmlChar *) argname : (const xmlChar *) prefix,
+ NULL,
+ (const xmlChar *) str);
+ }
+ else if (str)
xmlTextWriterWriteAttribute(writer,
(xmlChar *) argname,
(xmlChar *) str);
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 9c2957eb54..95d3af61db 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1578,7 +1578,7 @@ typedef struct SQLValueFunction
typedef enum XmlExprOp
{
IS_XMLCONCAT, /* XMLCONCAT(args) */
- IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, args) */
+ IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, xml_namespaces, args) */
IS_XMLFOREST, /* XMLFOREST(xml_attributes) */
IS_XMLPARSE, /* XMLPARSE(text, is_doc, preserve_ws) */
IS_XMLPI, /* XMLPI(name [, args]) */
@@ -1602,6 +1602,8 @@ typedef struct XmlExpr
char *name pg_node_attr(query_jumble_ignore);
/* non-XML expressions for xml_attributes */
List *named_args;
+ /* non-XML expressions for XMLNAMESPACES */
+ List *xmlnamespaces;
/* parallel list of String values */
List *arg_names pg_node_attr(query_jumble_ignore);
/* list of expressions */
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 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..67625c4773 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -225,6 +225,242 @@ 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)
+
+\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
----------
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 7505a14077..d370826b4a 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -150,6 +150,178 @@ 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.
+\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.
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index c07ed2b269..21c3e79120 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -221,6 +221,242 @@ 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)
+
+\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
----------
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index bac0388ac1..813027ddc2 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -66,6 +66,122 @@ 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')
+ );
+\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 ' ');
--
2.34.1
Hi
st 15. 1. 2025 v 21:35 odesílatel Jim Jones <jim.jones@uni-muenster.de>
napsal:
Hi Umar, Hi Pavel,
On 26.12.24 14:46, Jim Jones wrote:
The idea of NO DEFAULT is pretty much to free an element (and its
children) from a previous DEFAULT in the same scope.SELECT
xmlserialize(DOCUMENT
xmlelement(NAME "root",
xmlnamespaces(DEFAULT 'http:/x.y/ns1'),
xmlelement(NAME "foo",
xmlnamespaces(NO DEFAULT))
) AS text INDENT);xmlserialize
------------------------------
<root xmlns="http:/x.y/ns1">+
<foo xmlns=""/> +
</root>
(1 row)v3 is attached, now using xmlTextWriterWriteAttributeNS from libxml2 for
managing XML namespaces, instead of using xmlTextWriterWriteAttribute.
Libxml2 is quite lenient, allowing the duplication of default namespaces
within the same scope and even permitting NO DEFAULT namespaces when no
previous DEFAULT declaration has been made - both are semantically valid.The crux now is finding the appropriate balance between accuracy and
user intent. In the context of PostgreSQL's xmlelement and
xmlnamespaces, I would argue that explicitly declared namespaces,
redundant or not, ought to be preserved. A user who intentionally
repeats a namespace declaration might have sound reasons for doing so,
like ensuring clarity, preserving compatibility with external XML
processors, or sticking to a specific schema. Silently omitting these
declarations could lead to confusion.
Now, I have not any objections against the code
The patch has doc and enough regress tests
The patching and compilation without problems
make check-world passed
I'll mark this patch as ready for committer
Pavel has tidied up the parser modifications - it's looking much neater
now. Many thanks for that!
It was a pleasure
Regards
Pavel
Show quoted text
Best, Jim
On 16.01.25 08:36, Pavel Stehule wrote:
Now, I have not any objections against the code
The patch has doc and enough regress tests
The patching and compilation without problems
make check-world passedI'll mark this patch as ready for committer
rebase due to gram.y changes introduced in 80feb72
--
Jim
Attachments:
v4-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v4-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From d394083020dfca69f96f459d4852f77614934603 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Sun, 19 Jan 2025 10:27:07 +0100
Subject: [PATCH v4] Add XMLNamespaces option to XMLElement
This patch adds the scoped option XMLNamespaces to XMLElement,
as described in ISO/IEC 9075-14:2023, 11.2 XML lexically scoped
options:
xmlnamespaces(uri AS prefix, ...)
xmlnamespaces(DEFAULT uri, ...)
xmlnamespaces(NO DEFAULT, ...)
* prefix: Namespace's prefix.
* uri: Namespace's URI.
* DEFAULT prefix: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT: Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.
== Examples ==
SELECT xmlelement(NAME "foo", xmlnamespaces('http:/x.y' AS bar));
xmlelement
------------------------------
<foo xmlns:bar="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y'));
xmlelement
--------------------------
<foo xmlns="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
xmlelement
-----------------
<foo xmlns=""/>
---
doc/src/sgml/func.sgml | 57 ++++++-
src/backend/parser/gram.y | 100 ++++++++++--
src/backend/parser/parse_expr.c | 80 ++++++++++
src/backend/utils/adt/xml.c | 47 +++++-
src/include/nodes/primnodes.h | 4 +-
src/include/utils/xml.h | 6 +
src/test/regress/expected/xml.out | 236 ++++++++++++++++++++++++++++
src/test/regress/expected/xml_1.out | 172 ++++++++++++++++++++
src/test/regress/expected/xml_2.out | 236 ++++++++++++++++++++++++++++
src/test/regress/sql/xml.sql | 116 ++++++++++++++
10 files changed, 1032 insertions(+), 22 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 47370e581a..b33663bc09 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14482,7 +14482,10 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
</indexterm>
<synopsis>
-<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> <optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
+<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable>
+ <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional>
+ <optional>, <literal>XMLNAMESPACES</literal> ( {<replaceable>regular-nsuri</replaceable> <literal>AS</literal> <replaceable>nsprefix</replaceable> | DEFAULT <replaceable>default-nsuri</replaceable> | NO DEFAULT} <optional>, ...</optional> ) </optional>
+<optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
</synopsis>
<para>
@@ -14495,7 +14498,39 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
yield any <productname>PostgreSQL</productname> data type. The
argument(s) within <literal>XMLATTRIBUTES</literal> generate attributes
of the XML element; the <replaceable>content</replaceable> value(s) are
- concatenated to form its content.
+ concatenated to form its content. The arguments within <literal>XMLNAMESPACES</literal>
+ constuct namespace declarations from values provided in <replaceable>nsuri</replaceable>
+ and <replaceable>nsprefix</replaceable>, which correspond to the URI of a namespace and
+ its prefix, respectively. The option <literal>DEFAULT</literal> can be used to set the
+ default namespace declaration (without a prefix) to the URI provided in <replaceable>default-nsuri</replaceable>.
+ The option <literal>NO DEFAULT</literal> states that a namespace scope has no default namespace. A valid
+ <literal>XMLNAMESPACES</literal> item must fulfill the following conditions:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Only a single <literal>DEFAULT</literal> declaration item within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No two <replaceable>nsuri</replaceable> can be equal within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No <replaceable>nsprefix</replaceable> can be equal to <literal>xml</literal> or <literal>xmlns</literal>,
+ and no <replaceable>nsuri</replaceable> can be equal to <literal>http://www.w3.org/2000/xmlns/</literal>
+ or to <literal>http://www.w3.org/XML/1998/namespace</literal>, as they are already bouned to standard XML declarations.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The value of a <replaceable>regular-nsuri</replaceable> cannot be a zero-length string.
+ </para>
+ </listitem>
+ </itemizedlist>
+
</para>
<para>
@@ -14518,6 +14553,24 @@ SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');
xmlelement
-------------------------------------
<foo bar="2007-01-26">content</foo>
+
+SELECT xmlelement(NAME "foo:root", xmlnamespaces('http:/foo.bar/' AS foo), 'content');
+
+ xmlelement
+---------------------------------------------------------
+ <foo:root xmlns:foo="http:/foo.bar/">content</foo:root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/foo.bar/'), 'content');
+
+ xmlelement
+---------------------------------------------
+ <root xmlns="http:/foo.bar/">content</root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT), 'content');
+
+ xmlelement
+-------------------------------
+ <root xmlns="">content</root>
]]></screen>
</para>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d7f9c00c40..57c68b05b3 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
@@ -14278,6 +14286,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = NULL;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15331,12 +15348,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);
}
@@ -15478,12 +15495,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);
}
@@ -16018,21 +16035,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);
}
@@ -16047,12 +16064,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);
@@ -16069,7 +16091,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 ')'
@@ -16273,6 +16295,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); }
;
@@ -16309,6 +16334,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
@@ -19281,7 +19344,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);
@@ -19293,7 +19356,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_expr.c b/src/backend/parser/parse_expr.c
index bad1df732e..13f8a58ade 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));
+ 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/xml.c b/src/backend/utils/adt/xml.c
index db8d0d6a7e..ce79d97a5b 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 (argname && 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,
+ !argname ? NULL : (const xmlChar *) prefix,
+ argname ? (const xmlChar *) argname : (const xmlChar *) prefix,
+ NULL,
+ (const xmlChar *) str);
+ }
+ else if (str)
xmlTextWriterWriteAttribute(writer,
(xmlChar *) argname,
(xmlChar *) str);
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 59e7bb26bb..4e0bc0a96b 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1609,7 +1609,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]) */
@@ -1633,6 +1633,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..67625c4773 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -225,6 +225,242 @@ 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)
+
+\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
----------
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 7505a14077..d370826b4a 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -150,6 +150,178 @@ 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.
+\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.
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index c07ed2b269..21c3e79120 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -221,6 +221,242 @@ 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)
+
+\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
----------
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index bac0388ac1..813027ddc2 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -66,6 +66,122 @@ 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')
+ );
+\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 ' ');
--
2.34.1
Hi Jim & Pavel,
Sorry for getting back a bit late on this. Few more case you might
need consider:
As I mentioned in my first static review about XMLTable existing
behaviour might change, I give it a run time review and here are few
findings:
1. Because of this patch XMLTable namespace will accept NO DEFAULT
value, I was expecting an error message based on prior behavior '' but
If I run following query it show something different:
"
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: cache lookup failed for type 0
"
Can you please re-check this behaviour (there might be something wrong
with my build environment)
2. Seems like namespace as ColumnRef was already allowed (I doubt if
that's a correct implementation, I see other DB allows only strings
AFAIK), for non-default namespaces may be its fair, should this be
allowed for default namespace, any opinion.
create table tbl1 ( col1 text);
insert into tbl1 values ('abc');
insert into tbl1 values ('def');
select xmlelement(NAME "root", xmlnamespaces(default col1)) from tbl1;
xmlelement
---------------------
<root xmlns="abc"/>
<root xmlns="def"/>
(2 rows)
Changing status in commitfest, feel free to revert back.
Regards
Umar Hayat
On Sun, 19 Jan 2025 at 18:33, Jim Jones <jim.jones@uni-muenster.de> wrote:
On 16.01.25 08:36, Pavel Stehule wrote:
Now, I have not any objections against the code
The patch has doc and enough regress tests
The patching and compilation without problems
make check-world passedI'll mark this patch as ready for committer
rebase due to gram.y changes introduced in 80feb72
--
Jim
--
Umar Hayat
Bitnine (https://bitnine.net/)
Hi Umar
On 20.01.25 17:19, Umar Hayat wrote:
Hi Jim & Pavel,
Sorry for getting back a bit late on this. Few more case you might
need consider:As I mentioned in my first static review about XMLTable existing
behaviour might change, I give it a run time review and here are few
findings:1. Because of this patch XMLTable namespace will accept NO DEFAULT
value, I was expecting an error message based on prior behavior '' but
If I run following query it show something different:
"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: cache lookup failed for type 0
"
Can you please re-check this behaviour (there might be something wrong
with my build environment)
There is nothing wrong with your environment :) I forgot to test
XMLTable for NO DEFAULT namespaces. To be consistent with the existing
code, I added the following condition to transformRangeTableFunc:
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);
It adds an empty string to the uri, which is the value expected for NO
DEFAULT.
I also added a NO DEFAULT test for XMLTable in xml.sql
v5 attached.
2. Seems like namespace as ColumnRef was already allowed (I doubt if
that's a correct implementation, I see other DB allows only strings
AFAIK), for non-default namespaces may be its fair, should this be
allowed for default namespace, any opinion.
create table tbl1 ( col1 text);
insert into tbl1 values ('abc');
insert into tbl1 values ('def');
select xmlelement(NAME "root", xmlnamespaces(default col1)) from tbl1;
xmlelement
---------------------
<root xmlns="abc"/>
<root xmlns="def"/>
(2 rows)
What are your concerns about supporting ColumnRef for the URI's?
It is currently supported by XMLAttributes:
CREATE TABLE t AS SELECT 'http://x.y' AS uri;
SELECT xmlelement(NAME el, xmlattributes("uri" AS att)) FROM t;
xmlelement
------------------------
<el att="http://x.y"/>
(1 row)
Many thanks for the review!
Best, Jim
--
Jim
Attachments:
v5-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v5-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From cc1e6e68b0de2bfa7e2a15f70d3359b62679aed6 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Mon, 20 Jan 2025 19:43:14 +0100
Subject: [PATCH v5] Add XMLNamespaces option to XMLElement
This patch adds the scoped option XMLNamespaces to XMLElement,
as described in ISO/IEC 9075-14:2023, 11.2 XML lexically scoped
options:
xmlnamespaces(uri AS prefix, ...)
xmlnamespaces(DEFAULT uri, ...)
xmlnamespaces(NO DEFAULT, ...)
* prefix: Namespace's prefix.
* uri: Namespace's URI.
* DEFAULT prefix: Specifies the DEFAULT namespace to use within
the scope of a namespace declaration.
* NO DEFAULT: Specifies that NO DEFAULT namespace is to be
used within the scope of a namespace declaration.
== Examples ==
SELECT xmlelement(NAME "foo", xmlnamespaces('http:/x.y' AS bar));
xmlelement
------------------------------
<foo xmlns:bar="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y'));
xmlelement
--------------------------
<foo xmlns="http:/x.y"/>
SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
xmlelement
-----------------
<foo xmlns=""/>
---
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/xml.c | 53 +++++-
src/include/nodes/primnodes.h | 4 +-
src/include/utils/xml.h | 6 +
src/test/regress/expected/xml.out | 241 ++++++++++++++++++++++++++++
src/test/regress/expected/xml_1.out | 180 +++++++++++++++++++++
src/test/regress/expected/xml_2.out | 241 ++++++++++++++++++++++++++++
src/test/regress/sql/xml.sql | 121 ++++++++++++++
11 files changed, 1066 insertions(+), 24 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 47370e581a..b33663bc09 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14482,7 +14482,10 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
</indexterm>
<synopsis>
-<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> <optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
+<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable>
+ <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional>
+ <optional>, <literal>XMLNAMESPACES</literal> ( {<replaceable>regular-nsuri</replaceable> <literal>AS</literal> <replaceable>nsprefix</replaceable> | DEFAULT <replaceable>default-nsuri</replaceable> | NO DEFAULT} <optional>, ...</optional> ) </optional>
+<optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue>
</synopsis>
<para>
@@ -14495,7 +14498,39 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone=
yield any <productname>PostgreSQL</productname> data type. The
argument(s) within <literal>XMLATTRIBUTES</literal> generate attributes
of the XML element; the <replaceable>content</replaceable> value(s) are
- concatenated to form its content.
+ concatenated to form its content. The arguments within <literal>XMLNAMESPACES</literal>
+ constuct namespace declarations from values provided in <replaceable>nsuri</replaceable>
+ and <replaceable>nsprefix</replaceable>, which correspond to the URI of a namespace and
+ its prefix, respectively. The option <literal>DEFAULT</literal> can be used to set the
+ default namespace declaration (without a prefix) to the URI provided in <replaceable>default-nsuri</replaceable>.
+ The option <literal>NO DEFAULT</literal> states that a namespace scope has no default namespace. A valid
+ <literal>XMLNAMESPACES</literal> item must fulfill the following conditions:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Only a single <literal>DEFAULT</literal> declaration item within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No two <replaceable>nsuri</replaceable> can be equal within the same scope.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No <replaceable>nsprefix</replaceable> can be equal to <literal>xml</literal> or <literal>xmlns</literal>,
+ and no <replaceable>nsuri</replaceable> can be equal to <literal>http://www.w3.org/2000/xmlns/</literal>
+ or to <literal>http://www.w3.org/XML/1998/namespace</literal>, as they are already bouned to standard XML declarations.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The value of a <replaceable>regular-nsuri</replaceable> cannot be a zero-length string.
+ </para>
+ </listitem>
+ </itemizedlist>
+
</para>
<para>
@@ -14518,6 +14553,24 @@ SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');
xmlelement
-------------------------------------
<foo bar="2007-01-26">content</foo>
+
+SELECT xmlelement(NAME "foo:root", xmlnamespaces('http:/foo.bar/' AS foo), 'content');
+
+ xmlelement
+---------------------------------------------------------
+ <foo:root xmlns:foo="http:/foo.bar/">content</foo:root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/foo.bar/'), 'content');
+
+ xmlelement
+---------------------------------------------
+ <root xmlns="http:/foo.bar/">content</root>
+
+ SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT), 'content');
+
+ xmlelement
+-------------------------------
+ <root xmlns="">content</root>
]]></screen>
</para>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d7f9c00c40..57c68b05b3 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
@@ -14278,6 +14286,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = NULL;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15331,12 +15348,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);
}
@@ -15478,12 +15495,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);
}
@@ -16018,21 +16035,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);
}
@@ -16047,12 +16064,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);
@@ -16069,7 +16091,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 ')'
@@ -16273,6 +16295,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); }
;
@@ -16309,6 +16334,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
@@ -19281,7 +19344,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);
@@ -19293,7 +19356,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..13f8a58ade 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));
+ 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/xml.c b/src/backend/utils/adt/xml.c
index db8d0d6a7e..2672025261 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 (argname && 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,
+ !argname ? NULL : (const xmlChar *) prefix,
+ argname ? (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 59e7bb26bb..4e0bc0a96b 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1609,7 +1609,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]) */
@@ -1633,6 +1633,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..c69d988459 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -225,6 +225,242 @@ 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)
+
+\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 +1634,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..3ee333d46f 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -150,6 +150,178 @@ 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.
+\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 +1240,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..4bf16c15bd 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -221,6 +221,242 @@ 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)
+
+\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 +1620,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..516d1f7c8f 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -66,6 +66,122 @@ 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')
+ );
+\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 +569,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
On Tue, 21 Jan 2025 at 04:04, Jim Jones <jim.jones@uni-muenster.de> wrote:
Hi Umar
On 20.01.25 17:19, Umar Hayat wrote:
Hi Jim & Pavel,
Sorry for getting back a bit late on this. Few more case you might
need consider:As I mentioned in my first static review about XMLTable existing
behaviour might change, I give it a run time review and here are few
findings:1. Because of this patch XMLTable namespace will accept NO DEFAULT
value, I was expecting an error message based on prior behavior '' but
If I run following query it show something different:
"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: cache lookup failed for type 0
"
Can you please re-check this behaviour (there might be something wrong
with my build environment)There is nothing wrong with your environment :) I forgot to test
XMLTable for NO DEFAULT namespaces. To be consistent with the existing
code, I added the following condition to transformRangeTableFunc: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);It adds an empty string to the uri, which is the value expected for NO
DEFAULT.I also added a NO DEFAULT test for XMLTable in xml.sql
v5 attached.
2. Seems like namespace as ColumnRef was already allowed (I doubt if
that's a correct implementation, I see other DB allows only strings
AFAIK), for non-default namespaces may be its fair, should this be
allowed for default namespace, any opinion.
create table tbl1 ( col1 text);
insert into tbl1 values ('abc');
insert into tbl1 values ('def');
select xmlelement(NAME "root", xmlnamespaces(default col1)) from tbl1;
xmlelement
---------------------
<root xmlns="abc"/>
<root xmlns="def"/>
(2 rows)What are your concerns about supporting ColumnRef for the URI's?
It is currently supported by XMLAttributes:
For XMLAttributes attribute it should have ColumnRef/Expr because
that's the data/content we want to generate. But namespaces and xml
tags, IMO they should be considered as part of the structure/schema of
XML. Allowing namespaces (default or otherwise) to be generated
arbitrarily for each record does not seem correct to me, it's like
generating arbitrary XML using print string which does not require XML
functions.
- DB2 allows XMLElements namespace but it does not allow Expr/ColumnRef.
- Oracle Allow namespace in only XMLTable, and it does not allow Expr/ColumnRef.
- Having SConst/String or numeric can limit the error handling at
parsing stage which can validate the schema instead of expression
evaluation per record, which leads to following problem at runtime:
CREATE TABLE xmltab (uri TEXT);
INSERT INTO xmltab VALUES ('good'), ('');
SELECT XMLElement(NAME "root", XMLNamespaces(uri AS zz)) from xmltab;
ERROR: invalid XML namespace URI for "zz"
DETAIL: a regular XML namespace cannot be a zero-length string
Imagine there millions of records and in the middle it fails.
- Also expression evaluation per record and doing validation (valid
URI etc) I believe will have some performance impact ( I haven't
tested it, so can't say for sure , how much )
- It's possible that there might be some other angle that I am missing
right now, maybe Pavel/Àlvaro can shed light on this. I briefly went
through the first implementation thread. There was a bit of discussion
to use b_expr instead of c_expr, but I have yet to explore the
reasoning of not using SConst.
CREATE TABLE t AS SELECT 'http://x.y' AS uri;
SELECT xmlelement(NAME el, xmlattributes("uri" AS att)) FROM t;xmlelement
------------------------
<el att="http://x.y"/>
(1 row)Many thanks for the review!
Best, Jim
--
Jim
--
Umar Hayat
Bitnine (https://bitnine.net/)
Hi
út 21. 1. 2025 v 11:45 odesílatel Umar Hayat <postgresql.wizard@gmail.com>
napsal:
On Tue, 21 Jan 2025 at 04:04, Jim Jones <jim.jones@uni-muenster.de> wrote:
Hi Umar
On 20.01.25 17:19, Umar Hayat wrote:
Hi Jim & Pavel,
Sorry for getting back a bit late on this. Few more case you might
need consider:As I mentioned in my first static review about XMLTable existing
behaviour might change, I give it a run time review and here are few
findings:1. Because of this patch XMLTable namespace will accept NO DEFAULT
value, I was expecting an error message based on prior behavior '' but
If I run following query it show something different:
"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: cache lookup failed for type 0
"
Can you please re-check this behaviour (there might be something wrong
with my build environment)There is nothing wrong with your environment :) I forgot to test
XMLTable for NO DEFAULT namespaces. To be consistent with the existing
code, I added the following condition to transformRangeTableFunc: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);It adds an empty string to the uri, which is the value expected for NO
DEFAULT.I also added a NO DEFAULT test for XMLTable in xml.sql
v5 attached.
2. Seems like namespace as ColumnRef was already allowed (I doubt if
that's a correct implementation, I see other DB allows only strings
AFAIK), for non-default namespaces may be its fair, should this be
allowed for default namespace, any opinion.
create table tbl1 ( col1 text);
insert into tbl1 values ('abc');
insert into tbl1 values ('def');
select xmlelement(NAME "root", xmlnamespaces(default col1)) from tbl1;
xmlelement
---------------------
<root xmlns="abc"/>
<root xmlns="def"/>
(2 rows)What are your concerns about supporting ColumnRef for the URI's?
It is currently supported by XMLAttributes:
For XMLAttributes attribute it should have ColumnRef/Expr because
that's the data/content we want to generate. But namespaces and xml
tags, IMO they should be considered as part of the structure/schema of
XML. Allowing namespaces (default or otherwise) to be generated
arbitrarily for each record does not seem correct to me, it's like
generating arbitrary XML using print string which does not require XML
functions.- DB2 allows XMLElements namespace but it does not allow Expr/ColumnRef.
- Oracle Allow namespace in only XMLTable, and it does not allow
Expr/ColumnRef.- Having SConst/String or numeric can limit the error handling at
parsing stage which can validate the schema instead of expression
evaluation per record, which leads to following problem at runtime:CREATE TABLE xmltab (uri TEXT);
INSERT INTO xmltab VALUES ('good'), ('');
SELECT XMLElement(NAME "root", XMLNamespaces(uri AS zz)) from xmltab;
ERROR: invalid XML namespace URI for "zz"
DETAIL: a regular XML namespace cannot be a zero-length stringImagine there millions of records and in the middle it fails.
- Also expression evaluation per record and doing validation (valid
URI etc) I believe will have some performance impact ( I haven't
tested it, so can't say for sure , how much )- It's possible that there might be some other angle that I am missing
right now, maybe Pavel/Àlvaro can shed light on this. I briefly went
through the first implementation thread. There was a bit of discussion
to use b_expr instead of c_expr, but I have yet to explore the
reasoning of not using SConst.
Postgres usually doesn't force string constants in functions or
pseudofunctions arguments.
I remember a discussion related to string_agg, where the field separator is
not constant too, although it makes sense there.
I think there are more cases - probably aggregate (maybe window) functions
where postgres allows any expression and other databases allow just
constants there.
Personally, using only string constants can be sometimes too messy (when I
use other db).
Regards
Pavel
Show quoted text
CREATE TABLE t AS SELECT 'http://x.y' AS uri;
SELECT xmlelement(NAME el, xmlattributes("uri" AS att)) FROM t;xmlelement
------------------------
<el att="http://x.y"/>
(1 row)Many thanks for the review!
Best, Jim
--
Jim--
Umar Hayat
Bitnine (https://bitnine.net/)
On 21.01.25 11:48, Umar Hayat wrote:
For XMLAttributes attribute it should have ColumnRef/Expr because
that's the data/content we want to generate. But namespaces and xml
tags, IMO they should be considered as part of the structure/schema of
XML. Allowing namespaces (default or otherwise) to be generated
arbitrarily for each record does not seem correct to me, it's like
generating arbitrary XML using print string which does not require XML
functions.
I'm not sure I completely get your 'print string' argument. Namespaces
are added to the element using libxml2's xmlTextWriterWriteAttributeNS
function, so we’re letting libxml2 handle whether a namespace
declaration is valid or not. Of course, there are still some extra
checks required by the SQL/XML standard.
- DB2 allows XMLElements namespace but it does not allow Expr/ColumnRef.
- Oracle Allow namespace in only XMLTable, and it does not allow Expr/ColumnRef.- Having SConst/String or numeric can limit the error handling at
parsing stage which can validate the schema instead of expression
evaluation per record, which leads to following problem at runtime:CREATE TABLE xmltab (uri TEXT);
INSERT INTO xmltab VALUES ('good'), ('');
SELECT XMLElement(NAME "root", XMLNamespaces(uri AS zz)) from xmltab;
ERROR: invalid XML namespace URI for "zz"
DETAIL: a regular XML namespace cannot be a zero-length stringImagine there millions of records and in the middle it fails.
I don't think discarding a feature just because the input data might
raise an exception in a long transaction is a strong argument here. For
your specific case, the user can always use a WHERE clause to filter out
any URIs that aren’t valid for XMLNamespace. Additionally, the
documentation already mentions this specific limitation:
"The value of a <replaceable>regular-nsuri</replaceable> cannot be a
zero-length string."
So it shouldn’t really catch anyone off guard :)
Thanks for the review!
Best, Jim
Pavel and Jim,
If that's the case, it looks good to me.
Just wanted to highlight potential issues and implementation
differences compared to other databases.
Regards
Umar Hayat
po 27. 1. 2025 v 14:57 odesílatel Umar Hayat <postgresql.wizard@gmail.com>
napsal:
Pavel and Jim,
If that's the case, it looks good to me.
Just wanted to highlight potential issues and implementation
differences compared to other databases.
It is correct.
Every Time there will be some differences - DB2 has an absolutely
different stack for XML processing, Oracle has handy written parser, and
allows some syntaxes that Postgres does not, but for some cases it is
unfriendly, strict and restrictive.
And we are searching for some good compromise - between consistency with
self, consistency with standard, and good usability and good portability.
Regards
Pavel
Show quoted text
Regards
Umar Hayat
I've attached v6, which addresses two issues:
* Fixes a bug where XMLNAMESPACES declarations within a view were being
serialized as XMLATTRIBUTES.
* Prevents the makeString function from being called with a NULL
parameter - discussed in this thread [1]/messages/by-id/CAFj8pRC24FEBNfTUrDgAK8f2nqDVvzWCuq=R=T19nUjL9GuLBA@mail.gmail.com.
Best regards, Jim
[1]: /messages/by-id/CAFj8pRC24FEBNfTUrDgAK8f2nqDVvzWCuq=R=T19nUjL9GuLBA@mail.gmail.com
/messages/by-id/CAFj8pRC24FEBNfTUrDgAK8f2nqDVvzWCuq=R=T19nUjL9GuLBA@mail.gmail.com
Attachments:
v6-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v6-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
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="<>&"'" 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
----------
@@ -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="<>&"'" 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
----------
@@ -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
The following review has been posted through the commitfest application:
make installcheck-world: not tested
Implements feature: not tested
Spec compliant: not tested
Documentation: not tested
Hi Pavel,
I have tested this patch, and it proves to be highly useful when handling XMLNAMESPACES() with both DEFAULT and NO DEFAULT options. The following test cases confirm its correctness:
SELECT xmlelement(
NAME "foo",
XMLNAMESPACES('http://x.y' AS xy, 'http://a.b' AS ab, DEFAULT 'http://d.e'),
xmlelement(NAME "foot",
xmlelement(NAME "xy:shoe"),
xmlelement(NAME "ab:lace")
)
);
SELECT xmlelement(
NAME "foo",
XMLNAMESPACES('http://x.y' AS xy, 'http://a.b' AS ab, NO DEFAULT),
xmlelement(NAME "foot",
xmlelement(NAME "xy:shoe"),
xmlelement(NAME "ab:lace")
)
);
Additionally, I verified that the patch correctly supports multiple namespaces when using both DEFAULT and NO DEFAULT, ensuring expected behavior across different use cases.
Great work on this improvement!
rebase
--
Jim
Attachments:
v7-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v7-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From c439fe867d360a822e3e8da99d12e2afe579bbaf Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Tue, 10 Jun 2025 08:39:25 +0200
Subject: [PATCH v7] 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));
SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y'));
SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT));
---
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 | 55 +++++-
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, 1202 insertions(+), 34 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c67688cbf5..10b5682542 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14720,7 +14720,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>
@@ -14733,7 +14736,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>
@@ -14756,6 +14791,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 0b5652071d..76f1638299 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
@@ -14315,6 +14323,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = NULL;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15368,12 +15385,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);
}
@@ -15515,12 +15532,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);
}
@@ -16055,21 +16072,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);
}
@@ -16084,12 +16101,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);
@@ -16106,7 +16128,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 ')'
@@ -16310,6 +16332,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); }
;
@@ -16346,6 +16371,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
@@ -19322,7 +19385,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);
@@ -19334,7 +19397,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 1f8e2d5467..9f1afb21ed 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2355,6 +2355,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
XmlExpr *newx;
ListCell *lc;
int i;
+ bool has_default_xmlns = false;
newx = makeNode(XmlExpr);
newx->op = x->op;
@@ -2374,6 +2375,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);
@@ -2405,6 +2480,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),
@@ -2416,6 +2495,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 a4150bff2e..40ce43bccf 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
@@ -872,6 +871,7 @@ xmlelement(XmlExpr *xexpr,
int i;
ListCell *arg;
ListCell *narg;
+ ListCell *nsarg;
PgXmlErrorContext *xmlerrcxt;
volatile xmlBufferPtr buf = NULL;
volatile xmlTextWriterPtr writer = NULL;
@@ -933,12 +933,55 @@ 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.
+ */
+ 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);
@@ -4802,7 +4845,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 7d3b4198f2..e4ab2b8cd3 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 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.34.1
rebase
--
Jim
Attachments:
v8-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v8-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From 9c36deb99e600fe9a76fd1942081f3031bbd6216 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Mon, 16 Jun 2025 14:43:11 +0200
Subject: [PATCH v8] Add XMLNamespaces option to XMLElement
This patch adds the scoped option XMLNamespaces to the XMLElement()
function, as described in ISO/IEC 9075-14:2023, section 11.2,
"XML lexically scoped options".
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));
---
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 | 55 +++++-
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, 1202 insertions(+), 34 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c67688cbf5..10b5682542 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14720,7 +14720,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>
@@ -14733,7 +14736,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>
@@ -14756,6 +14791,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 50f53159d5..e605d173e9 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
@@ -14292,6 +14300,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = NULL;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15346,12 +15363,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);
}
@@ -15493,12 +15510,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);
}
@@ -16033,21 +16050,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);
}
@@ -16062,12 +16079,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);
@@ -16084,7 +16106,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 ')'
@@ -16288,6 +16310,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); }
;
@@ -16324,6 +16349,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
@@ -19246,7 +19309,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);
@@ -19258,7 +19321,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 a4150bff2e..40ce43bccf 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
@@ -872,6 +871,7 @@ xmlelement(XmlExpr *xexpr,
int i;
ListCell *arg;
ListCell *narg;
+ ListCell *nsarg;
PgXmlErrorContext *xmlerrcxt;
volatile xmlBufferPtr buf = NULL;
volatile xmlTextWriterPtr writer = NULL;
@@ -933,12 +933,55 @@ 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.
+ */
+ 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);
@@ -4802,7 +4845,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 01510b01b6..e51a31671b 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1587,7 +1587,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]) */
@@ -1611,6 +1611,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.34.1
rebased due to changes in 2e94721
--
Jim
Attachments:
v9-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v9-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From 1c6a9f73de9119beb0e251e9457b4e25d2e99ee4 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Tue, 1 Jul 2025 09:37:09 +0200
Subject: [PATCH v9] 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.
Documentation and regression tests are included.
---
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 | 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.sgml b/doc/src/sgml/func.sgml
index 126b8cfbad..93ea2d9e4b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14730,7 +14730,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>
@@ -14743,7 +14746,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>
@@ -14766,6 +14801,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 50f53159d5..e605d173e9 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
@@ -14292,6 +14300,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = NULL;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15346,12 +15363,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);
}
@@ -15493,12 +15510,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);
}
@@ -16033,21 +16050,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);
}
@@ -16062,12 +16079,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);
@@ -16084,7 +16106,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 ')'
@@ -16288,6 +16310,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); }
;
@@ -16324,6 +16349,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
@@ -19246,7 +19309,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);
@@ -19258,7 +19321,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 2bd39b6ac4..6561b6b14b 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
@@ -897,6 +896,7 @@ xmlelement(XmlExpr *xexpr,
int i;
ListCell *arg;
ListCell *narg;
+ ListCell *nsarg;
PgXmlErrorContext *xmlerrcxt;
volatile xmlBufferPtr buf = NULL;
volatile xmlTextWriterPtr writer = NULL;
@@ -961,20 +961,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)
@@ -4848,7 +4889,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
rebased due to recent changes in doc/src/sgml/func.sgml
--
Jim
Attachments:
v10-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v10-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From f1b797c15f4214bf654a3ca9c82d51f0fef93cbd Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
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
rebased
Jim
Attachments:
v11-0001-Add-XMLNamespaces-option-to-XMLElement.patchtext/x-patch; charset=UTF-8; name=v11-0001-Add-XMLNamespaces-option-to-XMLElement.patchDownload
From 7ced765fd7d485ce8974d90305e12bc0d90b7791 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Wed, 3 Dec 2025 09:11:17 +0100
Subject: [PATCH v11] 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 | 8 +-
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, 1206 insertions(+), 39 deletions(-)
diff --git a/doc/src/sgml/func/func-xml.sgml b/doc/src/sgml/func/func-xml.sgml
index 511bc90852..abd7e96fbf 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 c3a0a354a9..9f26cf6a27 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -137,6 +137,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, 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);
@@ -272,6 +278,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;
}
@@ -625,8 +632,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
@@ -14424,6 +14432,15 @@ xml_namespace_el:
$$->val = $2;
$$->location = @1;
}
+ | NO DEFAULT
+ {
+ $$ = makeNode(ResTarget);
+ $$->name = NULL;
+ $$->indirection = NIL;
+ $$->val = NULL;
+ $$->location = @1;
+ }
+
;
json_table:
@@ -15478,12 +15495,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);
}
@@ -15625,12 +15642,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);
}
@@ -16166,21 +16183,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);
}
@@ -16195,12 +16212,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);
@@ -16217,7 +16239,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 ')'
@@ -16421,6 +16443,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); }
;
@@ -16457,6 +16482,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
@@ -19412,7 +19475,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);
@@ -19424,7 +19487,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 ca26f6f61f..ea7c0b8fe3 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -842,7 +842,13 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
Node *ns_uri;
Assert(IsA(r, ResTarget));
- ns_uri = transformExpr(pstate, r->val, EXPR_KIND_FROM_FUNCTION);
+
+ if (!r->name && !r->val)
+ /* Create an empty String for NO DEFAULT namespaces */
+ 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 4524e49c32..52439ab702 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2360,6 +2360,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x)
XmlExpr *newx;
ListCell *lc;
int i;
+ bool has_default_xmlns = false;
newx = makeNode(XmlExpr);
newx->op = x->op;
@@ -2379,6 +2380,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);
@@ -2410,6 +2485,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),
@@ -2421,6 +2500,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 6cf90be40b..6ec33beed6 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10094,6 +10094,7 @@ get_rule_expr(Node *node, deparse_context *context,
bool needcomma = false;
ListCell *arg;
ListCell *narg;
+ ListCell *nsarg;
Const *con;
switch (xexpr->op)
@@ -10137,26 +10138,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(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 41e775570e..78078fe5a4 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -243,7 +243,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
@@ -901,6 +900,7 @@ xmlelement(XmlExpr *xexpr,
int i;
ListCell *arg;
ListCell *narg;
+ ListCell *nsarg;
PgXmlErrorContext *xmlerrcxt;
volatile xmlBufferPtr buf = NULL;
volatile xmlTextWriterPtr writer = NULL;
@@ -965,20 +965,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)
@@ -4836,7 +4877,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 1b4436f2ff..f97b4f64ca 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1603,7 +1603,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]) */
@@ -1627,6 +1627,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 732dac47bc..fbd6e53d79 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