I've attached v6, which addresses two issues: * Fixes a bug where XMLNAMESPACES declarations within a view were being serialized as XMLATTRIBUTES. * Prevents the makeString function from being called with a NULL parameter - discussed in this thread [1].
Best regards, Jim [1] https://www.postgresql.org/message-id/CAFj8pRC24FEBNfTUrDgAK8f2nqDVvzWCuq%3DR%3DT19nUjL9GuLBA%40mail.gmail.com
From 93ea0e1b07763bca7ffb1d4cf88cc380640604cb Mon Sep 17 00:00:00 2001 From: Jim Jones <jim.jo...@uni-muenster.de> Date: Thu, 20 Feb 2025 09:58:34 +0100 Subject: [PATCH v6] Add XMLNamespaces option to XMLElement This patch adds the scoped option XMLNamespaces to XMLElement, as described in ISO/IEC 9075-14:2023, 11.2 XML lexically scoped options: xmlnamespaces(uri AS prefix, ...) xmlnamespaces(DEFAULT uri, ...) xmlnamespaces(NO DEFAULT, ...) * prefix: Namespace's prefix. * uri: Namespace's URI. * DEFAULT prefix: Specifies the DEFAULT namespace to use within the scope of a namespace declaration. * NO DEFAULT: Specifies that NO DEFAULT namespace is to be used within the scope of a namespace declaration. == Examples == SELECT xmlelement(NAME "foo", xmlnamespaces('http:/x.y' AS bar)); xmlelement ------------------------------ <foo xmlns:bar="http:/x.y"/> SELECT xmlelement(NAME "foo", xmlnamespaces(DEFAULT 'http:/x.y')); xmlelement -------------------------- <foo xmlns="http:/x.y"/> SELECT xmlelement(NAME "foo", xmlnamespaces(NO DEFAULT)); xmlelement ----------------- --- doc/src/sgml/func.sgml | 57 +++++- src/backend/parser/gram.y | 100 +++++++++-- src/backend/parser/parse_clause.c | 7 +- src/backend/parser/parse_expr.c | 80 +++++++++ src/backend/utils/adt/ruleutils.c | 79 +++++++-- src/backend/utils/adt/xml.c | 53 +++++- src/include/nodes/primnodes.h | 4 +- src/include/utils/xml.h | 6 + src/test/regress/expected/xml.out | 259 ++++++++++++++++++++++++++++ src/test/regress/expected/xml_1.out | 197 +++++++++++++++++++++ src/test/regress/expected/xml_2.out | 259 ++++++++++++++++++++++++++++ src/test/regress/sql/xml.sql | 133 ++++++++++++++ 12 files changed, 1200 insertions(+), 34 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index df32ee0bf5..3a59c0d8c2 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -14538,7 +14538,10 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone= </indexterm> <synopsis> -<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> <optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue> +<function>xmlelement</function> ( <literal>NAME</literal> <replaceable>name</replaceable> + <optional>, <literal>XMLATTRIBUTES</literal> ( <replaceable>attvalue</replaceable> <optional> <literal>AS</literal> <replaceable>attname</replaceable> </optional> <optional>, ...</optional> ) </optional> + <optional>, <literal>XMLNAMESPACES</literal> ( {<replaceable>regular-nsuri</replaceable> <literal>AS</literal> <replaceable>nsprefix</replaceable> | DEFAULT <replaceable>default-nsuri</replaceable> | NO DEFAULT} <optional>, ...</optional> ) </optional> +<optional>, <replaceable>content</replaceable> <optional>, ...</optional></optional> ) <returnvalue>xml</returnvalue> </synopsis> <para> @@ -14551,7 +14554,39 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone= yield any <productname>PostgreSQL</productname> data type. The argument(s) within <literal>XMLATTRIBUTES</literal> generate attributes of the XML element; the <replaceable>content</replaceable> value(s) are - concatenated to form its content. + concatenated to form its content. The arguments within <literal>XMLNAMESPACES</literal> + constuct namespace declarations from values provided in <replaceable>nsuri</replaceable> + and <replaceable>nsprefix</replaceable>, which correspond to the URI of a namespace and + its prefix, respectively. The option <literal>DEFAULT</literal> can be used to set the + default namespace declaration (without a prefix) to the URI provided in <replaceable>default-nsuri</replaceable>. + The option <literal>NO DEFAULT</literal> states that a namespace scope has no default namespace. A valid + <literal>XMLNAMESPACES</literal> item must fulfill the following conditions: + + <itemizedlist> + <listitem> + <para> + Only a single <literal>DEFAULT</literal> declaration item within the same scope. + </para> + </listitem> + <listitem> + <para> + No two <replaceable>nsuri</replaceable> can be equal within the same scope. + </para> + </listitem> + <listitem> + <para> + No <replaceable>nsprefix</replaceable> can be equal to <literal>xml</literal> or <literal>xmlns</literal>, + and no <replaceable>nsuri</replaceable> can be equal to <literal>http://www.w3.org/2000/xmlns/</literal> + or to <literal>http://www.w3.org/XML/1998/namespace</literal>, as they are already bouned to standard XML declarations. + </para> + </listitem> + <listitem> + <para> + The value of a <replaceable>regular-nsuri</replaceable> cannot be a zero-length string. + </para> + </listitem> + </itemizedlist> + </para> <para> @@ -14574,6 +14609,24 @@ SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent'); xmlelement ------------------------------------- <foo bar="2007-01-26">content</foo> + +SELECT xmlelement(NAME "foo:root", xmlnamespaces('http:/foo.bar/' AS foo), 'content'); + + xmlelement +--------------------------------------------------------- + <foo:root xmlns:foo="http:/foo.bar/">content</foo:root> + + SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/foo.bar/'), 'content'); + + xmlelement +--------------------------------------------- + <root xmlns="http:/foo.bar/">content</root> + + SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT), 'content'); + + xmlelement +------------------------------- + <root xmlns="">content</root> ]]></screen> </para> diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 7d99c9355c..399f12b86c 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -136,6 +136,12 @@ typedef struct KeyActions KeyAction *deleteAction; } KeyActions; +typedef struct XmlElementOpts +{ + List *xml_attributes; + List *xml_namespaces; +} XmlElementOpts; + /* ConstraintAttributeSpec yields an integer bitmask of these flags: */ #define CAS_NOT_DEFERRABLE 0x01 #define CAS_DEFERRABLE 0x02 @@ -187,7 +193,7 @@ static Node *makeNotExpr(Node *expr, int location); static Node *makeAArrayExpr(List *elements, int location); static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location); -static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, +static Node *makeXmlExpr(XmlExprOp op, char *name, XmlElementOpts *opts, List *args, int location); static List *mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner); static TypeName *TableFuncTypeName(List *columns); @@ -267,6 +273,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); MergeWhenClause *mergewhen; struct KeyActions *keyactions; struct KeyAction *keyaction; + struct XmlElementOpts *xmlelementopts; ReturningClause *retclause; ReturningOptionKind retoptionkind; } @@ -618,8 +625,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <list> xmltable_column_list xmltable_column_option_list %type <node> xmltable_column_el %type <defelt> xmltable_column_option_el -%type <list> xml_namespace_list +%type <list> xml_namespace_list xml_namespaces %type <target> xml_namespace_el +%type <xmlelementopts> xmlelement_opts %type <node> func_application func_expr_common_subexpr %type <node> func_expr func_expr_windowless @@ -14285,6 +14293,15 @@ xml_namespace_el: $$->val = $2; $$->location = @1; } + | NO DEFAULT + { + $$ = makeNode(ResTarget); + $$->name = NULL; + $$->indirection = NIL; + $$->val = NULL; + $$->location = @1; + } + ; json_table: @@ -15338,12 +15355,12 @@ a_expr: c_expr { $$ = $1; } } | a_expr IS DOCUMENT_P %prec IS { - $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, + $$ = makeXmlExpr(IS_DOCUMENT, NULL, NULL, list_make1($1), @2); } | a_expr IS NOT DOCUMENT_P %prec IS { - $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, + $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NULL, list_make1($1), @2), @2); } @@ -15485,12 +15502,12 @@ b_expr: c_expr } | b_expr IS DOCUMENT_P %prec IS { - $$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL, + $$ = makeXmlExpr(IS_DOCUMENT, NULL, NULL, list_make1($1), @2); } | b_expr IS NOT DOCUMENT_P %prec IS { - $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL, + $$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NULL, list_make1($1), @2), @2); } @@ -16025,21 +16042,21 @@ func_expr_common_subexpr: } | XMLCONCAT '(' expr_list ')' { - $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1); + $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NULL, $3, @1); } | XMLELEMENT '(' NAME_P ColLabel ')' { - $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1); + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NULL, NIL, @1); } - | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ')' + | XMLELEMENT '(' NAME_P ColLabel ',' xmlelement_opts ')' { $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, @1); } | XMLELEMENT '(' NAME_P ColLabel ',' expr_list ')' { - $$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, @1); + $$ = makeXmlExpr(IS_XMLELEMENT, $4, NULL, $6, @1); } - | XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' expr_list ')' + | XMLELEMENT '(' NAME_P ColLabel ',' xmlelement_opts ',' expr_list ')' { $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1); } @@ -16054,12 +16071,17 @@ func_expr_common_subexpr: } | XMLFOREST '(' xml_attribute_list ')' { - $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1); + XmlElementOpts opts; + + opts.xml_attributes = $3; + opts.xml_namespaces = NIL; + + $$ = makeXmlExpr(IS_XMLFOREST, NULL, &opts, NIL, @1); } | XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')' { XmlExpr *x = (XmlExpr *) - makeXmlExpr(IS_XMLPARSE, NULL, NIL, + makeXmlExpr(IS_XMLPARSE, NULL, NULL, list_make2($4, makeBoolAConst($5, -1)), @1); @@ -16076,7 +16098,7 @@ func_expr_common_subexpr: } | XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')' { - $$ = makeXmlExpr(IS_XMLROOT, NULL, NIL, + $$ = makeXmlExpr(IS_XMLROOT, NULL, NULL, list_make3($3, $5, $6), @1); } | XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename xml_indent_option ')' @@ -16280,6 +16302,9 @@ opt_xml_root_standalone: ',' STANDALONE_P YES_P xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; } ; +xml_namespaces: XMLNAMESPACES '(' xml_namespace_list ')' { $$ = $3; } + ; + xml_attribute_list: xml_attribute_el { $$ = list_make1($1); } | xml_attribute_list ',' xml_attribute_el { $$ = lappend($1, $3); } ; @@ -16316,6 +16341,44 @@ xml_whitespace_option: PRESERVE WHITESPACE_P { $$ = true; } | /*EMPTY*/ { $$ = false; } ; +xmlelement_opts: xml_attributes + { + XmlElementOpts *n = palloc(sizeof(XmlElementOpts)); + + n->xml_attributes = $1; + n->xml_namespaces = NIL; + $$ = n; + } + | xml_namespaces + { + XmlElementOpts *n = palloc(sizeof(XmlElementOpts)); + + n->xml_attributes = NIL; + n->xml_namespaces = $1; + $$ = n; + } + | xmlelement_opts ',' xml_attributes + { + if ($$->xml_attributes) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("duplicate XMLATTRIBUTES specified"), + parser_errposition(@3))); + + $$->xml_attributes = $3; + } + | xmlelement_opts ',' xml_namespaces + { + if ($$->xml_namespaces) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("duplicate XMLNAMESACES specified"), + parser_errposition(@3))); + + $$->xml_namespaces = $3; + } + ; + /* We allow several variants for SQL and other compatibility. */ xmlexists_argument: PASSING c_expr @@ -19290,7 +19353,7 @@ makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location) } static Node * -makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args, +makeXmlExpr(XmlExprOp op, char *name, XmlElementOpts *opts, List *args, int location) { XmlExpr *x = makeNode(XmlExpr); @@ -19302,7 +19365,12 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args, * named_args is a list of ResTarget; it'll be split apart into separate * expression and name lists in transformXmlExpr(). */ - x->named_args = named_args; + if (opts) + { + x->named_args = opts->xml_attributes; + x->xmlnamespaces = opts->xml_namespaces; + } + x->arg_names = NIL; x->args = args; /* xmloption, if relevant, must be filled in by caller */ diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 2e64fcae7b..a4c04fa886 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -842,7 +842,12 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf) Node *ns_uri; Assert(IsA(r, ResTarget)); - ns_uri = transformExpr(pstate, r->val, EXPR_KIND_FROM_FUNCTION); + /* Create an empty String for NO DEFAULT namespaces */ + if (!r->name && !r->val) + ns_uri = transformExpr(pstate, makeStringConst("", r->location), EXPR_KIND_FROM_FUNCTION); + else + ns_uri = transformExpr(pstate, r->val, EXPR_KIND_FROM_FUNCTION); + ns_uri = coerce_to_specific_type(pstate, ns_uri, TEXTOID, constructName); assign_expr_collations(pstate, ns_uri); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index bad1df732e..93eeabdb9c 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2347,6 +2347,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) XmlExpr *newx; ListCell *lc; int i; + bool has_default_xmlns = false; newx = makeNode(XmlExpr); newx->op = x->op; @@ -2366,6 +2367,80 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) newx->named_args = NIL; newx->arg_names = NIL; + /* + * this adds the xmlnamespaces into arg_names and named_args + */ + foreach (lc, x->xmlnamespaces) + { + ResTarget *r = lfirst_node(ResTarget, lc); + Node *expr; + ListCell *lc2; + char *argname = NULL; + + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 2) <XML namespace declaration> shall contain at most one + * <XML default namespace declaration item>. + */ + if (!r->name) + { + if (has_default_xmlns) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("XML elements can have only a single [NO] DEFAULT namespace"), + parser_errposition(pstate, r->location))); + + has_default_xmlns = true; + } + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 5) No <XML namespace prefix> shall be equivalent to + * "xml" or "xmlns". + */ + else if (strcmp(r->name, NAMESPACE_XMLNS_DEFAULT_PREFIX) == 0 || + strcmp(r->name, NAMESPACE_XML_DEFAULT_PREFIX) == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid XML namespace prefix \"%s\"", r->name), + errdetail("this prefix is already bounded to a standard namespace URI"), + parser_errposition(pstate, r->location))); + else if (r->name) + argname = map_sql_identifier_to_xml_name(r->name, false, false); + + else if (IsA(r->val, ColumnRef)) + argname = map_sql_identifier_to_xml_name(FigureColname(r->val), + true, false); + + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 4) No two <XML namespace prefix>es shall be equivalent. + */ + if (x->op == IS_XMLELEMENT && argname) + { + foreach(lc2, newx->arg_names) + { + if (!strVal(lfirst(lc2))) + continue; + + if (strVal(lfirst(lc2)) && strcmp(argname, strVal(lfirst(lc2))) == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("XML namespace name \"%s\" appears more than once", + argname), + parser_errposition(pstate, r->location))); + } + } + + if(r->val) + expr = transformExprRecurse(pstate, r->val); + else + expr = transformExprRecurse(pstate, makeStringConst("", newx->location)); + + newx->named_args = lappend(newx->named_args, expr); + newx->arg_names = lappend(newx->arg_names, makeString(!argname ? "" : argname)); + newx->xmlnamespaces = lappend(newx->xmlnamespaces, makeString(NAMESPACE_XMLNS_DEFAULT_PREFIX)); + } + foreach(lc, x->named_args) { ResTarget *r = lfirst_node(ResTarget, lc); @@ -2397,6 +2472,10 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) foreach(lc2, newx->arg_names) { + + if (!strVal(lfirst(lc2))) + continue; + if (strcmp(argname, strVal(lfirst(lc2))) == 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -2408,6 +2487,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) newx->named_args = lappend(newx->named_args, expr); newx->arg_names = lappend(newx->arg_names, makeString(argname)); + newx->xmlnamespaces = lappend(newx->xmlnamespaces, makeString("")); } /* The other arguments are of varying types depending on the function */ diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 54dad97555..2a05ea8648 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -10004,6 +10004,7 @@ get_rule_expr(Node *node, deparse_context *context, bool needcomma = false; ListCell *arg; ListCell *narg; + ListCell *nsarg; Const *con; switch (xexpr->op) @@ -10047,26 +10048,84 @@ get_rule_expr(Node *node, deparse_context *context, } if (xexpr->named_args) { - if (xexpr->op != IS_XMLFOREST) + bool hasFunctCall = false; + + forthree(arg, xexpr->named_args, narg, xexpr->arg_names, nsarg, xexpr->xmlnamespaces) { + Node *e = (Node *)lfirst(arg); + char *argname = strVal(lfirst(narg)); + char *prefix = strVal(lfirst(nsarg)); + + /* we skip this entry, as it is not a XMLATTRIBUTES argument */ + if (strlen(prefix) != 0) + continue; + + if (!hasFunctCall) + { + if (xexpr->op != IS_XMLFOREST) + { + if (needcomma) + appendStringInfoString(buf, ", "); + appendStringInfoString(buf, "XMLATTRIBUTES("); + needcomma = false; + } + + hasFunctCall = true; + } + if (needcomma) appendStringInfoString(buf, ", "); - appendStringInfoString(buf, "XMLATTRIBUTES("); - needcomma = false; + + get_rule_expr((Node *)e, context, true); + appendStringInfo(buf, " AS %s", + quote_identifier(map_xml_name_to_sql_identifier(argname))); + needcomma = true; } - forboth(arg, xexpr->named_args, narg, xexpr->arg_names) + if (xexpr->op != IS_XMLFOREST && hasFunctCall) + appendStringInfoChar(buf, ')'); + + hasFunctCall = false; + + forthree(arg, xexpr->named_args, narg, xexpr->arg_names, nsarg, xexpr->xmlnamespaces) { - Node *e = (Node *) lfirst(arg); - char *argname = strVal(lfirst(narg)); + Node *e = (Node *)lfirst(arg); + char *argname = strVal(lfirst(narg)); + char *prefix = strVal(lfirst(nsarg)); + + /* we skip this entry, as it is not a XMLNAMESPACES argument */ + if (strlen(prefix) == 0) + continue; + + if (!hasFunctCall) + { + if (xexpr->op != IS_XMLFOREST) + { + if (needcomma) + appendStringInfoString(buf, ", "); + appendStringInfoString(buf, "XMLNAMESPACES("); + needcomma = false; + } + + hasFunctCall = true; + } if (needcomma) appendStringInfoString(buf, ", "); - get_rule_expr((Node *) e, context, true); - appendStringInfo(buf, " AS %s", - quote_identifier(map_xml_name_to_sql_identifier(argname))); + + if (strlen(argname) == 0) + { + appendStringInfo(buf, "DEFAULT "); + get_rule_expr((Node *)e, context, true); + } + else + { + get_rule_expr((Node *)e, context, true); + appendStringInfo(buf, " AS %s", + quote_identifier(map_xml_name_to_sql_identifier(argname))); + } needcomma = true; } - if (xexpr->op != IS_XMLFOREST) + if (xexpr->op != IS_XMLFOREST && hasFunctCall) appendStringInfoChar(buf, ')'); } if (xexpr->args) diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index db8d0d6a7e..2cdb85cce7 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -244,7 +244,6 @@ const TableFuncRoutine XmlTableRoutine = #define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance" #define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml" - #ifdef USE_LIBXML static int @@ -865,6 +864,7 @@ xmlelement(XmlExpr *xexpr, int i; ListCell *arg; ListCell *narg; + ListCell *nsarg; PgXmlErrorContext *xmlerrcxt; volatile xmlBufferPtr buf = NULL; volatile xmlTextWriterPtr writer = NULL; @@ -926,12 +926,53 @@ xmlelement(XmlExpr *xexpr, xmlTextWriterStartElement(writer, (xmlChar *) xexpr->name); - forboth(arg, named_arg_strings, narg, xexpr->arg_names) + forthree(arg, named_arg_strings, narg, xexpr->arg_names, nsarg, xexpr->xmlnamespaces) { char *str = (char *) lfirst(arg); char *argname = strVal(lfirst(narg)); + char *prefix = strVal(lfirst(nsarg)); - if (str) + if (str && strlen(prefix) != 0) + { + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 6) No <XML namespace URI> shall be identical, as defined + * in XML Namespaces, to http://www.w3.org/2000/xmlns/ or to + * http://www.w3.org/XML/1998/namespace + */ + if (strcmp(str, NAMESPACE_XMLNS) == 0 || strcmp(str, NAMESPACE_XML) == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid XML namespace URI \"%s\"", str), + errdetail("this URI is already bounded to standard a namespace prefix"))); + + /* + * SQL/XML:2023 - 11.2 <XML lexically scoped options> + * Syntax Rule 7) The value of an <XML namespace URI> contained in an + * <XML regular namespace declaration item> shall not be a zero-length string. + */ + if (strlen(argname) != 0 && strlen(str) == 0) + ereport(ERROR, + (errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING), + errmsg("invalid XML namespace URI for \"%s\"", argname), + errdetail("a regular XML namespace cannot be a zero-length string"))); + + /* + * xmlTextWriterWriteAttributeNS + * prefix - Namespace prefix for the attribute. Pass NULL for no prefix, + * which means DEFAULT namespace. + * name - Local name of the attribute (without prefix). This is the + * actual attribute name. + * namespaceURI - Namespace URI associated with the prefix (NULL for none). + * content - Value of the attribute. + */ + xmlTextWriterWriteAttributeNS(writer, + strlen(argname) == 0 ? NULL : (const xmlChar *) prefix, + strlen(argname) != 0 ? (const xmlChar *) argname : (const xmlChar *) prefix, + NULL, + (const xmlChar *) str); + } + else if (str) xmlTextWriterWriteAttribute(writer, (xmlChar *) argname, (xmlChar *) str); @@ -4789,7 +4830,11 @@ XmlTableSetNamespace(TableFuncScanState *state, const char *name, const char *ur #ifdef USE_LIBXML XmlTableBuilderData *xtCxt; - if (name == NULL) + if (name == NULL && strlen(uri) == 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("NO DEFAULT namespace is not supported"))); + else if (name == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("DEFAULT namespace is not supported"))); diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 839e71d52f..287e4bc749 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1583,7 +1583,7 @@ typedef struct SQLValueFunction typedef enum XmlExprOp { IS_XMLCONCAT, /* XMLCONCAT(args) */ - IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, args) */ + IS_XMLELEMENT, /* XMLELEMENT(name, xml_attributes, xml_namespaces, args) */ IS_XMLFOREST, /* XMLFOREST(xml_attributes) */ IS_XMLPARSE, /* XMLPARSE(text, is_doc, preserve_ws) */ IS_XMLPI, /* XMLPI(name [, args]) */ @@ -1607,6 +1607,8 @@ typedef struct XmlExpr char *name pg_node_attr(query_jumble_ignore); /* non-XML expressions for xml_attributes */ List *named_args; + /* non-XML expressions for XMLNAMESPACES */ + List *xmlnamespaces; /* parallel list of String values */ List *arg_names pg_node_attr(query_jumble_ignore); /* list of expressions */ diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index 0d7a816b9f..4a714dbca9 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -59,6 +59,12 @@ XmlPGetDatum(const xmltype *X) return PointerGetDatum(X); } +/* reserved prefixes and URIs for XMLNamespace() from SQL/XML:2023, 11.2 */ +#define NAMESPACE_XMLNS "http://www.w3.org/2000/xmlns/" +#define NAMESPACE_XML "http://www.w3.org/XML/1998/namespace" +#define NAMESPACE_XMLNS_DEFAULT_PREFIX "xmlns" +#define NAMESPACE_XML_DEFAULT_PREFIX "xml" + #define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n)) #define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x) diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 2e9616acda..4b300eba7e 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -225,6 +225,260 @@ SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as fun <foo funny="<>&"'" funnier="b<a/>r"/> (1 row) +-- DEFAULT NULL xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL)); + xmlelement +------------ + <root/> +(1 row) + +-- DEFAULT xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1')); + xmlelement +------------------------------- + <root xmlns="http:/x.y/ns1"/> +(1 row) + +-- DEFAULT numeric xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73)); + xmlelement +----------------------- + <root xmlns="42.73"/> +(1 row) + +-- DEFAULT empty xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT '')); + xmlelement +------------------ + <root xmlns=""/> +(1 row) + +-- DEFAULT empty xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT)); + xmlelement +------------------ + <root xmlns=""/> +(1 row) + +-- Testing xmlnamespace with subqueries +SELECT + xmlelement(NAME root, xmlnamespaces(DEFAULT 'ns'), + (SELECT xmlelement(NAME child1, xmlnamespaces(NO DEFAULT)))); + xmlelement +-------------------------------------------- + <root xmlns="ns"><child1 xmlns=""/></root> +(1 row) + +-- Testing xmlnamespace url from ColumnRef +CREATE TABLE xmlns (url text); +INSERT INTO xmlns VALUES ('http:/x.y/ns1'); +SELECT + xmlserialize(DOCUMENT + xmlelement(NAME root, xmlnamespaces(DEFAULT 'foo', url AS ns), + xmlelement(NAME root, + xmlelement(NAME child2, xmlnamespaces(url AS ns, NO DEFAULT)))) + AS text INDENT) +FROM xmlns; + xmlserialize +------------------------------------------------- + <root xmlns="foo" xmlns:ns="http:/x.y/ns1"> + + <root> + + <child2 xmlns:ns="http:/x.y/ns1" xmlns=""/>+ + </root> + + </root> +(1 row) + +-- NULL xmlnamespace uri +SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns)); + xmlelement +------------ + <root/> +(1 row) + +-- empty xmlelement containing one namespace +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1)); + xmlelement +----------------------------------- + <root xmlns:ns1="http:/x.y/ns1"/> +(1 row) + +-- empty xmlelement with DEFAULT and regular xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2)); + xmlelement +--------------------------------------------------------- + <root xmlns="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/> +(1 row) + +-- xmlelement containing a namespace and a text node +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node'); + xmlelement +-------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1">text node</root> +(1 row) + +-- empty xmlelement containing a) xmlnamespace and b) xmlattribute +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att)); + xmlelement +--------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" att="val"/> +(1 row) + +-- empty xmlelement containing a) xmlattribute and b) xmlnamespace +SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1)); + xmlelement +--------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" att="val"/> +(1 row) + +-- empty xmlelement containing two xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2)); + xmlelement +------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/> +(1 row) + +-- empty xmlelement containing two xmlnamespaces and one xmlattribute +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att)); + xmlelement +----------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2" att="val"/> +(1 row) + +-- xmlelement containing two xmlnamespaces, one xmlattribute, +-- and a text node. +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node'); + xmlelement +-------------------------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2" att="val">text node</root> +(1 row) + +-- empty root xmlelement containing one xmlnamespace, one xmlattribute, +-- and one xmlelement child (ns1:foo). +-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one +-- xmlelement child (ns2:bar). +-- xmlelement child "ns2:bar" containing a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1), + xmlattributes('val' AS att), + xmlelement(NAME "ns1:foo", + xmlnamespaces('http:/x.y/ns2' AS ns2), + xmlelement(NAME "ns2:bar", 'text node')) + ); + xmlelement +---------------------------------------------------------------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" att="val"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar></ns1:foo></root> +(1 row) + +-- empty root xmlelement containing one xmlnamespace, one xmlattribute, +-- and one xmlelement child (ns1:foo). +-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement +-- child (ns2:bar), and a text node. +-- xmlelement child "ns2:bar" containing a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1), + xmlattributes('val' AS att), + xmlelement(NAME "ns1:foo", + xmlnamespaces('http:/x.y/ns2' AS ns2), + xmlelement(NAME "ns2:bar", 'text node'), + 'mixed content') + ); + xmlelement +----------------------------------------------------------------------------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" att="val"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar>mixed content</ns1:foo></root> +(1 row) + +-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute, +-- and one xmlelement child (foo). +-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces(DEFAULT 'http:/x.y/ns1'), + xmlattributes('val' AS att), + xmlelement(NAME "foo", + xmlnamespaces(NO DEFAULT),'bar') + ); + xmlelement +---------------------------------------------------------------------- + <root xmlns="http:/x.y/ns1" att="val"><foo xmlns="">bar</foo></root> +(1 row) + +-- empty root xmlelement containing one xmlattribute, one xmlnamespace, +-- three child nodes generated by xmlforest and xmltext, one xmlcomment, +-- and a text node generated by xmlconcat. +SELECT + xmlelement(NAME "root", + xmlattributes(73 AS att), + xmlnamespaces('http:/x.y/ns1' AS ns), + xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"), + xmlcomment(':)'), + xmlconcat('foo', 'bar') + ); + xmlelement +-------------------------------------------------------------------------------------------------------------------------- + <root xmlns:ns="http:/x.y/ns1" att="73"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z><&></ns:z><!--:)-->foobar</root> +(1 row) + +-- test xmlnamespaces within views +CREATE VIEW view_xmlnamespaces AS +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), xmlattributes('val' AS att), + xmlelement(NAME "child1", xmlnamespaces('http:/x.y/ns2' AS ns2, NO DEFAULT)), + xmlcomment(':)'), + xmlconcat('foo', 'bar'), + xmlelement(NAME "child2", xmlnamespaces(DEFAULT NULL))) AS xmldoc; +SELECT * FROM view_xmlnamespaces; + xmldoc +--------------------------------------------------------------------------------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" xmlns="http:/x.y/def" att="val"><child1 xmlns:ns2="http:/x.y/ns2" xmlns=""/><!--:)-->foobar<child2/></root> +(1 row) + +\sv view_xmlnamespaces +CREATE OR REPLACE VIEW public.view_xmlnamespaces AS + SELECT XMLELEMENT(NAME root, XMLATTRIBUTES('val' AS att), XMLNAMESPACES('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), XMLELEMENT(NAME child1, XMLNAMESPACES('http:/x.y/ns2' AS ns2, DEFAULT '')), xmlcomment(':)'::text), XMLCONCAT('foo'::xml, 'bar'::xml), XMLELEMENT(NAME child2, XMLNAMESPACES(DEFAULT NULL::unknown))) AS xmldoc +\set VERBOSITY terse +-- duplicate xmlnamespace entry (ns1) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1)); +ERROR: XML namespace name "ns1" appears more than once at character 70 +-- invalid xmlnamespace syntax +SELECT xmlelement(NAME "root", xmlnamespaces('invalid')); +ERROR: syntax error at or near ")" at character 55 +-- multiple DEFAULT xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2')); +ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 71 +-- multiple NO DEFAULT xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT)); +ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 58 +-- invalid xmlnamespace prefix (xmlns) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns)); +ERROR: invalid XML namespace prefix "xmlns" at character 46 +-- invalid xmlnamespace prefix (xml) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml)); +ERROR: invalid XML namespace prefix "xml" at character 46 +-- duplicate xmlnamespace calls +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlnamespaces('http:/x.y/ns2' AS ns2)); +ERROR: duplicate XMLNAMESACES specified at character 71 +\set VERBOSITY default +-- invalid xmlnamespaces URIs +SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar)); +ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/" +DETAIL: this URI is already bounded to standard a namespace prefix +SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar)); +ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace" +DETAIL: this URI is already bounded to standard a namespace prefix +-- invalid DEFAULT xmlnamespace URIs +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/')); +ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/" +DETAIL: this URI is already bounded to standard a namespace prefix +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace')); +ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace" +DETAIL: this URI is already bounded to standard a namespace prefix +-- empty xmlnamespace uri +SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns)); +ERROR: invalid XML namespace URI for "ns" +DETAIL: a regular XML namespace cannot be a zero-length string SELECT xmlparse(content ''); xmlparse ---------- @@ -1398,6 +1652,11 @@ SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'), PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>' COLUMNS a int PATH 'a'); ERROR: DEFAULT namespace is not supported +SELECT * FROM XMLTABLE(XMLNAMESPACES(NO DEFAULT), + '/rows/row' + PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>' + COLUMNS a int PATH 'a'); +ERROR: NO DEFAULT namespace is not supported SELECT * FROM XMLTABLE('.' PASSING '<foo/>' COLUMNS a text PATH 'foo/namespace::node()'); diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index 7505a14077..788d5fdb5f 100644 --- a/src/test/regress/expected/xml_1.out +++ b/src/test/regress/expected/xml_1.out @@ -150,6 +150,195 @@ DETAIL: This functionality requires the server to be built with libxml support. SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as funnier)); ERROR: unsupported XML feature DETAIL: This functionality requires the server to be built with libxml support. +-- DEFAULT NULL xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- DEFAULT xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1')); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- DEFAULT numeric xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- DEFAULT empty xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT '')); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- DEFAULT empty xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- Testing xmlnamespace with subqueries +SELECT + xmlelement(NAME root, xmlnamespaces(DEFAULT 'ns'), + (SELECT xmlelement(NAME child1, xmlnamespaces(NO DEFAULT)))); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- Testing xmlnamespace url from ColumnRef +CREATE TABLE xmlns (url text); +INSERT INTO xmlns VALUES ('http:/x.y/ns1'); +SELECT + xmlserialize(DOCUMENT + xmlelement(NAME root, xmlnamespaces(DEFAULT 'foo', url AS ns), + xmlelement(NAME root, + xmlelement(NAME child2, xmlnamespaces(url AS ns, NO DEFAULT)))) + AS text INDENT) +FROM xmlns; +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- NULL xmlnamespace uri +SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty xmlelement containing one namespace +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty xmlelement with DEFAULT and regular xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- xmlelement containing a namespace and a text node +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node'); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty xmlelement containing a) xmlnamespace and b) xmlattribute +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty xmlelement containing a) xmlattribute and b) xmlnamespace +SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty xmlelement containing two xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty xmlelement containing two xmlnamespaces and one xmlattribute +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- xmlelement containing two xmlnamespaces, one xmlattribute, +-- and a text node. +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node'); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty root xmlelement containing one xmlnamespace, one xmlattribute, +-- and one xmlelement child (ns1:foo). +-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one +-- xmlelement child (ns2:bar). +-- xmlelement child "ns2:bar" containing a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1), + xmlattributes('val' AS att), + xmlelement(NAME "ns1:foo", + xmlnamespaces('http:/x.y/ns2' AS ns2), + xmlelement(NAME "ns2:bar", 'text node')) + ); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty root xmlelement containing one xmlnamespace, one xmlattribute, +-- and one xmlelement child (ns1:foo). +-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement +-- child (ns2:bar), and a text node. +-- xmlelement child "ns2:bar" containing a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1), + xmlattributes('val' AS att), + xmlelement(NAME "ns1:foo", + xmlnamespaces('http:/x.y/ns2' AS ns2), + xmlelement(NAME "ns2:bar", 'text node'), + 'mixed content') + ); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute, +-- and one xmlelement child (foo). +-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces(DEFAULT 'http:/x.y/ns1'), + xmlattributes('val' AS att), + xmlelement(NAME "foo", + xmlnamespaces(NO DEFAULT),'bar') + ); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty root xmlelement containing one xmlattribute, one xmlnamespace, +-- three child nodes generated by xmlforest and xmltext, one xmlcomment, +-- and a text node generated by xmlconcat. +SELECT + xmlelement(NAME "root", + xmlattributes(73 AS att), + xmlnamespaces('http:/x.y/ns1' AS ns), + xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"), + xmlcomment(':)'), + xmlconcat('foo', 'bar') + ); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- test xmlnamespaces within views +CREATE VIEW view_xmlnamespaces AS +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), xmlattributes('val' AS att), + xmlelement(NAME "child1", xmlnamespaces('http:/x.y/ns2' AS ns2, NO DEFAULT)), + xmlcomment(':)'), + xmlconcat('foo', 'bar'), + xmlelement(NAME "child2", xmlnamespaces(DEFAULT NULL))) AS xmldoc; +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +SELECT * FROM view_xmlnamespaces; +ERROR: relation "view_xmlnamespaces" does not exist +LINE 1: SELECT * FROM view_xmlnamespaces; + ^ +\sv view_xmlnamespaces +ERROR: relation "view_xmlnamespaces" does not exist +\set VERBOSITY terse +-- duplicate xmlnamespace entry (ns1) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1)); +ERROR: unsupported XML feature +-- invalid xmlnamespace syntax +SELECT xmlelement(NAME "root", xmlnamespaces('invalid')); +ERROR: syntax error at or near ")" at character 55 +-- multiple DEFAULT xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2')); +ERROR: unsupported XML feature +-- multiple NO DEFAULT xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT)); +ERROR: unsupported XML feature +-- invalid xmlnamespace prefix (xmlns) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns)); +ERROR: unsupported XML feature +-- invalid xmlnamespace prefix (xml) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml)); +ERROR: unsupported XML feature +-- duplicate xmlnamespace calls +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlnamespaces('http:/x.y/ns2' AS ns2)); +ERROR: duplicate XMLNAMESACES specified at character 71 +\set VERBOSITY default +-- invalid xmlnamespaces URIs +SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- invalid DEFAULT xmlnamespace URIs +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/')); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace')); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. +-- empty xmlnamespace uri +SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns)); +ERROR: unsupported XML feature +DETAIL: This functionality requires the server to be built with libxml support. SELECT xmlparse(content ''); ERROR: unsupported XML feature DETAIL: This functionality requires the server to be built with libxml support. @@ -1068,6 +1257,14 @@ ERROR: unsupported XML feature LINE 3: PASSING '<rows xmlns="http://x.y"><row... ^ DETAIL: This functionality requires the server to be built with libxml support. +SELECT * FROM XMLTABLE(XMLNAMESPACES(NO DEFAULT), + '/rows/row' + PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>' + COLUMNS a int PATH 'a'); +ERROR: unsupported XML feature +LINE 3: PASSING '<rows xmlns="http://x.y"><row... + ^ +DETAIL: This functionality requires the server to be built with libxml support. SELECT * FROM XMLTABLE('.' PASSING '<foo/>' COLUMNS a text PATH 'foo/namespace::node()'); diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index c07ed2b269..a2abcf89b5 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -221,6 +221,260 @@ SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as fun <foo funny="<>&"'" funnier="b<a/>r"/> (1 row) +-- DEFAULT NULL xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL)); + xmlelement +------------ + <root/> +(1 row) + +-- DEFAULT xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1')); + xmlelement +------------------------------- + <root xmlns="http:/x.y/ns1"/> +(1 row) + +-- DEFAULT numeric xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73)); + xmlelement +----------------------- + <root xmlns="42.73"/> +(1 row) + +-- DEFAULT empty xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT '')); + xmlelement +------------------ + <root xmlns=""/> +(1 row) + +-- DEFAULT empty xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT)); + xmlelement +------------------ + <root xmlns=""/> +(1 row) + +-- Testing xmlnamespace with subqueries +SELECT + xmlelement(NAME root, xmlnamespaces(DEFAULT 'ns'), + (SELECT xmlelement(NAME child1, xmlnamespaces(NO DEFAULT)))); + xmlelement +-------------------------------------------- + <root xmlns="ns"><child1 xmlns=""/></root> +(1 row) + +-- Testing xmlnamespace url from ColumnRef +CREATE TABLE xmlns (url text); +INSERT INTO xmlns VALUES ('http:/x.y/ns1'); +SELECT + xmlserialize(DOCUMENT + xmlelement(NAME root, xmlnamespaces(DEFAULT 'foo', url AS ns), + xmlelement(NAME root, + xmlelement(NAME child2, xmlnamespaces(url AS ns, NO DEFAULT)))) + AS text INDENT) +FROM xmlns; + xmlserialize +------------------------------------------------- + <root xmlns="foo" xmlns:ns="http:/x.y/ns1"> + + <root> + + <child2 xmlns:ns="http:/x.y/ns1" xmlns=""/>+ + </root> + + </root> +(1 row) + +-- NULL xmlnamespace uri +SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns)); + xmlelement +------------ + <root/> +(1 row) + +-- empty xmlelement containing one namespace +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1)); + xmlelement +----------------------------------- + <root xmlns:ns1="http:/x.y/ns1"/> +(1 row) + +-- empty xmlelement with DEFAULT and regular xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2)); + xmlelement +--------------------------------------------------------- + <root xmlns="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/> +(1 row) + +-- xmlelement containing a namespace and a text node +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node'); + xmlelement +-------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1">text node</root> +(1 row) + +-- empty xmlelement containing a) xmlnamespace and b) xmlattribute +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att)); + xmlelement +--------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" att="val"/> +(1 row) + +-- empty xmlelement containing a) xmlattribute and b) xmlnamespace +SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1)); + xmlelement +--------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" att="val"/> +(1 row) + +-- empty xmlelement containing two xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2)); + xmlelement +------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2"/> +(1 row) + +-- empty xmlelement containing two xmlnamespaces and one xmlattribute +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att)); + xmlelement +----------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2" att="val"/> +(1 row) + +-- xmlelement containing two xmlnamespaces, one xmlattribute, +-- and a text node. +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node'); + xmlelement +-------------------------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" xmlns:ns2="http:/x.y/ns2" att="val">text node</root> +(1 row) + +-- empty root xmlelement containing one xmlnamespace, one xmlattribute, +-- and one xmlelement child (ns1:foo). +-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one +-- xmlelement child (ns2:bar). +-- xmlelement child "ns2:bar" containing a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1), + xmlattributes('val' AS att), + xmlelement(NAME "ns1:foo", + xmlnamespaces('http:/x.y/ns2' AS ns2), + xmlelement(NAME "ns2:bar", 'text node')) + ); + xmlelement +---------------------------------------------------------------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" att="val"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar></ns1:foo></root> +(1 row) + +-- empty root xmlelement containing one xmlnamespace, one xmlattribute, +-- and one xmlelement child (ns1:foo). +-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement +-- child (ns2:bar), and a text node. +-- xmlelement child "ns2:bar" containing a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1), + xmlattributes('val' AS att), + xmlelement(NAME "ns1:foo", + xmlnamespaces('http:/x.y/ns2' AS ns2), + xmlelement(NAME "ns2:bar", 'text node'), + 'mixed content') + ); + xmlelement +----------------------------------------------------------------------------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" att="val"><ns1:foo xmlns:ns2="http:/x.y/ns2"><ns2:bar>text node</ns2:bar>mixed content</ns1:foo></root> +(1 row) + +-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute, +-- and one xmlelement child (foo). +-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces(DEFAULT 'http:/x.y/ns1'), + xmlattributes('val' AS att), + xmlelement(NAME "foo", + xmlnamespaces(NO DEFAULT),'bar') + ); + xmlelement +---------------------------------------------------------------------- + <root xmlns="http:/x.y/ns1" att="val"><foo xmlns="">bar</foo></root> +(1 row) + +-- empty root xmlelement containing one xmlattribute, one xmlnamespace, +-- three child nodes generated by xmlforest and xmltext, one xmlcomment, +-- and a text node generated by xmlconcat. +SELECT + xmlelement(NAME "root", + xmlattributes(73 AS att), + xmlnamespaces('http:/x.y/ns1' AS ns), + xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"), + xmlcomment(':)'), + xmlconcat('foo', 'bar') + ); + xmlelement +-------------------------------------------------------------------------------------------------------------------------- + <root xmlns:ns="http:/x.y/ns1" att="73"><ns:x>true</ns:x><ns:y>42</ns:y><ns:z><&></ns:z><!--:)-->foobar</root> +(1 row) + +-- test xmlnamespaces within views +CREATE VIEW view_xmlnamespaces AS +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), xmlattributes('val' AS att), + xmlelement(NAME "child1", xmlnamespaces('http:/x.y/ns2' AS ns2, NO DEFAULT)), + xmlcomment(':)'), + xmlconcat('foo', 'bar'), + xmlelement(NAME "child2", xmlnamespaces(DEFAULT NULL))) AS xmldoc; +SELECT * FROM view_xmlnamespaces; + xmldoc +--------------------------------------------------------------------------------------------------------------------------------------------- + <root xmlns:ns1="http:/x.y/ns1" xmlns="http:/x.y/def" att="val"><child1 xmlns:ns2="http:/x.y/ns2" xmlns=""/><!--:)-->foobar<child2/></root> +(1 row) + +\sv view_xmlnamespaces +CREATE OR REPLACE VIEW public.view_xmlnamespaces AS + SELECT XMLELEMENT(NAME root, XMLATTRIBUTES('val' AS att), XMLNAMESPACES('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), XMLELEMENT(NAME child1, XMLNAMESPACES('http:/x.y/ns2' AS ns2, DEFAULT '')), xmlcomment(':)'::text), XMLCONCAT('foo'::xml, 'bar'::xml), XMLELEMENT(NAME child2, XMLNAMESPACES(DEFAULT NULL::unknown))) AS xmldoc +\set VERBOSITY terse +-- duplicate xmlnamespace entry (ns1) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1)); +ERROR: XML namespace name "ns1" appears more than once at character 70 +-- invalid xmlnamespace syntax +SELECT xmlelement(NAME "root", xmlnamespaces('invalid')); +ERROR: syntax error at or near ")" at character 55 +-- multiple DEFAULT xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2')); +ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 71 +-- multiple NO DEFAULT xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT)); +ERROR: XML elements can have only a single [NO] DEFAULT namespace at character 58 +-- invalid xmlnamespace prefix (xmlns) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns)); +ERROR: invalid XML namespace prefix "xmlns" at character 46 +-- invalid xmlnamespace prefix (xml) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml)); +ERROR: invalid XML namespace prefix "xml" at character 46 +-- duplicate xmlnamespace calls +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlnamespaces('http:/x.y/ns2' AS ns2)); +ERROR: duplicate XMLNAMESACES specified at character 71 +\set VERBOSITY default +-- invalid xmlnamespaces URIs +SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar)); +ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/" +DETAIL: this URI is already bounded to standard a namespace prefix +SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar)); +ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace" +DETAIL: this URI is already bounded to standard a namespace prefix +-- invalid DEFAULT xmlnamespace URIs +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/')); +ERROR: invalid XML namespace URI "http://www.w3.org/2000/xmlns/" +DETAIL: this URI is already bounded to standard a namespace prefix +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace')); +ERROR: invalid XML namespace URI "http://www.w3.org/XML/1998/namespace" +DETAIL: this URI is already bounded to standard a namespace prefix +-- empty xmlnamespace uri +SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns)); +ERROR: invalid XML namespace URI for "ns" +DETAIL: a regular XML namespace cannot be a zero-length string SELECT xmlparse(content ''); xmlparse ---------- @@ -1384,6 +1638,11 @@ SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'), PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>' COLUMNS a int PATH 'a'); ERROR: DEFAULT namespace is not supported +SELECT * FROM XMLTABLE(XMLNAMESPACES(NO DEFAULT), + '/rows/row' + PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>' + COLUMNS a int PATH 'a'); +ERROR: NO DEFAULT namespace is not supported SELECT * FROM XMLTABLE('.' PASSING '<foo/>' COLUMNS a text PATH 'foo/namespace::node()'); diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index bac0388ac1..0a14a3deac 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -66,6 +66,134 @@ SELECT xmlelement(name foo, xmlattributes('2009-04-09 00:24:37'::timestamp as ba SELECT xmlelement(name foo, xmlattributes('infinity'::timestamp as bar)); SELECT xmlelement(name foo, xmlattributes('<>&"''' as funny, xml 'b<a/>r' as funnier)); +-- DEFAULT NULL xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT NULL)); +-- DEFAULT xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1')); +-- DEFAULT numeric xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 42.73)); +-- DEFAULT empty xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT '')); +-- DEFAULT empty xmlnamespace +SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT)); +-- Testing xmlnamespace with subqueries +SELECT + xmlelement(NAME root, xmlnamespaces(DEFAULT 'ns'), + (SELECT xmlelement(NAME child1, xmlnamespaces(NO DEFAULT)))); +-- Testing xmlnamespace url from ColumnRef +CREATE TABLE xmlns (url text); +INSERT INTO xmlns VALUES ('http:/x.y/ns1'); +SELECT + xmlserialize(DOCUMENT + xmlelement(NAME root, xmlnamespaces(DEFAULT 'foo', url AS ns), + xmlelement(NAME root, + xmlelement(NAME child2, xmlnamespaces(url AS ns, NO DEFAULT)))) + AS text INDENT) +FROM xmlns; +-- NULL xmlnamespace uri +SELECT xmlelement(NAME "root", xmlnamespaces(NULL AS ns)); +-- empty xmlelement containing one namespace +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1)); +-- empty xmlelement with DEFAULT and regular xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', 'http:/x.y/ns2' AS ns2)); +-- xmlelement containing a namespace and a text node +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), 'text node'); +-- empty xmlelement containing a) xmlnamespace and b) xmlattribute +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlattributes('val' AS att)); +-- empty xmlelement containing a) xmlattribute and b) xmlnamespace +SELECT xmlelement(NAME "root", xmlattributes('val' AS att), xmlnamespaces('http:/x.y/ns1' AS ns1)); +-- empty xmlelement containing two xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2)); +-- empty xmlelement containing two xmlnamespaces and one xmlattribute +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att)); +-- xmlelement containing two xmlnamespaces, one xmlattribute, +-- and a text node. +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns2' AS ns2), xmlattributes('val' AS att), 'text node'); +-- empty root xmlelement containing one xmlnamespace, one xmlattribute, +-- and one xmlelement child (ns1:foo). +-- empty xmlelement child "ns1:foo" containing one xmlnamespace and one +-- xmlelement child (ns2:bar). +-- xmlelement child "ns2:bar" containing a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1), + xmlattributes('val' AS att), + xmlelement(NAME "ns1:foo", + xmlnamespaces('http:/x.y/ns2' AS ns2), + xmlelement(NAME "ns2:bar", 'text node')) + ); +-- empty root xmlelement containing one xmlnamespace, one xmlattribute, +-- and one xmlelement child (ns1:foo). +-- xmlelement child "ns1:foo" containing one xmlnamespace, one xmlelement +-- child (ns2:bar), and a text node. +-- xmlelement child "ns2:bar" containing a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1), + xmlattributes('val' AS att), + xmlelement(NAME "ns1:foo", + xmlnamespaces('http:/x.y/ns2' AS ns2), + xmlelement(NAME "ns2:bar", 'text node'), + 'mixed content') + ); +-- empty root xmlelement containing one DEFAULT xmlnamespace, one xmlattribute, +-- and one xmlelement child (foo). +-- xmlelement child "foo" containing one NO DEFAULT xmlnamespace and a text node. +SELECT + xmlelement(NAME "root", + xmlnamespaces(DEFAULT 'http:/x.y/ns1'), + xmlattributes('val' AS att), + xmlelement(NAME "foo", + xmlnamespaces(NO DEFAULT),'bar') + ); +-- empty root xmlelement containing one xmlattribute, one xmlnamespace, +-- three child nodes generated by xmlforest and xmltext, one xmlcomment, +-- and a text node generated by xmlconcat. +SELECT + xmlelement(NAME "root", + xmlattributes(73 AS att), + xmlnamespaces('http:/x.y/ns1' AS ns), + xmlforest(true AS "ns:x", 42 AS "ns:y", xmltext('<&>') AS "ns:z"), + xmlcomment(':)'), + xmlconcat('foo', 'bar') + ); +-- test xmlnamespaces within views +CREATE VIEW view_xmlnamespaces AS +SELECT + xmlelement(NAME "root", + xmlnamespaces('http:/x.y/ns1' AS ns1, DEFAULT 'http:/x.y/def'), xmlattributes('val' AS att), + xmlelement(NAME "child1", xmlnamespaces('http:/x.y/ns2' AS ns2, NO DEFAULT)), + xmlcomment(':)'), + xmlconcat('foo', 'bar'), + xmlelement(NAME "child2", xmlnamespaces(DEFAULT NULL))) AS xmldoc; +SELECT * FROM view_xmlnamespaces; +\sv view_xmlnamespaces + +\set VERBOSITY terse +-- duplicate xmlnamespace entry (ns1) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1, 'http:/x.y/ns1' AS ns1)); +-- invalid xmlnamespace syntax +SELECT xmlelement(NAME "root", xmlnamespaces('invalid')); +-- multiple DEFAULT xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http:/x.y/ns1', DEFAULT 'http:/x.y/ns2')); +-- multiple NO DEFAULT xmlnamespaces +SELECT xmlelement(NAME "root", xmlnamespaces(NO DEFAULT, NO DEFAULT)); +-- invalid xmlnamespace prefix (xmlns) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xmlns)); +-- invalid xmlnamespace prefix (xml) +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS xml)); +-- duplicate xmlnamespace calls +SELECT xmlelement(NAME "root", xmlnamespaces('http:/x.y/ns1' AS ns1), xmlnamespaces('http:/x.y/ns2' AS ns2)); +\set VERBOSITY default +-- invalid xmlnamespaces URIs +SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/2000/xmlns/' AS bar)); +SELECT xmlelement(NAME "root", xmlnamespaces('http://www.w3.org/XML/1998/namespace' AS bar)); +-- invalid DEFAULT xmlnamespace URIs +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/2000/xmlns/')); +SELECT xmlelement(NAME "root", xmlnamespaces(DEFAULT 'http://www.w3.org/XML/1998/namespace')); +-- empty xmlnamespace uri +SELECT xmlelement(NAME "root", xmlnamespaces('' AS ns)); + SELECT xmlparse(content ''); SELECT xmlparse(content ' '); @@ -453,6 +581,11 @@ SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'), PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>' COLUMNS a int PATH 'a'); +SELECT * FROM XMLTABLE(XMLNAMESPACES(NO DEFAULT), + '/rows/row' + PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>' + COLUMNS a int PATH 'a'); + SELECT * FROM XMLTABLE('.' PASSING '<foo/>' COLUMNS a text PATH 'foo/namespace::node()'); -- 2.34.1