diff options
author | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2018-06-21 15:56:11 -0400 |
---|---|---|
committer | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2018-06-21 15:56:11 -0400 |
commit | e474c2b7e4b656895d6afcbd36857c9ebb0fcedd (patch) | |
tree | b9b44da9b68a9a4226d41b70b84315f65b6a1c55 /src | |
parent | 425b4c082c82672c646ed8bc9e140b4112899fe4 (diff) | |
download | postgresql-e474c2b7e4b656895d6afcbd36857c9ebb0fcedd.tar.gz postgresql-e474c2b7e4b656895d6afcbd36857c9ebb0fcedd.zip |
Set correct context for XPath evaluation
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.
No backpatch, out of fear of breaking code depending on the original
broken behavior.
Author: Markus Winand
Reported-By: Markus Winand
Reviewed-by: Álvaro Herrera
Discussion: https://postgr.es/m/0684A598-002C-42A2-AE12-F024A324EAE4@winand.at
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/utils/adt/xml.c | 10 | ||||
-rw-r--r-- | src/test/regress/expected/xml.out | 8 | ||||
-rw-r--r-- | src/test/regress/expected/xml_1.out | 12 | ||||
-rw-r--r-- | src/test/regress/expected/xml_2.out | 8 | ||||
-rw-r--r-- | 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 8307f1cf47b..37d85f71f3b 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 3eb638ca258..6e1f885112c 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -670,6 +670,12 @@ SELECT xpath('/nosuchtag', '<root/>'); {} (1 row) +SELECT xpath('root', '<root/>'); + xpath +----------- + {<root/>} +(1 row) + -- Round-trip non-ASCII data through xpath(). DO $$ DECLARE @@ -1212,7 +1218,7 @@ SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaa SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaaa?> <!--z--> bbbb<x>xxx</x>cccc</element></root>' 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 '<d><r><c><![CDATA[<hello> &"<>!<a>foo</a>]]></c></r><r><c>2</c></r></d>' columns c text); +select * from xmltable('d/r' passing '<d><r><c><![CDATA[<hello> &"<>!<a>foo</a>]]></c></r><r><c>2</c></r></d>' columns c text); c ------------------------- <hello> &"<>!<a>foo</a> diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index 2053734f65f..0eba4243462 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', '<root/>'); ^ DETAIL: This functionality requires the server to be built with libxml support. HINT: You need to rebuild PostgreSQL using --with-libxml. +SELECT xpath('root', '<root/>'); +ERROR: unsupported XML feature +LINE 1: SELECT xpath('root', '<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 '<root><element>a1a<!... DETAIL: This functionality requires the server to be built with libxml support. HINT: You need to rebuild PostgreSQL using --with-libxml. -- CDATA test -select * from xmltable('r' passing '<d><r><c><![CDATA[<hello> &"<>!<a>foo</a>]]></c></r><r><c>2</c></r></d>' columns c text); +select * from xmltable('d/r' passing '<d><r><c><![CDATA[<hello> &"<>!<a>foo</a>]]></c></r><r><c>2</c></r></d>' columns c text); ERROR: unsupported XML feature -LINE 1: select * from xmltable('r' passing '<d><r><c><![CDATA[<hello... - ^ +LINE 1: select * from xmltable('d/r' passing '<d><r><c><![CDATA[<hel... + ^ DETAIL: This functionality requires the server to be built with libxml support. HINT: You need to rebuild PostgreSQL using --with-libxml. -- XML builtin entities diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out index cb865a9ef77..3ec56e4c54b 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -650,6 +650,12 @@ SELECT xpath('/nosuchtag', '<root/>'); {} (1 row) +SELECT xpath('root', '<root/>'); + xpath +----------- + {<root/>} +(1 row) + -- Round-trip non-ASCII data through xpath(). DO $$ DECLARE @@ -1192,7 +1198,7 @@ SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaa SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaaa?> <!--z--> bbbb<x>xxx</x>cccc</element></root>' 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 '<d><r><c><![CDATA[<hello> &"<>!<a>foo</a>]]></c></r><r><c>2</c></r></d>' columns c text); +select * from xmltable('d/r' passing '<d><r><c><![CDATA[<hello> &"<>!<a>foo</a>]]></c></r><r><c>2</c></r></d>' columns c text); c ------------------------- <hello> &"<>!<a>foo</a> diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index c223603a1f2..3b91b56d5a9 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -188,6 +188,7 @@ SELECT xpath('count(//*)=0', '<root><sub/><sub/></root>'); SELECT xpath('count(//*)=3', '<root><sub/><sub/></root>'); SELECT xpath('name(/*)', '<root><sub/><sub/></root>'); SELECT xpath('/nosuchtag', '<root/>'); +SELECT xpath('root', '<root/>'); -- Round-trip non-ASCII data through xpath(). DO $$ @@ -423,7 +424,7 @@ SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaa SELECT * FROM xmltable('/root' passing '<root><element>a1a<!-- aaaa -->a2a<?aaaaa?> <!--z--> bbbb<x>xxx</x>cccc</element></root>' COLUMNS element text PATH 'element/text()'); -- should fail -- CDATA test -select * from xmltable('r' passing '<d><r><c><![CDATA[<hello> &"<>!<a>foo</a>]]></c></r><r><c>2</c></r></d>' columns c text); +select * from xmltable('d/r' passing '<d><r><c><![CDATA[<hello> &"<>!<a>foo</a>]]></c></r><r><c>2</c></r></d>' columns c text); -- XML builtin entities SELECT * FROM xmltable('/x/a' PASSING '<x><a><ent>'</ent></a><a><ent>"</ent></a><a><ent>&</ent></a><a><ent><</ent></a><a><ent>></ent></a></x>' COLUMNS ent text); |