A query which returns expected results with in-memory node returns incorrect results when stored to the database. The problem is manifest when performing a node test using the following-sibling axis against the on-disk data.
My test query creates an in-memory node, stores it on-disk, then performs a series of queries against the data in-memory and on-disk. The queries select a node, traverse the *-sibling XPath axes, and apply node tests. The results become inconsistent when the query performs a node test with a named namespace on the following-sibling axis.
I expected queries on in-memory and on-disk data to perform identically, and for queries involving namespaces and the following-sibling axis to return expected results.
My test query is as follows:
xquery version "3.1";
declare namespace tei="http://www.tei-c.org/ns/1.0";
let $in-mem :=
<TEI xmlns="http://www.tei-c.org/ns/1.0">
<div>
<div xml:id="d1"/>
<div xml:id="d2"/>
<div xml:id="d3"/>
</div>
</TEI>
let $store := xmldb:store("/db", "test.xml", $in-mem)
let $stored := doc($store)
return
(
exists($in-mem//tei:div[@xml:id eq "d2"]),
exists($stored//tei:div[@xml:id eq "d2"]),
exists($in-mem//tei:div[@xml:id eq "d2"]/preceding-sibling::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/preceding-sibling::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/preceding-sibling::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/preceding-sibling::tei:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/following-sibling::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/following-sibling::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/following-sibling::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/following-sibling::tei:div)
)
The queries all return true() except the final one, which returns false().
Update: My original test query used retrieved the source document via HTTP request, but in the interests of simplifying the test, I trimmed the test document to the bare minimum needed to reproduce the result.
A larger test of axes shows that the problem is limited to the following-sibling axis:
xquery version "3.1";
declare namespace tei="http://www.tei-c.org/ns/1.0";
let $in-mem := doc("https://github.com/HistoryAtState/frus/raw/master/volumes/frus1969-76v17.xml")
let $store := xmldb:store("/db", "test.xml", $in-mem)
let $stored := doc($store)
return
(
exists($in-mem//tei:div[@xml:id eq "d2"]),
exists($stored//tei:div[@xml:id eq "d2"]),
exists($in-mem//tei:div[@xml:id eq "d2"]/preceding-sibling::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/preceding-sibling::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/preceding-sibling::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/preceding-sibling::tei:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/following-sibling::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/following-sibling::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/following-sibling::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/following-sibling::tei:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/parent::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/parent::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/parent::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/parent::tei:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/child::*:head),
exists($stored//tei:div[@xml:id eq "d2"]/child::*:head),
exists($in-mem//tei:div[@xml:id eq "d2"]/child::tei:head),
exists($stored//tei:div[@xml:id eq "d2"]/child::tei:head),
exists($in-mem//tei:div[@xml:id eq "d2"]/following::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/following::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/following::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/following::tei:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/preceding::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/preceding::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/preceding::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/preceding::tei:div),
exists($in-mem//tei:div[@xml:id eq "ch1"]/descendant::*:div),
exists($stored//tei:div[@xml:id eq "ch1"]/descendant::*:div),
exists($in-mem//tei:div[@xml:id eq "ch1"]/descendant::tei:div),
exists($stored//tei:div[@xml:id eq "ch1"]/descendant::tei:div),
exists($in-mem//tei:div[@xml:id eq "ch1"]/descendant-or-self::*:div),
exists($stored//tei:div[@xml:id eq "ch1"]/descendant-or-self::*:div),
exists($in-mem//tei:div[@xml:id eq "ch1"]/descendant-or-self::tei:div),
exists($stored//tei:div[@xml:id eq "ch1"]/descendant-or-self::tei:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/ancestor::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/ancestor::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/ancestor::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/ancestor::tei:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/ancestor-or-self::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/ancestor-or-self::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/ancestor-or-self::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/ancestor-or-self::tei:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/self::*:div),
exists($stored//tei:div[@xml:id eq "d2"]/self::*:div),
exists($in-mem//tei:div[@xml:id eq "d2"]/self::tei:div),
exists($stored//tei:div[@xml:id eq "d2"]/self::tei:div)
)
Again, all return true() except exists($stored//tei:div[@xml:id eq "d2"]/following-sibling::tei:div), which returns false().
Note: My original test query used retrieved the source document via HTTP request, but in the interests of simplifying the test, I trimmed the test document to the bare minimum needed to reproduce the result. I've updated the issue description with the revised, simplified test.
The following variant query reveals that the problem is perhaps further limited to cases in which the name of the element selected in the node test is identical to that of the context node's parent element.
xquery version "3.1";
declare namespace tei="http://www.tei-c.org/ns/1.0";
let $test1 :=
<body xmlns="http://www.tei-c.org/ns/1.0">
<div xml:id="d1"/>
<div xml:id="d2"/>
<div xml:id="d3"/>
</body>
let $test2 :=
<div xmlns="http://www.tei-c.org/ns/1.0">
<div xml:id="d1"/>
<div xml:id="d2"/>
<div xml:id="d3"/>
</div>
let $store1 := xmldb:store("/db", "test1.xml", $test1)
let $store2 := xmldb:store("/db", "test2.xml", $test2)
let $doc1 := doc($store1)
let $doc2 := doc($store2)
return
(
exists($doc1//tei:div[@xml:id eq "d2"]/following-sibling::*:div),
exists($doc2//tei:div[@xml:id eq "d2"]/following-sibling::*:div),
exists($doc1//tei:div[@xml:id eq "d2"]/following-sibling::tei:div),
exists($doc2//tei:div[@xml:id eq "d2"]/following-sibling::tei:div)
)
This variant rules out non-default element namespaces as a factor. Again, the final expression returns false() when it should return true():
xquery version "3.1";
let $test1 :=
<x>
<y n="1"/>
<y n="2"/>
<y n="3"/>
</x>
let $test2 :=
<y>
<y n="1"/>
<y n="2"/>
<y n="3"/>
</y>
let $store1 := xmldb:store("/db", "test1.xml", $test1)
let $store2 := xmldb:store("/db", "test2.xml", $test2)
let $doc1 := doc($store1)
let $doc2 := doc($store2)
return
(
exists($doc1//y[@n eq "2"]/following-sibling::*:y),
exists($doc2//y[@n eq "2"]/following-sibling::*:y),
exists($doc1//y[@n eq "2"]/following-sibling::y),
exists($doc2//y[@n eq "2"]/following-sibling::y)
)
Possibly related:
Actually, this is very similar to #1407, which was fixed by @shabanovd in #1416. Perhaps the test cases for that issue and fix missed the case described here: that the problem still appears when the name of the element selected in the node test is identical to that of the context node's parent element.
I think I might have fixed that here https://github.com/adamretter/exist/commit/0096c8c7f0272cfa3b18c0497e283be41a7783ee. There will be a PR soon I hope.
@joewiz Okay my PR which might help with this is here - https://github.com/eXist-db/exist/pull/2113 please can you test?
@adamretter The error still appears to be present. Checking out #2113 and running the test in the issue description, the final result is still false() - indicating that there is still a problem with eXist's processing of the following-sibling axis on nodes stored in the database.
@adamretter I tested #2113 and can confirm the tests in this issue and the ones in PR #1556 now pass. Thank you so much!
Most helpful comment
I think I might have fixed that here https://github.com/adamretter/exist/commit/0096c8c7f0272cfa3b18c0497e283be41a7783ee. There will be a PR soon I hope.