eXist-db / exist

eXist Native XML Database and Application Platform
https://exist-db.org
GNU Lesser General Public License v2.1
428 stars 179 forks source link

[BUG] XQuery return wrong results on start-with comparison #4810

Open Twilight-Shuxin opened 1 year ago

Twilight-Shuxin commented 1 year ago

Describe the bug

Given XML document:

<Y1 id="1">
  <H1 id="2" a2="2">true</H1>
</Y1>

and XPath query: //*[starts-with(@a2,"1") = false()]

Exist db returns empty result set

Expected behavior

Should return

<Y1 id="1">
   <H1 id="2" a2="2">true</H1>
</Y1>
<H1 id="2" a2="2">true</H1>

as BaseX and Saxon

To Reproduce

  1. Go to Java admin client
  2. Add XML document to collection
  3. Use find command to execute XPath Query
  4. See error

Context (please always complete the following information)

Additional context

dizzzz commented 1 year ago

Are there any indexes defined?

joewiz commented 1 year ago

I can reproduce this in eXist 6.2.0. Here's the XQSuite:

xquery version "3.1";

module namespace t="http://exist-db.org/xquery/test";

declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $t:XML := document {
    <Y1 id="1">
        <H1 id="2" a2="2">true</H1>
    </Y1>
};

declare
    %test:setUp
function t:setup() {
    let $testCol := xmldb:create-collection("/db", "test")
    return
        xmldb:store("/db/test", "test.xml", $t:XML)
};

declare
    %test:tearDown
function t:tearDown() {
    xmldb:remove("/db/test")
};

declare
    %test:assertTrue
function t:test() {
    doc("/db/test/test.xml")//*[starts-with(@a2, "1") = false()]
    => exists()
};

The results showing the test failure:

<testsuite errors="0" failures="1" package="http://exist-db.org/xquery/test" pending="0" tests="1"
    time="PT0.003S" timestamp="2023-03-20T16:36:36.012-04:00">
    <testcase class="t:test" name="test">
        <failure message="assertTrue failed." type="failure-error-code-1"/>
        <output>false</output>
    </testcase>
</testsuite>
Twilight-Shuxin commented 1 year ago

Are there any indexes defined?

There is no index defined :)

joewiz commented 1 year ago

A revised XQSuite, with XML trimmed of unnecessary info, and with an in-memory version that passes, when the version stored in the database fails:

xquery version "3.1";

module namespace t="http://exist-db.org/xquery/test";

declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $t:XML := document {
    <X>
        <Y n="2"/>
    </X>
};

declare
    %test:setUp
function t:setup() {
    xmldb:create-collection("/db", "test"),
    xmldb:store("/db/test", "test.xml", $t:XML)
};

declare
    %test:tearDown
function t:tearDown() {
    xmldb:remove("/db/test")
};

declare
    %test:assertTrue
function t:test-db() {
    exists(
        doc("/db/test/test.xml")//*[starts-with(@n, "1") = false()]
    )
};

declare
    %test:assertTrue
function t:test-mem() {
    exists(
        $t:XML//*[starts-with(@n, "1") = false()]
    )
};

The results:

<testsuite errors="0" failures="1" package="http://exist-db.org/xquery/test" pending="0" tests="2"
    time="PT0.001S" timestamp="2023-03-20T23:30:23.526-04:00">
    <testcase class="t:test-db" name="test-db">
        <failure message="assertTrue failed." type="failure-error-code-1"/>
        <output>false</output>
    </testcase>
    <testcase class="t:test-mem" name="test-mem"/>
</testsuite>
line-o commented 1 year ago

This bug is closely related to #4824 as //*[somePredicate] does not iterate over the correct node set.

line-o commented 1 year ago

@Twilight-Shuxin I cannot reproduce the expected output with BaseX 10.4

The query <a><b c="1"/></a>//*[starts-with(@c, "a")=false()] does only return the inner element. And that is also meeting my expectations now that I learned what //* really stands for.

line-o commented 1 year ago

The test should be run agains document{<a><b c="1"/></a>}//*[starts-with(@c, "a")=false()] in eXist-db as that is where BaseX does return the expected results. The document-node is the implicit root in this issue.

line-o commented 1 year ago

Exist-db does return the expected results as well when the document-node is added to the query.

line-o commented 1 year ago

For the given example that would be

xquery version "3.1";

let $test := document{<Y1 id="1"><H1 id="2" a2="2">true</H1></Y1>}

return
    $test//*[starts-with(@a2,"1") = false()]
Twilight-Shuxin commented 1 year ago

@Twilight-Shuxin I cannot reproduce the expected output with BaseX 10.4

The query <a><b c="1"/></a>//*[starts-with(@c, "a")=false()] does only return the inner element. And that is also meeting my expectations now that I learned what //* really stands for.

Yes, I also had some misunderstandings on this issue, the expected result should only be when query is performed on the document. I will modify the report. Thanks for explaining!

line-o commented 1 year ago

@Twilight-Shuxin I cannot reproduce the reported issue when using the modified query

document{
  <Y1 id="1"><H1 id="2" a2="2">true</H1></Y1>
}//*[starts-with(@a2,"1") = false()]

returns

<Y1 id="1"><H1 id="2" a2="2">true</H1></Y1>,<H1 id="2" a2="2">true</H1>

tested on eXist-6.0.1 and 7.0.0-SNAPSHOT

line-o commented 1 year ago

Ah! This is a bug on persistent node-sets. @joewiz thanks for providing your findings and tests.