Commit 79af9a1d authored by Peter Eisentraut's avatar Peter Eisentraut

Fix namespace handling in xpath function

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>
parent 3fabed07
...@@ -141,9 +141,10 @@ static bool print_xml_decl(StringInfo buf, const xmlChar *version, ...@@ -141,9 +141,10 @@ static bool print_xml_decl(StringInfo buf, const xmlChar *version,
pg_enc encoding, int standalone); pg_enc encoding, int standalone);
static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg, static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
bool preserve_whitespace, int encoding); 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, static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
ArrayBuildState *astate); ArrayBuildState *astate,
PgXmlErrorContext *xmlerrcxt);
#endif /* USE_LIBXML */ #endif /* USE_LIBXML */
static StringInfo query_to_xml_internal(const char *query, char *tablename, static StringInfo query_to_xml_internal(const char *query, char *tablename,
...@@ -3599,26 +3600,41 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename, ...@@ -3599,26 +3600,41 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
* return value otherwise) * return value otherwise)
*/ */
static text * static text *
xml_xmlnodetoxmltype(xmlNodePtr cur) xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
{ {
xmltype *result; xmltype *result;
if (cur->type == XML_ELEMENT_NODE) if (cur->type == XML_ELEMENT_NODE)
{ {
xmlBufferPtr buf; xmlBufferPtr buf;
xmlNodePtr cur_copy;
buf = xmlBufferCreate(); 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(); PG_TRY();
{ {
xmlNodeDump(buf, NULL, cur, 0, 1); xmlNodeDump(buf, NULL, cur_copy, 0, 1);
result = xmlBuffer_to_xmltype(buf); result = xmlBuffer_to_xmltype(buf);
} }
PG_CATCH(); PG_CATCH();
{ {
xmlFreeNode(cur_copy);
xmlBufferFree(buf); xmlBufferFree(buf);
PG_RE_THROW(); PG_RE_THROW();
} }
PG_END_TRY(); PG_END_TRY();
xmlFreeNode(cur_copy);
xmlBufferFree(buf); xmlBufferFree(buf);
} }
else else
...@@ -3660,7 +3676,8 @@ xml_xmlnodetoxmltype(xmlNodePtr cur) ...@@ -3660,7 +3676,8 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
*/ */
static int static int
xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj, xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
ArrayBuildState *astate) ArrayBuildState *astate,
PgXmlErrorContext *xmlerrcxt)
{ {
int result = 0; int result = 0;
Datum datum; Datum datum;
...@@ -3679,7 +3696,8 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj, ...@@ -3679,7 +3696,8 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
for (i = 0; i < result; i++) for (i = 0; i < result; i++)
{ {
datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i])); datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
xmlerrcxt));
(void) accumArrayResult(astate, datum, false, (void) accumArrayResult(astate, datum, false,
XMLOID, CurrentMemoryContext); XMLOID, CurrentMemoryContext);
} }
...@@ -3881,9 +3899,9 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces, ...@@ -3881,9 +3899,9 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
* Extract the results as requested. * Extract the results as requested.
*/ */
if (res_nitems != NULL) if (res_nitems != NULL)
*res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate); *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
else else
(void) xml_xpathobjtoxmlarray(xpathobj, astate); (void) xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
} }
PG_CATCH(); PG_CATCH();
{ {
......
...@@ -612,6 +612,21 @@ SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="http://127.0.0.1"><loc ...@@ -612,6 +612,21 @@ SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="http://127.0.0.1"><loc
{1,2} {1,2}
(1 row) (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>'); SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
xpath xpath
------------------------- -------------------------
......
...@@ -514,6 +514,18 @@ LINE 1: SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="ht... ...@@ -514,6 +514,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. DETAIL: This functionality requires the server to be built with libxml support.
HINT: You need to rebuild PostgreSQL using --with-libxml. 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>'); SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
ERROR: unsupported XML feature ERROR: unsupported XML feature
LINE 1: SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>'... LINE 1: SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>'...
......
...@@ -178,6 +178,8 @@ SELECT xpath(NULL, NULL) IS NULL FROM xmltest; ...@@ -178,6 +178,8 @@ SELECT xpath(NULL, NULL) IS NULL FROM xmltest;
SELECT xpath('', '<!-- error -->'); 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('//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/@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('//b', '<a>one <b>two</b> three <b>etc</b></a>');
SELECT xpath('//text()', '<root>&lt;</root>'); SELECT xpath('//text()', '<root>&lt;</root>');
SELECT xpath('//@value', '<root value="&lt;"/>'); SELECT xpath('//@value', '<root value="&lt;"/>');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment