Sedna LogoBackground Top
 
Home  |  Getting Started  |  Documentation  |  Demo  |  Download  |  Support 

2.6 XQuery Triggers

XQuery triggers support in Sedna is provided as an XQuery extension. To create a trigger into the Sedna database you have to issue the following CREATE TRIGGER statement:

CREATE TRIGGER trigger-name  
( BEFORE | AFTER ) (INSERT | DELETE | REPLACE)  
ON path  
( FOR EACH NODE | FOR EACH STATEMENT )  
DO {  
  Update-statement ($NEW, $OLD,$WHERE);  
            . . .  
  Update-statement ($NEW, $OLD,$WHERE);  
  XQuery-statement ($NEW, $OLD, $WHERE);  
}

The DROP TRIGGER statement drops the trigger with the name which is the result of the trigger-name-expression:

DROP TRIGGER trigger-name-expression

Triggers can be defined to execute either before or after any INSERT, DELETE or REPLACE operation, either once per modified node (node-level triggers), or once per XQuery statement (statement-level triggers). If a trigger event occurs, the trigger’s action is called at the appropriate time to handle the event.

Create Trigger Parameters:

  • trigger-name is the unique per database trigger name.
  • ON path is XPath expression without any filter expression (predicates) that identifies the nodes on which the trigger is set. That means that the trigger fires when corresponding modification (insertion, deletion or replacement) of those nodes occurs. This XPath expression is prohibited to have predicates and parent axes.
  • FOR EACH NODE/FOR EACH STATEMENT: these key words mean the trigger created is a node-level or statement-level trigger. With a node-level trigger, the trigger action is invoked once for each node that is affected by the update statement that fired the trigger. In contrast, a statement-level trigger is invoked only once when an appropriate statement is executed, regardless of the number of nodes affected by that statement.
  • BEFORE/AFTER: triggers are also classified as before-triggers and after-triggers. BEFORE keyword in CREATE TRIGGER statement means the trigger created is before-trigger; AFTER keyword means the trigger created is after-trigger. Statement-level-before triggers fire before the statement starts to do anything, while statement-level-after triggers fire at the very end of the statement. Node-level-before triggers fire immediately before a particular node is operated on, while node-level-after triggers fire immediately after the node is operated on (but before any statement-level-after trigger).
  • DO: trigger action is specified in braces {} after the DO key word. Action contains zero or more update statements and an XQuery query. It is a mandatory requirement that node-level trigger action ends with an XQuery query, while this is optional for actions of statement-level triggers. It is prohibited to use prolog in statements of the trigger action.
  • Transition variables $NEW, $OLD and $WHERE are defined for each node-level trigger firing and can be used in each statement of the trigger action. These tree variables are defined as follows:
    • For INSERT: $NEW is the node being inserted; $OLD is undefined; $WHERE is the parent node in case in insert-into statement and sibling node in case of insert-preceding and insert-following statements;
    • For DELETE: $NEW is undefined; $OLD is the node being deleted; $WHERE is the parent of the deleted node;
    • For REPLACE: $NEW is the node being inserted during the replacement; $OLD is the node being replaced; $WHERE is the parent of the replaced node.

    You cannot use transition variables in statement-level triggers.

XQuery statement in the trigger action of a node-level trigger can return a node to the calling executor, if they choose. A node-level-trigger fired before an operation has the following choices:

  • It can return empty sequence to skip the operation for the current node. This instructs the executor to not perform the node-level operation that invoked the trigger (the insertion or replacement of a particular node).
  • For node-level INSERT triggers only, the returned node becomes the node that will be inserted. This allows the trigger to modify the node being inserted.
  • A node-level before trigger that does not intend to cause either of these behaviors must be careful to return as its result the same node that was passed in (that is, the $NEW node for INSERT and REPLACE triggers. For DELETE triggers its returned value is ignored in all cases except it is an empty sequence).

The trigger action return value is ignored for node-level triggers fired after an operation, and for all statement-level triggers, and so they may as well return empty sequence.

If more than one trigger is defined for the same event on the same document, the triggers will be fired in alphabetical order by trigger name. In the case of before triggers, the possibly-modified node returned by each trigger becomes the input to the next trigger. If any before trigger returns empty sequence, the operation is abandoned for that node and subsequent triggers are not fired.

