Hi, I'd like to propose the implementation of the XMLNamespaces option for XMLElement.
XMLNAMESPACES(nsuri AS nsprefix) XMLNAMESPACES(DEFAULT default-nsuri) XMLNAMESPACES(NO DEFAULT) * nsprefix: Namespace's prefix. * nsuri: Namespace's URI. * DEFAULT default-nsuri: Specifies the DEFAULT namespace to use within the scope of a namespace declaration. * NO DEFAULT: Specifies that NO DEFAULT namespace is to be used within the scope of a namespace declaration. This basically works pretty much like XMLAttributes, but with a few more restrictions (see SQL/XML:2023, 11.2 <XML lexically scoped options>): * XML namespace declaration shall contain at most one DEFAULT namespace declaration item. * No namespace prefix shall be equivalent to xml or xmlns. * No namespace URI shall be identical to http://www.w3.org/2000/xmlns/ or to http://www.w3.org/XML/1998/namespace. * The value of a namespace URI contained in an regular namespace declaration item (no DEFAULT) shall not be a zero-length string. Examples: SELECT xmlelement(NAME "foo", xmlnamespaces('http://x.y' AS bar)); xmlelement ------------------------------- <foo xmlns:bar="http://x.y"/> SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http://x.y')); xmlelement --------------------------- <foo xmlns="http://x.y"/> SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT)); xmlelement ----------------- <foo xmlns=""/> In transformXmlExpr() it seemed convenient to use the same parameters to store the prefixes and URIs as in XMLAttributes (arg_names and named_args), but I am still not so sure it is the right approach. Is there perhaps a better way? Any thoughts? Feedback welcome! Best, Jim
From 8dee3772be0d89b3d49eff17344ff53440e5f566 Mon Sep 17 00:00:00 2001 From: Jim Jones <jim.jo...@uni-muenster.de> Date: Fri, 20 Dec 2024 14:50:51 +0100 Subject: [PATCH v1] Add XMLNamespaces option to XMLElement This patch adds the scoped option XMLNamespaces to XMLElement, as described in ISO/IEC 9075-14:2023, 11.2 XML lexically scoped options: xmlnamespaces(uri AS prefix, ...) xmlnamespaces(DEFAULT uri, ...) xmlnamespaces(NO DEFAULT, ...) * prefix: Namespace's prefix. * uri: Namespace's URI. * DEFAULT prefix: Specifies the DEFAULT namespace to use within the scope of a namespace declaration. * NO DEFAULT: Specifies that NO DEFAULT namespace is to be used within the scope of a namespace declaration. == Examples == SELECT xmlelement(NAME "foo", xmlnamespaces('http:/x.y' AS bar)); xmlelement ------------------------------ <foo xmlns:bar="http:/x.y"/> SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y')); xmlelement -------------------------- <foo xmlns="http:/x.y"/> SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT)); xmlelement ----------------- <foo xmlns=""/> Tests and documentation were updated accordingly. --- doc/src/sgml/func.sgml | 57 +++++++- src/backend/parser/gram.y | 73 +++++++--- src/backend/parser/parse_expr.c | 73 ++++++++++ src/backend/utils/adt/xml.c | 32 ++++- src/include/nodes/primnodes.h | 4 +- src/include/utils/xml.h | 6 + src/test/regress/expected/xml.out | 205 ++++++++++++++++++++++++++++ src/test/regress/expected/xml_1.out | 151 ++++++++++++++++++++ src/test/regress/expected/xml_2.out | 205 ++++++++++++++++++++++++++++ src/test/regress/sql/xml.sql | 100 ++++++++++++++ 10 files changed, 884 insertions(+), 22 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 47370e581a..b33663bc09 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -14482,7 +14482,10 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone= </indexterm> <synopsis> -<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> <optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue> +<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> + <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> + <optional>, <literal>XMLNAMESPACES</literal> ( {<replaceable>regular-nsuri</replaceable> <literal>AS</literal> <replaceable>nsprefix</replaceable> | DEFAULT <replaceable>default-nsuri</replaceable> | NO DEFAULT} <optional>, ...</optional> ) </optional> +<optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue> </synopsis> <para> @@ -14495,7 +14498,39 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone= yield any <productname>PostgreSQL</productname> data type. The argument(s) within <literal>XMLATTRIBUTES</literal> generate attributes of the XML element; the <replaceable>content</replaceable> value(s) are - concatenated to form its content. + concatenated to form its content. The arguments within <literal>XMLNAMESPACES</literal> + constuct namespace declarations from values provided in <replaceable>nsuri</replaceable> + and <replaceable>nsprefix</replaceable>, which correspond to the URI of a namespace and + its prefix, respectively. The option <literal>DEFAULT</literal> can be used to set the + default namespace declaration (without a prefix) to the URI provided in <replaceable>default-nsuri</replaceable>. + The option <literal>NO DEFAULT</literal> states that a namespace scope has no default namespace. A valid + <literal>XMLNAMESPACES</literal> item must fulfill the following conditions: + + <itemizedlist> + <listitem> + <para> + Only a single <literal>DEFAULT</literal> declaration item within the same scope. + </para> + </listitem> + <listitem> + <para> + No two <replaceable>nsuri</replaceable> can be equal within the same scope. + </para> + </listitem> + <listitem> + <para> + No <replaceable>nsprefix</replaceable> can be equal to <literal>xml</literal> or <literal>xmlns</literal>, + and no <replaceable>nsuri</replaceable> can be equal to <literal>http://www.w3.org/2000/xmlns/</literal> + or to <literal>http://www.w3.org/XML/1998/namespace</literal>, as they are already bouned to standard XML declarations. + </para> + </listitem> + <listitem> + <para> + The value of a <replaceable>regular-nsuri</replaceable> cannot be a zero-length string. + </para> + </listitem> + </itemizedlist> + </para> <para> @@ -14518,6 +14553,24 @@ SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent'); xmlelement ------------------------------------- <foo bar="2007-01-26">content</foo> + +SELECT xmlelement(NAME "foo:root", xmlnamespaces('http:/foo.bar/' AS foo), 'content'); + + xmlelement +--------------------------------------------------------- + <foo:root xmlns:foo="http:/foo.bar/">content</foo:root> + + SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/foo.bar/'), 'content'); + + xmlelement +--------------------------------------------- + <root xmlns="http:/foo.bar/">content</root> + + SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT), 'content'); + + xmlelement +------------------------------- + <root xmlns="">content</root> ]]></screen> </para> diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 67eb96396a..e0020f88bc 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -185,7 +185,7 @@ static Node *makeNotExpr(Node *expr, int location); static Node *makeAArrayExpr(List *elements, int location); static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location); -static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, +static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *xmlnamespaces, List *args, int location); static List *mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner); static TypeName *TableFuncTypeName(List *columns); @@ -610,7 +610,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <list> xmltable_column_list xmltable_column_option_list %type <node> xmltable_column_el %type <defelt> xmltable_column_option_el -%type <list> xml_namespace_list +%type <list> xml_namespace_list xml_namespaces %type <target> xml_namespace_el %type <node> func_application func_expr_common_subexpr @@ -14210,6 +14210,15 @@ xml_namespace_el: $$->val = $2; $$->location = @1; } + | NO DEFAULT + { + $$ = makeNode(ResTarget); + $$->name = NULL; + $$->indirection = NIL; + $$->val = makeStringConst("", @1);; + $$->location = @1; + } + ; json_table: @@ -15263,12 +15272,12 @@ a_expr: c_expr { $$ = $1; } } | a_expr IS DOCUMENT_P %prec IS { - $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, + $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL, list_make1($1), @2); } | a_expr IS NOT DOCUMENT_P %prec IS { - $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, + $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL, list_make1($1), @2), @2); } @@ -15410,12 +15419,12 @@ b_expr: c_expr } | b_expr IS DOCUMENT_P %prec IS { - $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, + $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL, list_make1($1), @2); } | b_expr IS NOT DOCUMENT_P %prec IS { - $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, + $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, NIL, list_make1($1), @2), @2); } @@ -15950,23 +15959,47 @@ func_expr_common_subexpr: } | XMLCONCAT '(' expr_list ')' { - $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1); + $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, NIL, $3, @1); } | XMLELEMENT '(' NAME_P ColLabel ')' { - $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1); + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, NIL, @1); } | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ')' { - $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, @1); + $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, NIL, @1); } | XMLELEMENT '(' NAME_P ColLabel ',' expr_list ')' { - $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, @1); + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, $6, @1); } | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' expr_list ')' { - $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1); + $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, $8, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' expr_list ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, $8, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, NIL, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' xml_namespaces ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, NIL, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' xml_attributes ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, $8, $6, NIL, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' xml_namespaces ',' expr_list ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, $10, @1); + } + | XMLELEMENT '(' NAME_P ColLabel ',' xml_namespaces ',' xml_attributes ',' expr_list ')' + { + $$ = makeXmlExpr(IS_XMLELEMENT, $4, $8, $6, $10, @1); } | XMLEXISTS '(' c_expr xmlexists_argument ')' { @@ -15979,12 +16012,12 @@ func_expr_common_subexpr: } | XMLFOREST '(' xml_attribute_list ')' { - $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1); + $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, NIL, @1); } | XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')' { XmlExpr *x = (XmlExpr *) - makeXmlExpr(IS_XMLPARSE, NULL, NIL, + makeXmlExpr(IS_XMLPARSE, NULL, NIL, NIL, list_make2($4, makeBoolAConst($5, -1)), @1); @@ -15993,15 +16026,15 @@ func_expr_common_subexpr: } | XMLPI '(' NAME_P ColLabel ')' { - $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, @1); + $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, NIL, @1); } | XMLPI '(' NAME_P ColLabel ',' a_expr ')' { - $$ = makeXmlExpr(IS_XMLPI, $4, NULL, list_make1($6), @1); + $$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, list_make1($6), @1); } | XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')' { - $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, + $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, NIL, list_make3($3, $5, $6), @1); } | XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename xml_indent_option ')' @@ -16205,6 +16238,9 @@ opt_xml_root_standalone: ',' STANDALONE_P YES_P xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; } ; +xml_namespaces: XMLNAMESPACES '(' xml_namespace_list ')' { $$ = $3; } + ; + xml_attribute_list: xml_attribute_el { $$ = list_make1($1); } | xml_attribute_list ',' xml_attribute_el { $$ = lappend($1, $3); } ; @@ -19211,8 +19247,8 @@ makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location) } static Node * -makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args, - int location) +makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *xmlnamespaces, + List *args, int location) { XmlExpr *x = makeNode(XmlExpr); @@ -19223,6 +19259,7 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args, * expression and name lists in transformXmlExpr(). */ x->named_args = named_args; + x->xmlnamespaces = xmlnamespaces; x->arg_names = NIL; x->args = args; /* xmloption, if relevant, must be filled in by caller */ diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index c2806297aa..783184d782 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2347,6 +2347,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) XmlExpr *newx; ListCell *lc; int i; + bool has_default_ns = false; newx = makeNode(XmlExpr); newx->op = x->op; @@ -2467,6 +2468,78 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) i++; } + /* + * this adds the xmlnamespaces into arg_names and named_args + */ + foreach (lc, x->xmlnamespaces) + { + ResTarget *r = lfirst_node(ResTarget, lc); + Node *expr; + StringInfoData nsbuf; + + expr = transformExprRecurse(pstate, r->val); + initStringInfo(&nsbuf); + + if (!r->name) + { + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 2) <XML namespace declaration> shall contain at most one + * <XML default namespace declaration item>. + */ + if (has_default_ns) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("XML elements can have only a single [NO] DEFAULT namespace"), + parser_errposition(pstate, r->location))); + else + appendStringInfo(&nsbuf, NAMESPACE_XMLNS_DEFAULT_PREFIX); + + has_default_ns = true; + } + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 5) No <XML namespace prefix> shall be equivalent to + * "xml" or "xmlns". + */ + else if (strcmp(r->name, NAMESPACE_XMLNS_DEFAULT_PREFIX) == 0 || + strcmp(r->name, NAMESPACE_XML_DEFAULT_PREFIX) == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid XML namespace prefix \"%s\"", r->name), + errdetail("this prefix is already bounded to a standard namespace URI"), + parser_errposition(pstate, r->location))); + else if (r->name) + appendStringInfo(&nsbuf, "%s:%s", NAMESPACE_XMLNS_DEFAULT_PREFIX, + map_sql_identifier_to_xml_name(r->name, false, false)); + else if (IsA(r->val, ColumnRef)) + appendStringInfo(&nsbuf, "%s:%s", NAMESPACE_XMLNS_DEFAULT_PREFIX, + map_sql_identifier_to_xml_name(FigureColname(r->val), true, false)); + + /* reject duplicate argnames in XMLELEMENT only */ + if (x->op == IS_XMLELEMENT) + { + ListCell *lc2; + + foreach (lc2, newx->arg_names) + { + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 4) No two <XML namespace prefix>es shall be equivalent. + */ + if (strcmp(nsbuf.data, strVal(lfirst(lc2))) == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("XML namespace \"%s\" appears more than once", + nsbuf.data), + parser_errposition(pstate, r->location))); + } + } + + newx->named_args = lappend(newx->named_args, expr); + newx->arg_names = lappend(newx->arg_names, makeString(nsbuf.data)); + } + return (Node *) newx; } diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 0898cb1be4..70fec22e21 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -244,7 +244,6 @@ const TableFuncRoutine XmlTableRoutine = #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance" #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml" - #ifdef USE_LIBXML static int @@ -931,6 +930,37 @@ xmlelement(XmlExpr *xexpr, char *str = (char *) lfirst(arg); char *argname = strVal(lfirst(narg)); + StringInfoData xmlns_prefix; + initStringInfo(&xmlns_prefix); + appendStringInfo(&xmlns_prefix, "%s:", NAMESPACE_XMLNS_DEFAULT_PREFIX); + + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 6) No <XML namespace URI> shall be identical, as defined + * in XML Namespaces, to http://www.w3.org/2000/xmlns/ or to + * http://www.w3.org/XML/1998/namespace + */ + if (str && ((strncmp(argname, xmlns_prefix.data, xmlns_prefix.len) == 0 || + strcmp(argname, NAMESPACE_XMLNS_DEFAULT_PREFIX) == 0) && + (strcmp(str, NAMESPACE_XMLNS) == 0 || strcmp(str, NAMESPACE_XML) == 0))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid XML namespace URI \"%s\"", str), + errdetail("this URI is already bounded to standard a namespace prefix"))); + + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 7) The value of an <XML namespace URI> contained in an + * <XML regular namespace declaration item> shall not be a zero-length string. + */ + if (strncmp(argname, xmlns_prefix.data, 6) == 0 && (str && strlen(str) == 0)) + ereport(ERROR, + (errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING), + errmsg("invalid XML namespace URI for \"%s\"", argname), + errdetail("a regular XML namespace cannot be a zero-length string"))); + + pfree(xmlns_prefix.data); + if (str) xmlTextWriterWriteAttribute(writer, (xmlChar *) argname, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index b0ef1952e8..2e5ad905a0 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1578,7 +1578,7 @@ typedef struct SQLValueFunction typedef enum XmlExprOp { IS_XMLCONCAT, /* XMLCONCAT(args) */ - IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, args) */ + IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, xml_namespaces, args) */ IS_XMLFOREST, /* XMLFOREST(xml_attributes) */ IS_XMLPARSE, /* XMLPARSE(text, is_doc, preserve_ws) */ IS_XMLPI, /* XMLPI(name [, args]) */ @@ -1602,6 +1602,8 @@ typedef struct XmlExpr char *name pg_node_attr(query_jumble_ignore); /* non-XML expressions for xml_attributes */ List *named_args; + /* non-XML expressions for XMLNAMESPACES */ + List *xmlnamespaces; /* parallel list of String values */ List *arg_names pg_node_attr(query_jumble_ignore); /* list of expressions */ diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index ed20e21375..4cd365d414 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -59,6 +59,12 @@ XmlPGetDatum(const xmltype *X) return PointerGetDatum(X); } +/* reserved prefixes and URIs for XMLNamespace() from SQL/XML:2023, 11.2 */ +#define NAMESPACE_XMLNS "http://www.w3.org/2000/xmlns/" +#define NAMESPACE_XML "http://www.w3.org/XML/1998/namespace" +#define NAMESPACE_XMLNS_DEFAULT_PREFIX "xmlns" +#define NAMESPACE_XML_DEFAULT_PREFIX "xml" + #define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n)) #define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index fb5f345855..c9be4a3d72 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -225,6 +225,211 @@ SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as fun <foo funny="<>&"'" 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