|
Home | Getting Started | Documentation | Demo | Download | Support |
2.6 XQuery TriggersXQuery 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:
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:
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.
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 ExamplesThe 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; }
|