Typically, node-level-before triggers are used for checking or modifying the data that will be inserted or updated. For example, a before trigger might be used to insert the current time node as a child of the inserting node, or to check that two descendants of the inserting node are consistent. Node-level-after triggers are most sensibly used to propagate the updates to other documents, or make consistency checks against other documents. The reason for this division of labor is that an after-trigger can be certain it is seeing the final value of the node, while a before-trigger cannot; there might be other before triggers firing after it. When designing your trigger-application note, that node-level triggers are typically cheaper than statement-level ones.

If a trigger function executes update-statement then these commands may fire other triggers again (cascading triggers). Currently trigger cascading level in Sedna is limited to 10.

Note 6 Currently is it prohibited in a trigger action to update the same document or collection that is being updated by the outer update statement that has fired this trigger.






update: update: update:
INSERT DELETE REPLACE








trigger event:trigger path trigger path
INSERT >= >=
update path update path




trigger event: trigger pathtrigger path
DELETE >= >=
update pathupdate path




trigger event: trigger path
REPLACE >=
update path





Figure 1: Update and trigger path lengths needed for trigger firing


Note also that hierarchy of the XML data sometimes can affect the trigger firing in a complicated way. For example, if a node is deleted with all its descendant subtree, then a DELETE-trigger set on the descendants of the deleting node is fired. In this situation length of trigger path >= length of update path. In general, triggers fire according to the table in figure 1.

  2.6.1 Trigger Examples
2.6.1 Trigger Examples

The following trigger is set on insertion of person nodes. When some person node is inserted, the trigger analyzes its content and modifies it in the following way. If the person is under 14 years old, the trigger inserts additional child node age-group with the text value ’infant’: if the person is older than 14 years old - the trigger inserts age-group node with value ’adult’:

CREATE TRIGGER "tr1"  
BEFORE INSERT  
ON doc("auction")/site//person  
FOR EACH NODE  
DO {  
  if($NEW/age < 14)  
  then  
    <person>{attribute id {$NEW/@id}}  
            {$NEW/*}  
              <age-group>infant</age-group>  
    </person>  
    else  
    <person>{attribute id {$NEW/@id}}  
            {$NEW/*}  
            <age-group>adult</age-group>  
    </person>;  
}

The following trigger tr2 prohibits (throws exception) stake increase if the person has already more than 3 open auctions:

CREATE TRIGGER "tr2"  
BEFORE INSERT  
ON doc("auction")/site/open_auctions/open_auction/bidder  
FOR EACH NODE  
DO {  
 if(($NEW/increase > 10.5) and  
  (count($WHERE/../open_auction  
    [bidder/personref/@person=$NEW/personref/@person]) > 3))  
 then error(xs:QName("tr2"),"The increase is prohibited")  
 else ($NEW);  
}

The following trigger tr3 cancels person node deletion if there are any open auctions referenced by this person:

CREATE TRIGGER "tr3"  
BEFORE DELETE  
ON doc("auction")/site//person  
FOR EACH NODE  
DO {  
  if(exists(  
    $WHERE//open_auction/bidder/personref/@person=$OLD/@id))  
  then ()  
  else $OLD;  
}

The next statement-level trigger tr4 maintains statistics in the document named stat. When this trigger is fired, the update operation is completed - that gives the possibility to make aggregative checks on the updated data. After deletion of any node in the auction document, the trigger refreshes statistics in stat and throws exception if there are more than 50 persons left:

CREATE TRIGGER "tr4"  
AFTER DELETE  
ON doc("auction")//*  
FOR EACH STATEMENT  
DO {  
  UPDATE replace $b in doc("stat")/stat with  
  <stat>  
    <open_auctions>  
      {count(doc("auction")//open_auction)}  
    </open_auctions>  
    <closed_auctions>  
      {count(doc("auction")//closed_auction)}  
    </closed_auctions>  
    <persons>  
      {count(doc("auction")//person)}  
    </persons>  
  </stat>;  
 
  UPDATE insert  
  if(count(doc("auction")//person) < 10)  
  then <warning>  
         "Critical number of person left in the auction"  
       </warning>  
  else ()  
  into doc("stat")/stat;  
}