Skip to content

Commit c8ef5b1

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 6bf343c commit c8ef5b1

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
@@ -140,9 +140,10 @@ static bool print_xml_decl(StringInfo buf, const xmlChar *version,
140140
pg_enc encoding, int standalone);
141141
static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
142142
bool preserve_whitespace, int encoding);
143-
static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
143+
static text *xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt);
144144
static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
145-
ArrayBuildState **astate);
145+
ArrayBuildState **astate,
146+
PgXmlErrorContext *xmlerrcxt);
146147
#endif /* USE_LIBXML */
147148

148149
static StringInfo query_to_xml_internal(const char *query, char *tablename,
@@ -3593,26 +3594,41 @@ SPI_sql_row_to_xmlelement(int rownum, StringInfo result, char *tablename,
35933594
* return value otherwise)
35943595
*/
35953596
static text *
3596-
xml_xmlnodetoxmltype(xmlNodePtr cur)
3597+
xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
35973598
{
35983599
xmltype *result;
35993600

36003601
if (cur->type == XML_ELEMENT_NODE)
36013602
{
36023603
xmlBufferPtr buf;
3604+
xmlNodePtr cur_copy;
36033605

36043606
buf = xmlBufferCreate();
3607+
3608+
/*
3609+
* The result of xmlNodeDump() won't contain namespace definitions
3610+
* from parent nodes, but xmlCopyNode() duplicates a node along with
3611+
* its required namespace definitions.
3612+
*/
3613+
cur_copy = xmlCopyNode(cur, 1);
3614+
3615+
if (cur_copy == NULL)
3616+
xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
3617+
"could not copy node");
3618+
36053619
PG_TRY();
36063620
{
3607-
xmlNodeDump(buf, NULL, cur, 0, 1);
3621+
xmlNodeDump(buf, NULL, cur_copy, 0, 1);
36083622
result = xmlBuffer_to_xmltype(buf);
36093623
}
36103624
PG_CATCH();
36113625
{
3626+
xmlFreeNode(cur_copy);
36123627
xmlBufferFree(buf);
36133628
PG_RE_THROW();
36143629
}
36153630
PG_END_TRY();
3631+
xmlFreeNode(cur_copy);
36163632
xmlBufferFree(buf);
36173633
}
36183634
else
@@ -3654,7 +3670,8 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
36543670
*/
36553671
static int
36563672
xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
3657-
ArrayBuildState **astate)
3673+
ArrayBuildState **astate,
3674+
PgXmlErrorContext *xmlerrcxt)
36583675
{
36593676
int result = 0;
36603677
Datum datum;
@@ -3676,7 +3693,8 @@ xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
36763693

36773694
for (i = 0; i < result; i++)
36783695
{
3679-
datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
3696+
datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i],
3697+
xmlerrcxt));
36803698
*astate = accumArrayResult(*astate, datum,
36813699
false, XMLOID,
36823700
CurrentMemoryContext);
@@ -3880,9 +3898,9 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
38803898
* Extract the results as requested.
38813899
*/
38823900
if (res_nitems != NULL)
3883-
*res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate);
3901+
*res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
38843902
else
3885-
(void) xml_xpathobjtoxmlarray(xpathobj, astate);
3903+
(void) xml_xpathobjtoxmlarray(xpathobj, astate, xmlerrcxt);
38863904
}
38873905
PG_CATCH();
38883906
{

src/test/regress/expected/xml.out

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

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

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy