Skip to content

Closures: deterministic order of captured variables in query plans - #2798

Merged
ChristianGruen merged 1 commit into
BaseXdb:mainfrom
GuntherRademacher:#2798
Nov 4, 2026
Merged

ChristianGruen merged 1 commit into
BaseXdb:mainfrom
GuntherRademacher:#2798

Conversation

@GuntherRademacher

Copy link
Copy Markdown
Member

The bindings of a closure were kept in a HashMap<Var, Expr> where Var does not override Object.hashCode(), so the order of the captured variables (and the variable numbering) in the query plan depended on identity hash codes and changed from run to run. Plans for the same query could therefore differ spuriously (in tests, bug reports, or when comparing parser implementations).

Example: run the following a few times with basex -x:

    let $a := <a/>, $b := <b/>, $c := <c/>, $d := <d/>
    return fn() { ($a, $b, $c, $d) }

The Closure lists its variables as $c $a $b $d in one run and $d $b $c $a in the next.

The maps are now LinkedHashMaps (insertion order), in the parser (LocalVars), in Closure (constructor, copy, inlining of nested closures) and in PartFunc. This preserves query semantics while making capture order deterministic. A test in FuncItemTest compiles five such queries 100 times each and compares the plans; it fails if any single one of these changes is reverted.

The declared types (HashMap<Var, Expr>) are left as they are to keep this change minimal. I can follow up with a refactoring to Map<Var, Expr> if desired.

@ChristianGruen
ChristianGruen merged commit b4fa26e into BaseXdb:main Nov 4, 2026
2 checks passed
@ChristianGruen
ChristianGruen deleted the #2798 branch November 4, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants