[bugfix] RootNode: declare CONTEXT_ITEM dependency#6409
Merged
line-o merged 2 commits intoMay 30, 2026
Conversation
RootNode (the lone-slash root step '/') inherited the default
Dependency.CONTEXT_SET from AbstractExpression and never declared
CONTEXT_ITEM. The predicate-hoisting path of the optimizer uses
CONTEXT_ITEM to decide whether a sub-expression can be evaluated once
outside the iteration. Without that bit, predicates whose only
context-dependent leaf is '/' get hoisted out and evaluated with no
context item.
When that happens, RootNode.eval falls through to the static-context
default branch — getStaticallyKnownDocuments() with a null
staticDocumentPaths, which returns every XML resource the broker can
see. Under guest that filters down to the empty set; under admin the
sequence includes the admin user's own account record from
/db/system/security/exist/accounts/. An expression like
fn:count(.[5 * /]) then throws XPTY0004 ("Too many operands at the
right of *") because the multi-item result of '/' atomizes to multiple
untypedAtomic values.
Surfaces in W3C qt3tests prod-PathExpr/PathExpr-1, -2, -15 and
prod-StepExpr/Steps-leading-lone-slash-15, -17, -1a when measured
under an admin broker — they pass under guest only because the
fallback returns the empty sequence there.
Adding CONTEXT_ITEM keeps the per-iteration context flowing through to
the predicate. RootNode.eval is unchanged; top-level '/' still falls
through to the static-context default in its no-context cases, so the
long-standing 'absolute path without fn:doc' convention is preserved
for legitimate uses.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
reinhapa
requested changes
May 29, 2026
PMD JUnit naming convention requires [a-z][a-zA-Z0-9]* — no underscores. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Member
Author
line-o
approved these changes
May 29, 2026
reinhapa
approved these changes
May 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[This response was co-authored with Claude Code. -Joe]
Summary
A one-line dependency-declaration fix in
RootNode(the lone-slash root step/), plus a regression test. Without this, the optimizer's predicate hoister treats predicates whose only context-dependent leaf is/as context-independent and evaluates them once outside the iteration. With no context item to resolve against,RootNode.evalfalls through to the static-context default branch (getStaticallyKnownDocuments()), which returns every XML resource the broker can read. Under admin that includes content from/db/system/security/, which then atomizes to multiple untypedAtomics and breaks arithmetic in the predicate.Surfaced while preparing the exist-xqts-runner "assertion fixes only" PR (eXist-db/exist-xqts-runner#61), where commit 5 changes the runner's embedded server connection from
getBroker()toauthenticate("admin", "")— that change is a prerequisite for the in-flight EXPath File built-in PR (#6257). The runner change unblocks #6257; this fix removes the seven XQTS-HEAD regressions admin-context introduced.The fix
RootNodeextendsStepextendsAbstractExpression, which returnsDependency.DEFAULT_DEPENDENCIES = CONTEXT_SET. The default isn't enough: the optimizer's predicate hoister looks atCONTEXT_ITEMspecifically to decide whether a sub-expression can be evaluated once outside the iteration./evaluation depends on the context item to resolve the owner document, so the override is the correct dependency declaration.RootNode.evalis unchanged. Top-level/in queries with nofn:doc()still falls through to the static-context default in its no-context cases, so the long-standing "absolute path without fn:doc" eXist convention is preserved for legitimate uses (e.g.//fooat the top of a query).What it fixes
Per a measured before/after on XQTS HEAD against current
develop, with two builds of exist-xqts-runner that differ only in broker authentication (guest vs admin):prod-PathExprPathExpr-1prod-PathExprPathExpr-2prod-PathExprPathExpr-15prod-StepExprSteps-leading-lone-slash-15prod-StepExprSteps-leading-lone-slash-17prod-StepExprSteps-leading-lone-slash-1aAll six are "leading lone slash" XPath syntax-constraint tests from Nicolae Brinza's 2009 contributions, of the shape
fn:count(.[5 * /])over a single-element document. The expected behavior per the spec, with the bid element as context item, is:/resolves to its owner document (single item), arithmetic produces a single numeric value, the positional predicate has no match, count is zero.These tests pass under guest only because the static-context fallback returns the empty sequence there (guest can't read system collections). The fix removes the dependency on that accidental coincidence.
The diagnosis trail
Instrumenting
OpNumeric.evalwith oneSystem.err.printlnof the operands made the issue obvious:ctxItem=nullin both cases is the smoking gun: the predicate isn't being iterated per-item. The 3-itemrsequnder admin isadmin.xmlatomized through the static-doc fallback. The cause is the missingCONTEXT_ITEMdependency onRootNode.The "all documents in scope under admin" behavior of
RootNode.evalis a vendor-specific static-context default that the XPath 3.1 spec permits (§2.1.1 — static context can define a default context item). What's not permitted is the optimizer mistaking a context-dependent expression for a context-independent one; that's the actual bug.Test plan
RootNodeContextItemDependencyTestcovers three positive cases that all pass with the fix and (2 of 3) error without it. The test uses an in-memory<bid>23</bid>document bound vialet $ctx := document {...}/bidand exercisesfn:count($ctx[5 * /]),fn:count($ctx[(/) * 5]), andfn:count($ctx[fn:count(/) eq 1]).mvn test -pl exist-core— 6,592 tests, 0 failures, 0 errors, 106 skipped, 3:39 wall.Related