Skip to content

Implement W3C XQuery Update Facility 3.0 alongside deprecated legacy syntax#6111

Closed
joewiz wants to merge 7 commits intoeXist-db:developfrom
joewiz:feature/w3c-xquery-update-3.0-v2
Closed

Implement W3C XQuery Update Facility 3.0 alongside deprecated legacy syntax#6111
joewiz wants to merge 7 commits intoeXist-db:developfrom
joewiz:feature/w3c-xquery-update-3.0-v2

Conversation

@joewiz
Copy link
Copy Markdown
Member

@joewiz joewiz commented Mar 7, 2026

Summary

Implements the W3C XQuery Update Facility 3.0 specification alongside eXist-db's existing proprietary update syntax, which is retained as deprecated. The new standard insert node, delete node, replace node, replace value of node, rename node, and copy ... modify ... return expressions bring eXist-db in line with other XQuery processors (BaseX, Saxon) and enable in-memory node updates via the copy-modify (transform) expression.

The legacy update insert/delete/replace/rename/value syntax continues to work but is deprecated. Applications should migrate to the W3C standard syntax. See the Migration Guide below.

Closes #3634
Closes #628

Supersedes #6109.

What Changed

New package: org.exist.xquery.xquf

File Purpose
PendingUpdateList.java Accumulates update primitives during query execution; applies them atomically at snapshot boundary. Handles both persistent (stored) and in-memory nodes. Conflict detection for XUDY0015/0017/0024. Also hosts checkFragmentation() utility (moved from old Modification class).
UpdatePrimitive.java Base class + enum for update operation types (INSERT_BEFORE, INSERT_AFTER, INSERT_INTO, INSERT_INTO_AS_FIRST, INSERT_INTO_AS_LAST, DELETE, REPLACE_NODE, REPLACE_VALUE, RENAME, PUT)
XQUFInsertExpr.java W3C insert node(s) ... (into|as first into|as last into|before|after) ...
XQUFDeleteExpr.java W3C delete node(s) ...
XQUFReplaceNodeExpr.java W3C replace node ... with ...
XQUFReplaceValueExpr.java W3C replace value of node ... with ...
XQUFRenameExpr.java W3C rename node ... as ...
XQUFTransformExpr.java W3C copy $var := expr modify expr return expr — deep-copies nodes, creates nested PUL scope, applies updates to copy
XQUFFnPut.java fn:put($node, $uri) — adds PUT primitive to PUL

Retained (deprecated): org.exist.xquery.update package

The legacy proprietary update implementation classes (Modification, Insert, Delete, Replace, Rename, Update) are retained and continue to work. The checkFragmentation() utility methods were copied to PendingUpdateList for use by the new XQUF system.

Grammar changes

File Changes
XQuery.g Added W3C-compliant xqufInsertExpr, xqufDeleteExpr, xqufReplaceExpr, xqufRenameExpr, xqufTransformExpr productions alongside the existing updateExpr rule. Both syntaxes are available in exprSingle. Legacy and XQUF sections are clearly delineated with comments and removal instructions.
XQueryTree.g Added new tree walker rules that instantiate XQUF expression classes alongside existing legacy rules; added %updating annotation support. Legacy and XQUF sections are clearly delineated with comments and removal instructions.

In-memory node mutation support

File Changes
memtree/DocumentImpl.java Added mutation methods: insertBefore, insertAfter, insertInto, insertIntoAsFirst, insertIntoAsLast, removeChild, replaceChild, replaceElementContent, renameNode, deepCopy, compact. The memtree flat-array structure required careful index management for structural modifications.
memtree/ElementImpl.java Added getFirstChildFor/getNextSiblingFor that skip deleted nodes; added renameNode
memtree/NodeImpl.java Added deletion marking (isDeleted/markDeleted), renameNode, getNextSiblingFor

Static type checking (XUST0001/XUST0002)

File Changes
Expression.java Added isUpdating() and isVacuous() methods for W3C updating/vacuous expression classification
ConditionalExpression.java Override isUpdating()/isVacuous() for if/then/else branches
TypeswitchExpression.java Override for typeswitch cases
SwitchExpression.java Override for switch cases
PathExpr.java Override with recursive step detection
SequenceConstructor.java Override for enclosed expressions
Various expression classes Added isUpdating() overrides where needed

