Performing Update Operations

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.


Inserting Nodes

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.

Inserting Element 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.

Inserting Attribute Nodes

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.

Deleting Nodes

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.

Renaming Nodes

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.

Replacing Nodes

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.

Using FLWU Expressions

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.

Schema Conformance

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:

Tamino Response to Non-Permissible Update Operation

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.

Conflicts

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:

  1. 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 insert operation acts on the same node (not commutative).

  2. 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

Examples

1a

Two insert into operations on the same node:

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 middlename and title to be inserted as child elements of name, the result of the operation depends on the order of the elementary update operations. In this case you can use two successive elementary update operations as a workaround:

update for $a in input()/patient
where $a/name/surname = "Atkins"
do insert <middlename>J.</middlename> into $a/name

This inserts the middlename as new child element of name, in the order that is prescribed by the schema.

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 title element.

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 patient nodes that are retained after processing the where clause do not contain a type element with the attribute brand:

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 $a//type with an attribute brand.

1c

This query tries to replace the book with the title "TCP/IP Illustrated" and to add the other entries for the complete three-volume set:

 
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 book elements that have a child element title with the content "TCP/IP Illustrated". Then three update operations are performed:

  1. replace
    For all books found, the contents of the title element is replaced with the previous contents concatenated with the string "I. The Protocols" using a blank as separator.

  2. Two insert Operations
    For all books found, the two specified XML fragments are inserted as sibling elements following the book element.

In this query two insert following operations act on the same update node. The result is ambiguous regarding the order of the inserted elements (not commutative). Volume 2 would be inserted as next sibling to volume 1, and then volume 3 could either be inserted as next sibling to volume 1 (thus shifting volume 2 to the next position), or it could be inserted after the just inserted volume 2.

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 rename operations on the same node:

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.

Security

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.