eXist-db / exist

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

[BUG] Map context item (.) bug: effectiveBooleanValue error filtering maps with not() #3873

Open joewiz opened 3 years ago

joewiz commented 3 years ago

Describe the bug

Issue https://github.com/eXist-db/exist/issues/3240 described and PR https://github.com/eXist-db/exist/pull/3496 fixed a number of errors thrown in eXist when the context item (.) is a map, array, or function. We've got another one, so far affecting only maps in certain conditions:

When using a predicate to filter sequence of maps, wrapping the filter expression in a function that involves the context item's effective boolean value causes an error to be thrown.

xquery version "3.1";

let $words :=
    (
        map {
            "word": "foo",
            "rhymes-with-boo": true()
        },
        map {
            "word": "bar",
            "rhymes-with-boo": false()
        }
    )
return
    $words[not(.?rhymes-with-boo)]?word

This query should return bar, and BaseX and Saxon return the expected result, but eXist throws an effectiveBooleanValue error:

err:FORG0006 effectiveBooleanValue: first item of '(true, false)' is not a node, and sequence length > 1 [at line 15, column 12]

The error location points to the not() function. This suggests that eXist is treating the query as if the context item is the sequence on the left-hand side of the predicate, instead of the item being

Expected behavior

eXist should not throw this incorrect error.

To Reproduce

Building on the tests in https://github.com/eXist-db/exist/pull/3496/files#diff-6f2de4fc23f42984d074ae40eb4f18bb63715b592ad8d08b699010a7ed1e7460:

xquery version "3.1";

module namespace filter-function-items-in-context="http://exist-db.org/xquery/test/filter-function-items-in-context";

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

declare variable $filter-function-items-in-context:maps := ( map { "id": 1 }, map { "id": 2, "foo": 3 } );

declare
    %test:assertEmpty
function filter-function-items-in-context:effective-boolean-value() {
    $filter-function-items-in-context:maps[not(.?id)]
};

This returns the following result:

<testsuite package="http://exist-db.org/xquery/test/filter-function-items-in-context"
    timestamp="2021-05-05T00:09:31.915-04:00" tests="1" failures="0" errors="1" pending="0"
    time="PT0.001S">
    <testcase name="filter-function-items-in-context:effective-boolean-value"
        class="filter-function-items-in-context:effective-boolean-value">
        <error type="err:FORG0006"
            message="Invalid argument type. effectiveBooleanValue: first item of '(1, 2)' is not a node, and sequence length &gt; 1"
        />
    </testcase>
</testsuite>

Context (please always complete the following information):

Additional context

line-o commented 3 years ago

I believe this is a duplicate of the issue with not() in a predicate.

line-o commented 3 years ago

To test this hypothesis:

line-o commented 3 years ago

related #2159 (I can't find the issue where I posted about the wrapped not function)

line-o commented 3 years ago

Here is what I meant

declare variable $local:maps := (
  map { "id": 1 },
  map { "id": 2, "foo": 3 }, 
  map{ 1: 0 }
);

declare function local:not($a) {
    not($a)
};

declare function local:effective-boolean-value() {
    $local:maps[local:not(.?id)]
};

local:effective-boolean-value()
joewiz commented 3 years ago

@line-o I think I found the issue where you posted about the not() function: https://github.com/eXist-db/exist/issues/2308#issuecomment-452339885 (and where you developed the test suite committed in PR #2370). I agree that this is more about eXist's handling of not() than a particular issue with maps, and that it's a dupe of #2308, where @wolfgangmm shared this key insight about the underlying problem with not() in eXist:

eXist handles fn:not in a particular way by trying to evaluate it as a set operation. Obviously this approach is wrongly applied here. A naive fix would likely result in many expressions becoming slower by an order of magnitude, so we need to be careful here. We'll keep this issue open, but fixing will take considerable time.