XQueryContext / XQuery integration

File Changes
XQueryContext.java Added PendingUpdateList field; PUL accumulates during query execution; updated checkFragmentation import to PendingUpdateList
XQuery.java After query evaluation, applies PUL at snapshot boundary (deferred execution model)

Error codes

File Changes
ErrorCodes.java Added W3C XUDY/XUST/XUTY error codes (XUDY0009, 0014, 0015, 0016, 0017, 0021, 0023, 0024, 0027, 0029, 0030, 0031; XUST0001, 0002, 0028; XUTY0004-0013, 0022)

Tests

File Changes
XQUFBasicTest.java 85 JUnit tests covering all W3C XQUF expression types + 4 mutual exclusion checks
XQUFBenchmark.java Performance benchmarks for both XQUF and legacy update syntax (persistent operations) plus XQUF-only in-memory copy-modify benchmarks
bindingConflictXQUF.xqm XQUF (copy/modify/return) editions of the XUDY0023 namespace conflict tests, in a separate module from the legacy bindingConflict.xqm

All existing legacy update tests (UpdateInsertTest, UpdateReplaceTest, UpdateValueTest, etc.) are unchanged and continue to pass.

Spec Reference

XQTS Results

QT4 XQuery Update test sets (upd-*), run against the consolidated branch (2026-03-13):

Metric Score
Total tests 788 (excl. skipped)
Passed 691 (87.7%)
Failed 97
Non-schema passed 684/684 (100%)
Test sets at 100% 30 of 40

All 97 failures are XML Schema revalidation tests (out of scope — eXist-db does not support XSD revalidation strict or revalidation lax). The 77 revalidation tests span 4 Revalidation* test sets plus setToUntyped and NilUpdates. This matches BaseX's scope, which also only supports revalidation skip.

Non-schema compliance: 100% — every test that does not require XML Schema validation passes.

Test suite results

Module Tests Failures Errors Skipped
exist-core 6622 0 0 128
XQUFBasicTest 85 0 0 0

Benchmark Results

Run with mvn test -pl exist-core -Dtest=XQUFBenchmark -Dexist.run.benchmarks=true -Ddependency-check.skip=true

Persistent update operations — W3C XQUF syntax

All persistent operations use the PUL deferred execution model: updates accumulate during query evaluation, then are applied atomically in a single transaction at the snapshot boundary.

Operation N=10 N=50 N=200 Scaling
insert node ... into 1.6 ms 4.4 ms 12.2 ms ~linear
delete node 1.1 ms 3.3 ms 8.4 ms ~linear
replace value of node 2.3 ms 5.7 ms 17.5 ms ~linear
replace node 3.0 ms 7.6 ms 21.4 ms ~linear
rename node 14.1 ms 17.6 ms 28.3 ms sub-linear

Persistent update operations — Legacy syntax (deprecated)

Legacy operations apply immediately during query evaluation — each update is its own mini-transaction.

Operation N=10 N=50 N=200 Scaling
update insert ... into 3.9 ms 8.8 ms 23.4 ms ~linear
update delete 2.7 ms 4.8 ms 12.8 ms ~linear
update value ... with 6.5 ms 11.2 ms 26.6 ms ~linear
update replace ... with 2.8 ms 6.9 ms 22.0 ms ~linear
update rename ... as 2.0 ms 4.1 ms 12.3 ms ~linear

Comparison notes: XQUF insert/delete/replace node are faster than their legacy equivalents at all sizes, likely due to the PUL batching updates into a single transaction commit. XQUF rename has higher base cost (~14ms at N=10) due to namespace handling/NamePool overhead but scales sub-linearly. Legacy rename scales linearly but starts lower. replace value is comparable between the two systems.

In-memory copy-modify operations (XQUF-only)

These are entirely new — the legacy update syntax does not support in-memory node updates.

