In Tamino XQuery you can perform the following elementary update
operations on the node level: inserting, deleting, replacing and renaming. All
update operations follow the syntactic pattern that they begin with the keyword
update
. You can either specify directly one of the update
operations using insert
, delete
,
replace
, rename
or you can construct more complex
expressions by using a special form of the FLWOR expression that is only used
for update operations. In general, all update operations have to result in
well-formed documents.
For inserting nodes you have to specify two expressions. The first expression represents the node(s) to be inserted and the second expression determines the update node, namely the position in all matching documents at which the insert operation should take place. As a result, the documents will contain the additional element or attribute nodes inserted at the update node. This is done differently for element and attribute nodes.
Consider the following query expression that extends our current bibliography:
update insert <book year="2001"> <title>XML Schema Part 0: Primer</title> <editor> <last>Fallside</last> <first>David C.</first> <affiliation>IBM</affiliation> </editor> <publisher>World Wide Web Consortium</publisher> <price>0.00</price> </book> into input()/bib
This query inserts the book
element as last
child element of each bib
element. Using the keyword
into
always tries to insert an element as the last child element
of the update node. If the update node has not yet any child elements, the
elements to insert will then be its first child elements.
Apart from into
there are two other keywords that you can
use when inserting element nodes. Using preceding
the element
nodes will be inserted as preceding siblings to the update node. Using
following
the element nodes will be inserted as siblings following
the update node.
Attributes are always associated with an element and there is no order
on an element's attributes defined. This is why you can only use
into
to insert attribute nodes into a specified element:
update insert attribute edition {"1"} into input()/bib/book[title = "TCP/IP Illustrated"]
This query uses a computed attribute constructor to insert an attribute
node edition
into each
book
element whose title is "TCP/IP
Illustrated", marking it as the first edition (see
ElementConstructor for
information about computed constructors). If there is not yet such an attribute
in that book
element, then it is inserted as a new
attribute provided that the resulting document is still valid according to the
schema (see the section Schema
Conformance for details). If there is already an attribute
edition
, then the operation is rejected and its
content will not be overwritten. In this case, you can replace the contents of
the node by using the replace
expression.
You can remove nodes by using the update delete
expression.
This is not limited to element nodes:
update delete input()/reviews/entry/review/text() update delete input()/bib/book/author[2]
The first query effectively blanks all review texts: from all
review
elements the content, which is retrieved by
the node test text()
, is deleted. However, the
review
elements themselves are retained. The second
query expression deletes all author
element nodes
that are the second author
child nodes of
book
elements.
The above queries have no impact on the validity of documents according to the schema. However, if you want to delete necessary element nodes, this will usually fail due to the required validity, see the section Schema Conformance. But you can delete nodes such as comment nodes that are siblings of the root element.
Furthermore, you can delete whole documents as in the following query:
update for $a in input()/reviews do ( delete root($a) )
This query deletes all reviews
documents,
since the root function returns the document nodes of all reviews
documents in the current collection.
For element and attribute nodes as well as for processing instruction
nodes you can change the name of the node by using the rename
operation.
update rename input()/bib/book/@year as jahr
This query translates the year
attribute name
of book
elements into German. All
rename
operations on elements and attributes have direct impact on
the schema validity of documents. See the section
Schema Conformance for
details.
The syntax for replacing nodes is similar to the insert
expression: The first expression determines the update node while the second
expression specifies the replacing node(s). You can replace an element node as
in the following query:
update replace input()/bib/book/title[. = "TCP/IP Illustrated" ] with <title>TCP/IP Illustrated I. The Protocols</title>
This replaces the title
element with the
content "TCP/IP Illustrated" with a
title
element that has the contents
"TCP/IP Illustrated I. The Protocols". You can also
replace the text node of the element directly:
update replace input()/bib/book/title[. = "TCP/IP Illustrated" ]/text() with text{"TCP/IP Illustrated I. The Protocols"}
As the replacement can be any valid XQuery expression, you can also perform replacements with nodes containing other nodes such as:
update replace input()/bib/book[title = "TCP/IP Illustrated" ] with <book year="1995"> <title>TCP/IP Illustrated II. The Implementation</title> <author><last>Stevens</last><first>W.</first></author> <author><last>Wright</last><first>G.</first></author> <publisher>Addison-Wesley</publisher> <price>67.99</price> </book>
This query completely replaces the book
with
the title "TCP/IP Illustrated". You can also replace
attributes:
update replace input()/bib/book[title = "TCP/IP Illustrated"]/@year with attribute year {"2003"}
Anticipating a new edition of this book, the year
attribute
is replaced with an attribute of the same name and the contents
"2003", using a computed attribute constructor.
There is a variant of the FLWOR expression that allows you to use the flexibility of FLWOR expressions with update operations. Principally you could rewrite all of the update queries shown above as FLWU expressions. The query
update delete input()/bib/book/author[2]
is equivalent to the following query using a FLWU expression:
update for $a in input()/bib/book do delete $a/author[2]
There are two differences to a regular FLWOR expression: The keyword
update
always appears in front of the for
clause. The
return
clause is replaced with a do
clause that is
followed by one or more update expressions. Using a FLWU expression always
binds at least one variable, which allows query expressions that are not
possible without. Consider the following query using the patient database:
update for $a in input()//doctor let $b := $a/@pager where starts-with($b, "3") do replace $b with attribute pager { string-join(("11", $b), "-") }
The numbers of all doctor's pagers that start with
"3" are prepended with
"11-". The for
clause creates tuples
with doctor
elements somewhere and binds them to variable
$a
. For each of the tuples a variable binding for $b
is added containing the value of the attribute
pager
. The tuples are then restricted to those
that meet the condition that the value of $b
(the pager number)
starts with the value "3". For these tuples the
contents of the pager
attribute is replaced by
using a computed attribute constructor that constructs the attribute
pager
with the concatenation of the strings
"11-" and the previous pager number. With the sample
documents atkins.xml
and bloggs.xml
stored, one pager number is affected ("342") and
will thus be changed to "11-342" in two tuples
found.
In contrast to other query operations it is the distinguishing property of update operations that they modify XML documents. This can lead to problems. Consider the following query:
update delete input()/bib//affiliation
Submitting this query proves not to be successful:
To understand why this operation has failed, the first step is to look
up the explanation to message
INOXDE7730 which says: "A sequence does not
contain all required elements. In particular, the sequence ends without
containing all required elements." The information which elements are
required at which place is in the schema
definition and that states that inside an
editor
element an
affiliation
element is required as last child
element and may not be omitted. The query would thus result in a document
instance that is not valid according to the schema and this is why it is
rejected by Tamino.
In general, documents must be valid according to the schema definition. This also includes documents on which an update operation has been performed. Let us reconsider a query from the section First Steps:
update for $a in input()/bib/book where $a/title = "The Economics of Technology and Content for Digital TV" do ( insert <affiliation>CITI</affiliation> following $a/author/first rename $a/author as editor )
In this FLWU expression there are two update operations: an insert
operation and a rename operation. Taken for themselves, both update operations
would fail because the document gets invalid: an
author
element may not have an
affiliation
element as a child, and an
author
element cannot be simply renamed to
editor
because that element requires an
affiliation
element as last child element. However,
in this query both update operations are enclosed in the do
clause
and the operation succeeds. Why?
If you have more than one update operation in a single query expression,
the order in which the update operations are performed is not relevant. For all
tuples that remain after applying the where
clause, both
update operations are performed resulting in temporary documents. If all these
temporary documents conform to the schema definition, the operation as a whole
succeeds. If at least one of the documents is no longer valid, the operation
fails.
There are situations in which you want to perform update operations in such a way that they violate the schema. As a resort, you can modify the validation mode in the schema description. The Tamino Schema Editor normally enforces validation so that you will find the following information for the document element:
<tsd:logical> <tsd:content>closed</tsd:content> </tsd:logical>
Closed content means that validation is strict. To make it lax, you need to change the schema description and update it into the database. See the section Open Content vs. Closed Content Validation for details.
FLWU expressions allow several elementary update operations. If you specify more than one update operation, the order in which these operations are performed does not play a role. However, there are some situations that can lead to the following conflicts:
The result of the elementary update operations is ambiguous. This can have one of the following reasons:
a) |
The result of the elementary operations depends on the order in which they are performed (they are not commutative). |
b) |
Inserting more than one attribute with the same name into an element node or inserting an attribute that already exists. |
c) |
Inserting into the position preceding or following the update
node if more than one |
An elementary update operation is performed that has no effect on the result because of other elementary update operations. This can happen if an elementary update operation affects a node that may no longer exist by the time the operation is performed.
Note:
In any of these conflict cases, the update operation is rejected by
Tamino.
The following tables summarize the possible conflicts. The first table
shows the conflicts for operations on one element node, the other two are
subsets of the first table, since not all operations can be performed on any
kind of node. As an example, if you use a replace
operation and an
insert preceding
operation on the same element node, conflict 2
arises. If you try to delete and replace the same comment node in a single
update operation, conflicts 1a and 2 arise.
Operations on one element node | delete |
replace |
rename |
insert attribute |
insert into |
insert preceding |
insert following |
---|---|---|---|---|---|---|---|
delete |
2 | 1a and 2 | 2 | 2 | 2 | 2 | 2 |
replace |
1a and 2 | 2 | 2 | 2 | 2 | 2 | |
rename |
1a | — | — | — | — | ||
insert
attribute |
1b | — | — | — | |||
insert into |
1a | — | — | ||||
insert
preceding |
1c | — | |||||
insert
following |
1c |
Operations on one attribute or PI node | delete |
replace |
rename |
---|---|---|---|
delete |
2 | 1a and 2 | 2 |
replace |
1a and 2 | 2 | |
rename |
1a |
Operations on one node of some other kind | delete |
replace |
---|---|---|
delete |
2 | 1a and 2 |
replace |
1a and 2 |
1a |
Two update for $a in input()/patient where $a/name/surname = "Atkins" do ( insert <middlename>J.</middlename> into $a/name insert <title>Prof.</title> into $a/name ) Although the schema allows the elements
update for $a in input()/patient where $a/name/surname = "Atkins" do insert <middlename>J.</middlename> into $a/name This inserts the update for $a in input()/patient where $a/name/surname = "Atkins" do insert <title>Prof.</title> into $a/name And this second FLWU expression then inserts the
Note that this could also be performed in a single operation, when the content is declared as a sequence: update for $a in input()/patient let $i:= (<middlename>J.</middlename>,<title>Prof.</title>) where $a/name/surname = "Atkins" do ( insert $i into $a/name ) |
1b |
Provided that the update for $a in input()/patient where $a/name/surname = "Bloggs" do ( insert attribute brand {"Somnex"} into $a//type insert attribute brand {"Cardiovelocimil"} into $a//type ) This conflict cannot be resolved: the operation fails with the
message INOXQE
6450. This query also fails, if there already existed an element
|
1c |
This query tries to replace the |
update for $a in input()/bib/book let $title := $a/title where $title = "TCP/IP Illustrated" do ( replace $title/text() with string-join(($title/text(), "I. The Protocols"), " ") insert <book year="1995"> <title>TCP/IP Illustrated II. The Implementation</title> <author><last>Stevens</last><first>W.</first></author> <author><last>Wright</last><first>G.</first></author> <publisher>Addison-Wesley</publisher> <price>67.99</price> </book> following $a insert <book year="1996"> <title>TCP/IP Illustrated III.</title> <author><last>Stevens</last><first>W.</first></author> <publisher>Addison-Wesley</publisher> <price>49.95</price> </book> following $a ) The update nodes consist of
In this query two As a solution, you can define a sequence to be inserted: update for $a in input()/bib/book let $title := $a/title let $i := (<book year="1995"> <title>TCP/IP Illustrated II. The Implementation</title> <author><last>Stevens</last><first>W.</first></author> <publisher>Addison-Wesley</publisher> <price>67.99</price> </book>, <book year="1996"> <title>TCP/IP Illustrated III.</title> <author><last>Stevens</last><first>W.</first></author> <publisher>Addison-Wesley</publisher> <price>49.95</price> </book>) where $title = "TCP/IP Illustrated" do ( replace $title/text() with string-join(($title/text(), "I. The Protocols"), " ") insert $i following $a ) |
|
2 |
Two delete operations on the same node: update for $a in input()//doctor do ( delete $a delete $a) Two update for $a in input()//doctor do ( rename $a as surgeon rename $a as dentist ) These conflicts cannot be resolved: both operations fail with the message INOXQE 6451. |
Apart from any conflicts, Tamino checks for each update operation whether the resulting documents may be written in that form by the user. If permission is not granted by Tamino, the operation is rejected.