Skip to content

Commit e32cb8d

Browse files
committed
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>
1 parent 1619442 commit e32cb8d

File tree

4 files changed

+55
-8
lines changed

4 files changed

+55
-8
lines changed

src/backend/utils/adt/xml.c

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,10 @@ static bool print_xml_decl(StringInfo buf, const xmlChar *version,
141141
pg_enc encoding, int standalone);
142142
static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
143143
bool preserve_whitespace, int encoding);
144-
static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
144+
static text *xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt);
145145
static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
146-
ArrayBuildState **astate);
146+
ArrayBuildState **astate,
147+
PgXmlErrorContext *xmlerrcxt);
147148
#endif /* USE_LIBXML */
148149

149150
static StringInfo query_to_xml_internal(const char *query, char *tablename,
@@ -3606,26 +3607,41 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
36063607
* return value otherwise)
36073608
*/
36083609
static text *
3609-
xml_xmlnodetoxmltype(xmlNodePtr cur)
3610+
xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
36103611
{
36113612
xmltype *result;
36123613

36133614
if (cur->type == XML_ELEMENT_NODE)
36143615
{
36153616
xmlBufferPtr buf;
3617+
xmlNodePtr cur_copy;
36163618

36173619
buf = xmlBufferCreate();
3620+
3621+
/*
3622+
* The result of xmlNodeDump() won't contain namespace definitions
3623+
* from parent nodes, but xmlCopyNode() duplicates a node along with
3624+
* its required namespace definitions.
3625+
*/
3626+
cur_copy = xmlCopyNode(cur, 1);
3627+
3628+
if (cur_copy == NULL)
3629+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
3630+
"could not copy node");
3631+
36183632
PG_TRY();
36193633
{
3620-
xmlNodeDump(buf, NULL, cur, 0, 1);
3634+
xmlNodeDump(buf, NULL, cur_copy, 0, 1);
36213635
result = xmlBuffer_to_xmltype(buf);
36223636
}
36233637
PG_CATCH();
36243638
{
3639+
xmlFreeNode(cur_copy);
36253640
xmlBufferFree(buf);
36263641
PG_RE_THROW();
36273642
}
36283643
PG_END_TRY();
3644+
xmlFreeNode(cur_copy);
36293645
xmlBufferFree(buf);
36303646
}
36313647
else
@@ -3667,7 +3683,8 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
36673683
*/
36683684
static int
36693685
xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
3670-
ArrayBuildState **astate)
3686+
ArrayBuildState **astate,
3687+
PgXmlErrorContext *xmlerrcxt)
36713688
{
36723689
int result = 0;
36733690
Datum datum;
@@ -3689,7 +3706,8 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
36893706

36903707
for (i = 0; i < result; i++)
36913708
{
3692-
datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
3709+
datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
3710+
xmlerrcxt));
36933711
*astate = accumArrayResult(*astate, datum,
36943712
false, XMLOID,
36953713
CurrentMemoryContext);
@@ -3893,9 +3911,9 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
38933911
* Extract the results as requested.
38943912
*/
38953913
if (res_nitems != NULL)
3896-
*res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate);
3914+
*res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
38973915
else
3898-
(void) xml_xpathobjtoxmlarray(xpathobj, astate);
3916+
(void) xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
38993917
}
39003918
PG_CATCH();
39013919
{

src/test/regress/expected/xml.out

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,21 @@ SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="http://127.0.0.1"><loc
584584
{1,2}
585585
(1 row)
586586

587+
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']]);
588+
xpath
589+
------------------------------------------------------------------------------------------------------------------------------------------------
590+
{"<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\"/>"}
591+
(1 row)
592+
593+
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']]);
594+
xpath
595+
--------------------------------------------------------------------------------------
596+
{"<local:piece xmlns:local=\"http://127.0.0.1\" xmlns=\"http://127.0.0.2\" id=\"1\">+
597+
<internal>number one</internal> +
598+
<internal2/> +
599+
</local:piece>","<local:piece xmlns:local=\"http://127.0.0.1\" id=\"2\"/>"}
600+
(1 row)
601+
587602
SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
588603
xpath
589604
-------------------------

src/test/regress/expected/xml_1.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,18 @@ LINE 1: SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="ht...
498498
^
499499
DETAIL: This functionality requires the server to be built with libxml support.
500500
HINT: You need to rebuild PostgreSQL using --with-libxml.
501+
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']]);
502+
ERROR: unsupported XML feature
503+
LINE 1: SELECT xpath('//loc:piece', '<local:data xmlns:local="http:/...
504+
^
505+
DETAIL: This functionality requires the server to be built with libxml support.
506+
HINT: You need to rebuild PostgreSQL using --with-libxml.
507+
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']]);
508+
ERROR: unsupported XML feature
509+
LINE 1: SELECT xpath('//loc:piece', '<local:data xmlns:local="http:/...
510+
^
511+
DETAIL: This functionality requires the server to be built with libxml support.
512+
HINT: You need to rebuild PostgreSQL using --with-libxml.
501513
SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
502514
ERROR: unsupported XML feature
503515
LINE 1: SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>'...

src/test/regress/sql/xml.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ SELECT xpath(NULL, NULL) IS NULL FROM xmltest;
174174
SELECT xpath('', '<!-- error -->');
175175
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>');
176176
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']]);
177+
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']]);
178+
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']]);
177179
SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
178180
SELECT xpath('//text()', '<root>&lt;</root>');
179181
SELECT xpath('//@value', '<root value="&lt;"/>');

0 commit comments

Comments
 (0)