Operation N=10 N=50 N=200 Scaling
copy-modify single replaceValue 4.2 ms 5.9 ms 6.4 ms flat (copy-dominated)
copy-modify multi replaceValue 1.5 ms 1.8 ms 2.0 ms sub-linear
copy-modify insert + delete 2.1 ms 3.0 ms 4.5 ms sub-linear
copy-modify deep tree (3N items) 3.6 ms 4.6 ms 9.5 ms ~linear

Known Limitations

  1. Batch replaceNode on many siblings in same document: When replacing hundreds of sibling elements in the same persistent document within a single PUL, stale B-tree node references can occur. The XQueryUpdateTest.replace test is @Ignored with explanation. This is a deep B-tree storage issue, not a PUL logic issue.

  2. XML Schema revalidation: The W3C spec's revalidation mode (revalidation strict/lax/skip) is not implemented. eXist-db does not currently support XSD-based revalidation after updates.

  3. fn:put(): Implemented as a PUL primitive but limited to eXist-db's storage model.

  4. Legacy and XQUF syntax cannot be mixed in the same module: Both syntaxes are available, but using both in the same module raises XPST0003 at compile time. The legacy system applies updates immediately during evaluation while XQUF defers them to a snapshot boundary — mixing the two would produce undefined behavior. The mutual exclusion check is enforced during tree walking via XQueryContext.markLegacyUpdate()/markXQUFUpdate().

Credits

The namespace conflict detection approach (XUDY0021/0023/0024 via NamePool) was informed by BaseX's XQUF implementation.

Test Plan

  • 85 JUnit tests in XQUFBasicTest covering all W3C update expressions + mutual exclusion checks
  • All existing legacy update tests unchanged and passing
  • Parallel XQUF versions of bindingConflict.xqm namespace conflict tests
  • Full exist-core test suite: 6622 tests, 0 failures
  • QT4 XQTS update test sets: 691/788 (87.7%) — non-schema: 684/684 (100%)
  • Performance benchmark: 14 benchmarks (5 XQUF persistent, 5 legacy persistent, 4 XQUF in-memory) all passing
  • Manual testing of copy-modify with complex documents

CI Status

All checks green (unit tests, integration tests on macOS/ubuntu/windows, XQTS, container image, license check).

# Run new XQUF tests
JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn test -pl exist-core \
    -Dtest="org.exist.xquery.xquf.XQUFBasicTest" \
    -Ddependency-check.skip=true

# Run legacy update tests (unchanged)
JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn test -pl exist-core \
    -Dtest="org.exist.xquery.update.UpdateInsertTest,org.exist.xquery.update.UpdateReplaceTest,org.exist.xquery.update.UpdateValueTest,org.exist.xquery.update.IndexIntegrationTest,org.exist.xquery.update.UpdateInsertTriggersDefragTest" \
    -Ddependency-check.skip=true

# Run benchmark (XQUF + legacy)
JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn test -pl exist-core \
    -Dtest=XQUFBenchmark \
    -Dexist.run.benchmarks=true -Ddependency-check.skip=true

# Full exist-core test suite
JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn test -pl exist-core \
    -Ddependency-check.skip=true

Migration Guide

This guide covers how to migrate XQuery code from eXist-db's deprecated proprietary update syntax to the W3C standard syntax. Both syntaxes currently work, but the legacy syntax will be removed in a future release.

Conceptual Changes

Pending Update List (PUL) and Deferred Execution

The most significant architectural difference between the two syntaxes is how updates are executed:

  • Legacy behavior (immediate execution): Each update expression executes immediately, modifying the database as it is encountered during query evaluation. This allows freely mixing updates with reads in the same expression.
  • W3C behavior (deferred execution via PUL): Update expressions add "primitives" to a Pending Update List (PUL) during query evaluation. No database modification occurs until the entire query finishes, at which point the PUL is applied atomically in a single transaction. This is called the "snapshot boundary."

Practical impact: With W3C syntax, you can no longer observe the effect of an update within the same query that performs it. Updates are only visible after the query completes.

Separation of Updating and Non-Updating Expressions (XUST0001)

The W3C spec introduces a strict static type system that classifies expressions as either updating or non-updating. These cannot be mixed in certain contexts:

  • Comma expressions: insert node <x/> into /root, doc('/db/test.xml') is a static error (XUST0001) because it mixes an updating expression (insert) with a non-updating one (doc()).
  • FLWOR return clauses: The return clause must be entirely updating or entirely non-updating.
  • Element constructors: Updating expressions cannot appear inside element constructors like <result>{ insert node ... }</result>.

Updating Functions

Functions containing W3C updating expressions must be declared with the %updating annotation (W3C 3.0 syntax) or the updating keyword (W3C 1.0 backward-compatible syntax):

(: W3C 3.0 annotation syntax - preferred :)
declare %updating function local:add-child($parent as element()) {
    insert node <child/> into $parent
};

(: W3C 1.0 keyword syntax - also supported :)
declare updating function local:add-child($parent as element()) {
    insert node <child/> into $parent
};

Both forms are equivalent per the W3C XQuery Update Facility 3.0 spec.

Key rules:

  • XUST0001: Calling an updating function in a non-updating context is a static error.
  • XUST0002: The body of a declare %updating function must actually be an updating expression (or vacuous/empty).
  • XUST0028: An updating function must not declare a return type. (declare %updating function local:foo() as item()* { ... } is an error.)

Note: Functions using the legacy update syntax do not need the %updating annotation, since legacy expressions are not classified as "updating" in the W3C sense.

Copy-Modify Expressions (NEW)

The W3C spec adds copy ... modify ... return (also called "transform" expressions) for functional, non-destructive updates on in-memory copies of nodes:

copy $c := $node
modify (replace value of node $c/title with "New Title")
return $c

This deep-copies $node, applies updates to the copy, and returns the modified copy. The original node is unchanged. This works on both persistent and in-memory nodes and is the primary way to do "read-and-modify" in a single expression. There is no legacy equivalent.


Syntax Migration Reference

Operation Legacy (deprecated) W3C Standard
Insert child update insert <x/> into /root insert node <x/> into /root
Insert before update insert <x/> preceding /node insert node <x/> before /node
Insert after update insert <x/> following /node insert node <x/> after /node
Insert as first child insert node <x/> as first into /root
Insert as last child insert node <x/> as last into /root
Delete update delete /node delete node /node
Replace node update replace /node with <x/> replace node /node with <x/>
Replace value update value /node with 'text' replace value of node /node with 'text'
Rename update rename /node as 'newname' rename node /node as 'newname'

Insert attributes

Attributes can only be inserted into an element with W3C syntax, not before/after another attribute:

(: LEGACY - inserted relative to another attribute :)
update insert attribute id { 'abc' } preceding //item/@status

(: W3C - inserted into the parent element :)
insert node attribute id { 'abc' } into //item

Replace value semantics

W3C replace value of node atomizes the replacement content to a string. If you pass element nodes, they are atomized and joined with spaces:

(: LEGACY - could insert child elements :)
update value $node with (<a>1</a>, <b>2</b>)
(: Result: <node><a>1</a><b>2</b></node> :)

(: W3C - atomizes to string :)
replace value of node $node with (<a>1</a>, <b>2</b>)
(: Result: <node>1 2</node> :)

Common Migration Patterns

Pattern 1: Split update + read into separate queries

The most common migration pattern. Legacy code that combined updates with reads in a single expression must be split when using W3C syntax.

Legacy:

(: Works with legacy syntax - update is immediate :)
update insert <child/> into doc('/db/test.xml')/root,
doc('/db/test.xml')

W3C — Option A: Separate queries:

(: Query 1: perform the update :)
insert node <child/> into doc('/db/test.xml')/root

(: Query 2: read the result :)
doc('/db/test.xml')

W3C — Option B: copy-modify for in-memory result:

let $doc := doc('/db/test.xml')
return copy $c := $doc/root
modify (insert node <child/> into $c)
return $c

Pattern 2: Wrap updates in util:eval() as a last resort

util:eval() is an escape hatch that lets you encapsulate a W3C updating expression and execute it from a non-updating context. It works by running the update in a separate, independent query context — so the W3C static typing rules don't apply across the boundary. This is a last resort when the alternatives (separate queries, copy-modify, or %updating functions) aren't practical.

