v1-0001-Add-XMLNamespaces-option-to-XMLElement.patch
text/x-patch
Filename: v1-0001-Add-XMLNamespaces-option-to-XMLElement.patch
Type: text/x-patch
Part: 0
Message:
Add XMLNamespaces to XMLElement
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v1-0001
Subject: Add XMLNamespaces option to XMLElement
| File | + | − |
|---|---|---|
| doc/src/sgml/func.sgml | 55 | 2 |
| src/backend/parser/gram.y | 55 | 18 |
| src/backend/parser/parse_expr.c | 73 | 0 |
| src/backend/utils/adt/xml.c | 31 | 1 |
| src/include/nodes/primnodes.h | 3 | 1 |
| src/include/utils/xml.h | 6 | 0 |
| src/test/regress/expected/xml_1.out | 151 | 0 |
| src/test/regress/expected/xml_2.out | 205 | 0 |
| src/test/regress/expected/xml.out | 205 | 0 |
| src/test/regress/sql/xml.sql | 100 | 0 |
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