]> granicus.if.org Git - postgresql/commitdiff
Fix namespace handling in xpath function
authorPeter Eisentraut <peter_e@gmx.net>
Wed, 7 Jan 2015 04:06:13 +0000 (23:06 -0500)
committerPeter Eisentraut <peter_e@gmx.net>
Sun, 18 Jan 2015 03:11:20 +0000 (22:11 -0500)
Previously, the xml value resulting from an xpath query would not have
namespace declarations if the namespace declarations were attached to
an ancestor element in the input xml value.  That means the output value
was not correct XML.  Fix that by running the result value through
xmlCopyNode(), which produces the correct namespace declarations.

Author: Ali Akbar <the.apaan@gmail.com>

src/backend/utils/adt/xml.c
src/test/regress/expected/xml.out
src/test/regress/expected/xml_1.out
src/test/regress/sql/xml.sql

index 422be69bd6d912a8449a2bb38800b6f44b0b8d54..60de783808d445faa63ef1ceb6b09f0527dc32ad 100644 (file)
@@ -141,9 +141,10 @@ static bool print_xml_decl(StringInfo buf, const xmlChar *version,
                           pg_enc encoding, int standalone);
 static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
                  bool preserve_whitespace, int encoding);
-static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
+static text *xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt);
 static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
-                                          ArrayBuildState **astate);
+                                          ArrayBuildState **astate,
+                                          PgXmlErrorContext *xmlerrcxt);
 #endif   /* USE_LIBXML */
 
 static StringInfo query_to_xml_internal(const char *query, char *tablename,
@@ -3595,26 +3596,41 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
  * return value otherwise)
  */
 static text *
-xml_xmlnodetoxmltype(xmlNodePtr cur)
+xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
 {
        xmltype    *result;
 
        if (cur->type == XML_ELEMENT_NODE)
        {
                xmlBufferPtr buf;
+               xmlNodePtr      cur_copy;
 
                buf = xmlBufferCreate();
+
+               /*
+                * The result of xmlNodeDump() won't contain namespace definitions
+                * from parent nodes, but xmlCopyNode() duplicates a node along with
+                * its required namespace definitions.
+                */
+               cur_copy = xmlCopyNode(cur, 1);
+
+               if (cur_copy == NULL)
+                       xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+                                               "could not copy node");
+
                PG_TRY();
                {
-                       xmlNodeDump(buf, NULL, cur, 0, 1);
+                       xmlNodeDump(buf, NULL, cur_copy, 0, 1);
                        result = xmlBuffer_to_xmltype(buf);
                }
                PG_CATCH();
                {
+                       xmlFreeNode(cur_copy);
                        xmlBufferFree(buf);
                        PG_RE_THROW();
                }
                PG_END_TRY();
+               xmlFreeNode(cur_copy);
                xmlBufferFree(buf);
        }
        else
@@ -3656,7 +3672,8 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
  */
 static int
 xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
-                                          ArrayBuildState **astate)
+                                          ArrayBuildState **astate,
+                                          PgXmlErrorContext *xmlerrcxt)
 {
        int                     result = 0;
        Datum           datum;
@@ -3678,7 +3695,8 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
 
                                        for (i = 0; i < result; i++)
                                        {
-                                               datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
+                                               datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
+                                                                                                                                        xmlerrcxt));
                                                *astate = accumArrayResult(*astate, datum,
                                                                                                   false, XMLOID,
                                                                                                   CurrentMemoryContext);
@@ -3882,9 +3900,9 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
                 * Extract the results as requested.
                 */
                if (res_nitems != NULL)
-                       *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate);
+                       *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
                else
-                       (void) xml_xpathobjtoxmlarray(xpathobj, astate);
+                       (void) xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
        }
        PG_CATCH();
        {
index 382f9df5093157f85351067fbf498bb18e2befa0..9b7b393c85fc1835f3bcc8cfc54c1491d556cb01 100644 (file)
@@ -584,6 +584,21 @@ SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="http://127.0.0.1"><loc
  {1,2}
 (1 row)
 
+SELECT xpath('//loc:piece', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>', ARRAY[ARRAY['loc', 'http://127.0.0.1']]);
+                                                                     xpath                                                                      
+------------------------------------------------------------------------------------------------------------------------------------------------
+ {"<local:piece xmlns:local=\"http://127.0.0.1\" id=\"1\">number one</local:piece>","<local:piece xmlns:local=\"http://127.0.0.1\" id=\"2\"/>"}
+(1 row)
+
+SELECT xpath('//loc:piece', '<local:data xmlns:local="http://127.0.0.1" xmlns="http://127.0.0.2"><local:piece id="1"><internal>number one</internal><internal2/></local:piece><local:piece id="2" /></local:data>', ARRAY[ARRAY['loc', 'http://127.0.0.1']]);
+                                        xpath                                         
+--------------------------------------------------------------------------------------
+ {"<local:piece xmlns:local=\"http://127.0.0.1\" xmlns=\"http://127.0.0.2\" id=\"1\">+
+   <internal>number one</internal>                                                   +
+   <internal2/>                                                                      +
+ </local:piece>","<local:piece xmlns:local=\"http://127.0.0.1\" id=\"2\"/>"}
+(1 row)
+
 SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
           xpath          
 -------------------------
index a34d1f41dd664cf8627cae2ccf254373d743c336..97382499da63fb152b3999557d3078f5b1ac2ff8 100644 (file)
@@ -498,6 +498,18 @@ LINE 1: SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="ht...
                                         ^
 DETAIL:  This functionality requires the server to be built with libxml support.
 HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath('//loc:piece', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>', ARRAY[ARRAY['loc', 'http://127.0.0.1']]);
+ERROR:  unsupported XML feature
+LINE 1: SELECT xpath('//loc:piece', '<local:data xmlns:local="http:/...
+                                    ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath('//loc:piece', '<local:data xmlns:local="http://127.0.0.1" xmlns="http://127.0.0.2"><local:piece id="1"><internal>number one</internal><internal2/></local:piece><local:piece id="2" /></local:data>', ARRAY[ARRAY['loc', 'http://127.0.0.1']]);
+ERROR:  unsupported XML feature
+LINE 1: SELECT xpath('//loc:piece', '<local:data xmlns:local="http:/...
+                                    ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
 SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
 ERROR:  unsupported XML feature
 LINE 1: SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>'...
index 90d4d67f04e289f9bee569c7c8eb349881d840a1..ce87d426842f6a1e91a014b43c9c671cc02e5843 100644 (file)
@@ -174,6 +174,8 @@ SELECT xpath(NULL, NULL) IS NULL FROM xmltest;
 SELECT xpath('', '<!-- error -->');
 SELECT xpath('//text()', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>');
 SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>', ARRAY[ARRAY['loc', 'http://127.0.0.1']]);
+SELECT xpath('//loc:piece', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>', ARRAY[ARRAY['loc', 'http://127.0.0.1']]);
+SELECT xpath('//loc:piece', '<local:data xmlns:local="http://127.0.0.1" xmlns="http://127.0.0.2"><local:piece id="1"><internal>number one</internal><internal2/></local:piece><local:piece id="2" /></local:data>', ARRAY[ARRAY['loc', 'http://127.0.0.1']]);
 SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
 SELECT xpath('//text()', '<root>&lt;</root>');
 SELECT xpath('//@value', '<root value="&lt;"/>');