Update Primitives
The XQuery Update Facility (abbreviated as XQUF) provides five basic operations acting upon XML nodes:
insert one or several nodes inside/after/before a specified node
delete one or several nodes
replace a node (and all its descendants if it is an element) by a sequence of nodes.
replace the contents (children) of a node with a sequence of nodes, or the value of a node with a string value.
rename a node (applicable to elements, attributes and processing instructions) without affecting its contents or attributes.
Combination of update primitives with the base language
Typically, we use some plain query to select the node(s) we want to update, then we apply update operations on those nodes. This is similar to the SQL UPDATE... WHERE... instruction.
Example 1: in a document doc.xml
, rename all elements children of a CATEGORY as CATEGORY_NAME
:
for $name in doc("doc.xml")//CATEGORY/NAME (: selection :) return rename node $name as CATEGORY_NAME (: update :)
Example 2: for all BOOK elements which have an attribute Id, replace that attribute with a child ID in first position:
for $idattr in doc("data.xml")//BOOK/@Id (: selection :) return ( delete node $idattr, (: update 1 :) insert node <ID>{string($idattr)}</ID> (: update 2 :) as first into $idattr/.. )
With the latter script the following fragment
<BOOK Id="0025">some content</BOOK>
would be modified into:
<BOOK><ID>0025</ID>some content</BOOK>
Note
In the second example, it is completely irrelevant whether the delete is written after or before the insert node. This surprising property of XQUF is explained below.
There are some restrictions in the way the 5 updating operations can mix with the base XQuery language. XQUF makes a distinction between Updating Expressions (which encompass update primitives) and non-updating expressions. Updating Expressions cannot appear anywhere. This topic will be explained in more detail.
Processing models
There are two main ways of using the update primitives:
Direct update of an XML database:
In the examples in previous topic, nodes belonging to a database are selected then updated.
Note
The XQUF notion of a database is very general: it means any collection of XML documents or well-formed fragments (trees).
XQuery Update does not define precisely the protocol by which updating operations are applied to a database. This is left to implementations. For example transaction and isolation issues are not addressed by the specifications.
It is simply assumed that updates are applied to the database when the execution of a script completes. The language is designed in such a way that semantics of the "apply-updates" operation are precisely defined, yet as much space as possible is left for optimization by database implementations.
Points to be noticed:
Updates are not applied immediately as the updating expression executes. Instead they are accumulated into a "Pending Update List". At some point at the end of the execution, Pending Updates are applied all at once, and the database is updated atomically.
A noticeable consequence is that updates are not visible during the script execution, but only after. This can be fairly off-putting for a developer. It also has a definite influence on programming style. We will see later examples of this effect and how to cope with it.
The same expression can update several documents at once. The examples above could be applied to any collection of documents instead of the single document doc.xml. Example:
for $name in collection("/allbooks")//CATEGORY/NAME return rename node $name as CATEGORY_NAME
Transforms without side effects:
The XQUF has a supplementary operation called transform which updates a copy of an existing node, without modifying the original, and returns the transformed tree.
The following example produces a transformed version of doc.xml without actually touching the original document:
copy $d := doc("doc.xml") modify ( for $n in $d//CATEGORY/NAME return rename node $n as CATEGORY_NAME ) return $d
Notice that within the modify clause, XQUF forbids modifying the original version of copied trees (here the document doc.xml itself); only the copied trees can be modified.
The following expression would cause an error:copy $d := doc("doc.xml") modify ( for $n in doc("doc.xml")//CATEGORY/NAME(: *** wrong *** :) return rename node $n as CATEGORY_NAME ) return $d