From 6952930bc6d706a42736d9579f449bcbf598d201 Mon Sep 17 00:00:00 2001 From: Markus Winand Date: Tue, 27 Mar 2018 08:36:59 +0000 Subject: [PATCH] Set correct context for XPath evaluation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the SQL standard, the context of XMLTABLE's XPath row_expression is the document node of the XML input document, not the root node. This becomes visible when a relative path rather than absolute is used as row expression. Absolute paths is what was used in original tests and docs (and the most common form used in examples throughout the interwebs), which explains why this wasn't noticed before. Other functions such as xpath() and xpath_exists() also have this problem. While not specified by the SQL standard, it would be pretty odd to leave those functions to behave differently than XMLTABLE, so change them too. However, this is a backwards-incompatible change. Author: Markus Winand Reported-By: Markus Winand Reviewed-by: Álvaro Herrera Discussion: https://postgr.es/m/0684A598-002C-42A2-AE12-F024A324EAE4@winand.at --- src/backend/utils/adt/xml.c | 10 ++-------- src/test/regress/expected/xml.out | 8 +++++++- src/test/regress/expected/xml_1.out | 12 +++++++++--- src/test/regress/expected/xml_2.out | 8 +++++++- src/test/regress/sql/xml.sql | 3 ++- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 8307f1cf47..37d85f71f3 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -3934,10 +3934,7 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces, if (xpathctx == NULL || xmlerrcxt->err_occurred) xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate XPath context"); - xpathctx->node = xmlDocGetRootElement(doc); - if (xpathctx->node == NULL || xmlerrcxt->err_occurred) - xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR, - "could not find root XML element"); + xpathctx->node = (xmlNodePtr) doc; /* register namespaces, if any */ if (ns_count > 0) @@ -4276,10 +4273,7 @@ XmlTableSetDocument(TableFuncScanState *state, Datum value) if (xpathcxt == NULL || xtCxt->xmlerrcxt->err_occurred) xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, "could not allocate XPath context"); - xpathcxt->node = xmlDocGetRootElement(doc); - if (xpathcxt->node == NULL || xtCxt->xmlerrcxt->err_occurred) - xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR, - "could not find root XML element"); + xpathcxt->node = (xmlNodePtr) doc; } PG_CATCH(); { diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 3eb638ca25..6e1f885112 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -670,6 +670,12 @@ SELECT xpath('/nosuchtag', ''); {} (1 row) +SELECT xpath('root', ''); + xpath +----------- + {} +(1 row) + -- Round-trip non-ASCII data through xpath(). DO $$ DECLARE @@ -1212,7 +1218,7 @@ SELECT * FROM xmltable('/root' passing 'a1aa2aa1aa2a bbbbxxxcccc' COLUMNS element text PATH 'element/text()'); -- should fail ERROR: more than one value returned by column XPath expression -- CDATA test -select * from xmltable('r' passing ' &"<>!foo]]>2' columns c text); +select * from xmltable('d/r' passing ' &"<>!foo]]>2' columns c text); c ------------------------- &"<>!foo diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index 2053734f65..0eba424346 100644 --- a/src/test/regress/expected/xml_1.out +++ b/src/test/regress/expected/xml_1.out @@ -576,6 +576,12 @@ LINE 1: SELECT xpath('/nosuchtag', ''); ^ DETAIL: This functionality requires the server to be built with libxml support. HINT: You need to rebuild PostgreSQL using --with-libxml. +SELECT xpath('root', ''); +ERROR: unsupported XML feature +LINE 1: SELECT xpath('root', ''); + ^ +DETAIL: This functionality requires the server to be built with libxml support. +HINT: You need to rebuild PostgreSQL using --with-libxml. -- Round-trip non-ASCII data through xpath(). DO $$ DECLARE @@ -1067,10 +1073,10 @@ LINE 1: SELECT * FROM xmltable('/root' passing 'a1a &"<>!foo]]>2' columns c text); +select * from xmltable('d/r' passing ' &"<>!foo]]>2' columns c text); ERROR: unsupported XML feature -LINE 1: select * from xmltable('r' passing ''); {} (1 row) +SELECT xpath('root', ''); + xpath +----------- + {} +(1 row) + -- Round-trip non-ASCII data through xpath(). DO $$ DECLARE @@ -1192,7 +1198,7 @@ SELECT * FROM xmltable('/root' passing 'a1aa2aa1aa2a bbbbxxxcccc' COLUMNS element text PATH 'element/text()'); -- should fail ERROR: more than one value returned by column XPath expression -- CDATA test -select * from xmltable('r' passing ' &"<>!foo]]>2' columns c text); +select * from xmltable('d/r' passing ' &"<>!foo]]>2' columns c text); c ------------------------- &"<>!foo diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index c223603a1f..3b91b56d5a 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -188,6 +188,7 @@ SELECT xpath('count(//*)=0', ''); SELECT xpath('count(//*)=3', ''); SELECT xpath('name(/*)', ''); SELECT xpath('/nosuchtag', ''); +SELECT xpath('root', ''); -- Round-trip non-ASCII data through xpath(). DO $$ @@ -423,7 +424,7 @@ SELECT * FROM xmltable('/root' passing 'a1aa2aa1aa2a bbbbxxxcccc' COLUMNS element text PATH 'element/text()'); -- should fail -- CDATA test -select * from xmltable('r' passing ' &"<>!foo]]>2' columns c text); +select * from xmltable('d/r' passing ' &"<>!foo]]>2' columns c text); -- XML builtin entities SELECT * FROM xmltable('/x/a' PASSING ''"&<>' COLUMNS ent text); -- 2.11.0