Common situations where util:eval() is needed:

  • XQSuite test functions that need to perform an update and read the result in the same function (test functions can't be declared %updating because they need to return assertion values)
  • let bindings where you want to sequence an update before a read within a single FLWOR expression
  • Trigger callbacks or other framework-managed functions whose signatures you don't control

Legacy:

declare
    %test:assertEquals('<root><child/></root>')
function xqu:root() {
    let $f  := xmldb:store('/db', 'xupdate.xml', <root/>)
    let $u  := update insert <child/> into doc($f)/root
    return doc($f)
};

W3C (using util:eval()):

declare
    %test:assertEquals('<root><child/></root>')
function xqu:root() {
    let $f  := xmldb:store('/db', 'xupdate.xml', <root/>)
    let $u  := util:eval("insert node <child/> into doc('" || $f || "')/root")
    return doc($f)
};

Note: Since util:eval() runs the string in a fresh query context, namespace declarations from the outer module are not inherited. You must declare namespaces inline (e.g., xmlns:mods='...' on element constructors).

Pattern 3: Updating functions in triggers/modules

Functions that perform W3C updates must be declared with %updating. If that's not practical (e.g., XQSuite test functions or trigger callbacks that can't change their signature), use util:eval().

W3C — Option A: util:eval():

declare function trigger:after-create-document($uri as xs:anyURI) {
    return util:eval("insert node <event .../> into doc('/db/triggers/events.xml')/events")
};

W3C — Option B: %updating annotation (if you control the function signature):

declare %updating function trigger:after-create-document($uri as xs:anyURI) {
    insert node <event .../> into doc('/db/triggers/events.xml')/events
};

Note: declare %updating function must not declare a return type (XUST0028).

Pattern 4: Batch updates in FLWOR expressions

FLWOR expressions that return W3C updating expressions work naturally — each iteration adds primitives to the PUL, and they're all applied atomically at the end:

for $item in doc('/db/data.xml')//item
return replace value of node $item/@status with 'processed'

However, you cannot observe intermediate results within the same query. All replacements happen after the query completes. (With legacy syntax, each iteration's update was visible to subsequent iterations.)

Pattern 5: Multiple updates on the same node

The W3C spec has conflict detection rules. Some operations that the legacy syntax silently allowed are now errors:

  • XUDY0017: Multiple replace value of node on the same target node in the same PUL.
  • XUDY0015: Multiple rename node on the same target node.
  • XUDY0016: Multiple replace node on the same target node.

If your legacy code performed multiple updates on the same node in a loop, you may need to restructure to update each node at most once per query.


Quick Checklist

  • Replace update insert with insert node
  • Replace update delete with delete node
  • Replace update replace with replace node ... with
  • Replace update value with replace value of node ... with
  • Replace update rename with rename node ... as
  • Replace preceding/following with before/after
  • Change attribute insertions from before/after @attr to into element
  • Split any expressions that mix updates with reads (XUST0001)
  • Add declare %updating function to functions containing updates, or wrap with util:eval()
  • Remove return types from updating function declarations (XUST0028)
  • Check for multiple updates to the same node (XUDY0015/0016/0017)
  • Review replace value of node for atomization behavior changes

joewiz and others added 5 commits March 13, 2026 19:28
Add ANTLR 2 grammar rules for the W3C XQuery Update Facility 3.0
(https://www.w3.org/TR/xquery-update-30/) syntax:

  - InsertExpr: insert node(s) into/as first/as last/before/after
  - DeleteExpr: delete node(s)
  - ReplaceExpr: replace (value of) node
  - RenameExpr: rename node as
  - TransformExpr: copy $var := expr modify expr return expr
  - FunctionDecl: updating keyword support

XQuery.g changes add lexer tokens and parser rules that produce
XQUF-specific AST nodes. XQueryTree.g changes walk those AST nodes
to instantiate the corresponding Java expression classes.

Mutual exclusion: the tree grammar detects conflicting use of W3C
XQUF syntax and eXist-db's legacy update extensions in the same
query, reporting XUST0002 at compile time.

Ref: W3C XQuery Update Facility 3.0, Sections 2.1-2.6

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the static typing rules required by the W3C XQuery Update
Facility 3.0 specification:

XUST0001 — updating expressions in non-updating context:
  - Add Expression.isUpdating() method with overrides in all
    expression subclasses to propagate the updating flag
  - XQuery.execute() checks that the top-level expression is not
    updating unless in an updating context

XUST0002 — non-updating expressions in updating context:
  - Add Expression.isVacuous() with recursive detection through
    TypeswitchExpression, SwitchExpression, SequenceConstructor,
    and PathExpr to identify vacuous expressions (empty sequence,
    error, ()) that are valid in both contexts

Expression infrastructure:
  - XQueryContext: add copy-namespaces preserve/inherit accessors,
    xqufEnabled flag, and legacy-vs-XQUF mutual exclusion tracking
  - FunctionSignature: add isUpdating flag for declaring updating
    functions
  - FunctionCall/UserDefinedFunction: propagate updating status
  - ErrorCodes: add XUST0001, XUST0002, XUDY0009, XUDY0014-0016,
    XUDY0021, XUDY0023-0024, XUDY0027, XUTY0004-0008, XUTY0010,
    XUTY0012-0013, XUTY0022, XQTY0153

Ref: W3C XQuery Update Facility 3.0, Sections 2.2, 3.1, 3.2

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add the core expression classes for the W3C XQuery Update Facility 3.0
(https://www.w3.org/TR/xquery-update-30/):

Expression classes (org.exist.xquery.xquf):
  - XQUFInsertExpr: insert into/as first/as last/before/after with
    target type validation (XUTY0004-0008) and compatibility checks
  - XQUFDeleteExpr: delete with node-type validation
  - XQUFReplaceNodeExpr: replace node with content type constraints
    (XUTY0010, XUTY0012-0013)
  - XQUFReplaceValueExpr: replace value of node
  - XQUFRenameExpr: rename with QName computation and validation
  - XQUFTransformExpr: copy-modify-return with deep copy that
    preserves complete document structure (all document-level
    children), copy-namespaces semantics (preserve/no-preserve,
    inherit/no-inherit), and namespace scope materialization
  - XQUFFnPut: fn:put() stub (XQUF Section 2.6)
  - UpdatePrimitive: typed container for pending update primitives

Pending Update List (PUL):
  - Five-phase application per W3C spec Section 3.3.2:
    Phase 1: inserts, renames, replaceValue (non-element)
    Phase 3: replaceNode
    Phase 4: replaceElementContent
    Phase 5: deletes (reverse document order)
  - In-memory application via memtree mutation methods
  - Persistent (stored) document application via existing eXist-db
    Modification/transaction infrastructure with broker locking
  - Conflict detection: XUDY0009 (duplicate targets), XUDY0014-0016
    (conflicting inserts/renames/replaces), XUDY0021/0023/0024
    (namespace conflict detection using NamePool approach, credit
    BaseX for the pattern)
  - Attribute replace: pre-captured original indices processed in
    descending order to handle rename+replace interactions and
    array-shift invalidation

Ref: W3C XQuery Update Facility 3.0, Sections 2.1-2.6, 3.3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend the eXist-db in-memory (memtree) document model to support
the mutation operations required by the W3C XQuery Update Facility.
The memtree uses a flat-array architecture where nodes are stored in
parallel arrays (nodeKind[], nodeName[], alpha[], alphaLen[], etc.)
with a next[] chain for sibling navigation.

DocumentImpl mutation methods:
  - insertChildren: insert nodes before/after/into elements using
    MemTreeBuilder serialization with next[] chain restitching
  - insertAttributes: add/replace attributes on elements
  - removeNode: soft-delete (nodeKind=-1) with subtree marking and
    predecessor-based next[] chain restitching
  - removeAttribute: shift attr arrays with alpha[] index fixup
  - replaceNode: remove target + insert replacement content
  - replaceValue: update text/comment/PI/attribute values in-place
  - renameNode/renameAttribute: change element/attribute QNames
  - replaceElementContent: clear children + insert text
  - mergeAdjacentTextNodes: W3C spec post-update normalization
  - compact: full tree rebuild via serialization/deserialization
  - copyNodeIntoDocument: namespace-aware deep copy with
    scope map for no-inherit materialization and no-preserve
    filtering
  - stripUnusedNamespacesInSubtree: no-preserve implementation
  - findAttribute: QName-based attribute lookup for PUL application

NodeImpl axis navigation fixes:
  - selectFollowing: fix document element to find document-level
    siblings (comments, PIs) after the element
  - selectPreceding/selectFollowing: skip soft-deleted nodes
    (nodeKind=-1) left by mergeAdjacentTextNodes

ElementImpl: add setNodeName for in-place rename support.
FunInScopePrefixes: respect inheritNamespaces context for in-memory
nodes (self-only when no-inherit).

Ref: W3C XQuery Update Facility 3.0, Section 3.3 (Update Routines)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add comprehensive test coverage for the W3C XQuery Update Facility
3.0 implementation:

XQUFBasicTest (94 tests):
  - Insert expressions: into, as first, as last, before, after,
    with attributes, multiple inserts, insert into empty elements
  - Delete expressions: elements, attributes, text, comments, PIs
  - Replace node: elements, attributes, swap, text nodes
  - Replace value: elements, attributes, text, comments, PIs
  - Rename: elements, attributes, with namespace handling
  - Copy-modify (transform): basic, nested, multiple variables,
    independent copies, persistent document sources
  - Static type checking: XUST0001 (updating in non-updating
    context), XUST0002 (legacy/XQUF mutual exclusion)
  - Dynamic errors: XUDY0009 (duplicate targets), XUDY0015
    (duplicate rename), XUDY0027 (target type), XUTY0004-0008
    (insert type constraints), XUTY0010/0012-0013 (replace type
    constraints)
  - Copy-namespaces: preserve/no-preserve, inherit/no-inherit
    with namespace propagation tests
  - Attribute replacement interactions: swap, rename+replace
  - Following/preceding axis after document-level mutations
  - Persistent document operations via XMLDB API

XQUFBenchmark: performance comparison of W3C XQUF vs eXist-db
legacy update extensions for insert, delete, replace, and rename.

XQTS QT4 results (non-schema): 684/684 (100%)
XQTS QT4 results (overall): 691/788 (87.7%)
  Remaining 97 failures are XML Schema revalidation (out of scope).

Ref: W3C XQuery Update Facility 3.0
Closes eXist-db#3634

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@joewiz joewiz force-pushed the feature/w3c-xquery-update-3.0-v2 branch from f449db1 to 7a856c4 Compare March 13, 2026 23:31
@joewiz joewiz added enhancement new features, suggestions, etc. xquery issue is related to xquery implementation labels Mar 13, 2026
joewiz and others added 2 commits March 14, 2026 02:24
Register XQUFFnPut.SIGNATURE in the fn namespace function registry
so that fn:put() is recognized as a built-in function. Without this
registration, queries using fn:put() fail with XPST0017 (function
not defined).

fn:put() adds a put primitive to the Pending Update List for
deferred document persistence per W3C XQUF Section 2.6.

Ref: W3C XQuery Update Facility 3.0, Section 2.6

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the applyPersistentPut method in PendingUpdateList, which was
previously a TODO stub (LOG.warn only). fn:put now stores the target
node as an XML document at the specified URI in the database.

Implementation:
- Parses target URI to collection path + document name
- Gets or creates the target collection
- Serializes the node to XML text
- Stores via broker.storeDocument()
- Error handling: FODC0002 for storage failures, FODC0005 for invalid URI

The fn:put PUL primitive was already correctly constructed by
XQUFFnPut.java and added to the PUL. Only the persistent application
step was missing.

XQUFBasicTest: 94/94 pass (no regressions).

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

joewiz commented Apr 6, 2026

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

Closing — superseded by #6214 (v2/w3c-xquery-update-3.0).

This work has been consolidated into a clean v2/ branch as part of the eXist-db 7.0 PR reorganization. The new PR includes all commits from this PR plus additional related work, with reviewer feedback incorporated where applicable. See the reviewer guide for the full context.

@joewiz joewiz closed this Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement new features, suggestions, etc. xquery issue is related to xquery implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] Implement XQuery Update Facility 3.0 updating/inserting/deleting comment()'s before the root element

1 participant