Skip to content

[feature] Support map atomization in XQuery 4.0#6259

Open
joewiz wants to merge 2 commits into
eXist-db:developfrom
joewiz:feature/xq4-atomization
Open

[feature] Support map atomization in XQuery 4.0#6259
joewiz wants to merge 2 commits into
eXist-db:developfrom
joewiz:feature/xq4-atomization

Conversation

@joewiz
Copy link
Copy Markdown
Member

@joewiz joewiz commented Apr 25, 2026

Summary

  • Add version-gated map atomization for XQuery 4.0 (maps are atomizable in XQ4 but not in XQ 3.1)
  • Resolves ~103 XQTS QT4 test failures caused by err:FOTY0013 when atomizing map results
  • XQ 3.1 behavior unchanged: maps still raise FOTY0013

What Changed

AbstractMapType.java

  • isXq4Atomizable() — version gate: returns true when context.getXQueryVersion() >= 40
  • atomizeValues() — iterates all map entries, atomizes each value, returns flat Sequence
  • atomize() override — for XQ4 single-entry maps returns the atomized value; for XQ 3.1 throws FOTY0013

Atomize.java

  • Updated static atomize(Sequence) to detect XQ4 maps and expand them via atomizeValues() before individual item atomization — same pattern as ArrayType.flatten() for arrays

ConcatExpr.java

  • The FOTY0013 guard for function items now has a carve-out: XQ4 maps are atomized and their string values concatenated

Spec References

Test Plan

  • Build compiles on develop (no XQ4 parser = dormant code path, safe to merge independently)
  • XQSuite tests (1218): zero new regressions (7 pre-existing HOF/xml-to-json failures)
  • XPathQueryTest (150): all pass
  • Full exist-core test suite (7835): zero new regressions
  • XQ 3.1 maps.xqm:no-atomization still expects error (maps not atomizable in 3.1)
  • XQ 3.1 cast-constructor.xqm:atomize-map-fails still expects FOTY0013
  • XQTS QT4 runner verification (requires next-v3 integration with XQ4 parser)

Note: On develop, the isXq4Atomizable() check always returns false since the XQ4 parser support (v2/xq4-parser-extensions) is not yet merged. The code path activates when integrated into next-v3.

🤖 Generated with Claude Code

In XQuery 3.1, maps are function items and cannot be atomized (FOTY0013).
In XQuery 4.0, maps are a distinct type whose atomization returns the
concatenation of the atomized values of their entries.

This change adds version-gated map atomization so that ~103 XQTS QT4
tests that fail with FOTY0013 can pass when combined with the XQ4 parser
support (v2/xq4-parser-extensions).

- AbstractMapType: override atomize(), add isXq4Atomizable() and
  atomizeValues() for XQ4 semantics
- Atomize: expand XQ4 maps in static atomize(Sequence), paralleling
  ArrayType.flatten() for arrays
- ConcatExpr: allow XQ4 maps in string concatenation instead of
  unconditionally throwing FOTY0013

XQ 3.1 behavior is unchanged: maps still raise FOTY0013.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@joewiz joewiz requested a review from a team as a code owner April 25, 2026 03:28
Comment on lines +82 to +84
if (single instanceof AbstractMapType && ((AbstractMapType) single).isXq4Atomizable()) {
return ((AbstractMapType) single).atomizeValues();
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use direct cast using local variable here:

Suggested change
if (single instanceof AbstractMapType && ((AbstractMapType) single).isXq4Atomizable()) {
return ((AbstractMapType) single).atomizeValues();
}
if (single instanceof AbstractMapType mapType && mapType.isXq4Atomizable()) {
return mapType.atomizeValues();
}

Comment on lines +93 to +94
if (next instanceof AbstractMapType && ((AbstractMapType) next).isXq4Atomizable()) {
result.addAll(((AbstractMapType) next).atomizeValues());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

Suggested change
if (next instanceof AbstractMapType && ((AbstractMapType) next).isXq4Atomizable()) {
result.addAll(((AbstractMapType) next).atomizeValues());
if (next instanceof AbstractMapType mapType && mapType.isXq4Atomizable()) {
result.addAll(mapType.atomizeValues());

Comment on lines +71 to +72
if (item instanceof AbstractMapType && ((AbstractMapType) item).isXq4Atomizable()) {
final Sequence atomized = ((AbstractMapType) item).atomizeValues();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Suggested change
if (item instanceof AbstractMapType && ((AbstractMapType) item).isXq4Atomizable()) {
final Sequence atomized = ((AbstractMapType) item).atomizeValues();
if (item instanceof AbstractMapType mapType && mapType.isXq4Atomizable()) {
final Sequence atomized = mapType.atomizeValues();

Use Java 16+ pattern-matching instanceof to eliminate redundant casts
in Atomize.java and ConcatExpr.java.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@joewiz
Copy link
Copy Markdown
Member Author

joewiz commented Apr 25, 2026

[This response was co-authored with Claude Code. -Joe]

Thanks @reinhapa — addressed in b7c258c:

  • Converted all 3 instanceof + cast patterns to Java 16+ pattern-matching instanceof in Atomize.java (2 locations) and ConcatExpr.java (1 location)

@line-o line-o added xquery issue is related to xquery implementation XQ4 xquery 4 labels May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

XQ4 xquery 4 xquery issue is related to xquery implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants