Maintaining Semantic Integrity

Triggers are the mechanism that is best suited for maintaining semantic integrity in a database (if we allow for a closed-world assumption). Triggers are well known from SQL. Most relational database management systems implement trigger mechanisms.

Let us assume that we have stored several album documents within our encyclopedia collection. In addition, we have stored several review documents that relate to some of those album documents. When we delete an album document, we want to make sure that the review elements relating to the deleted album document are also removed from the database.

We can achieve this by defining an onDelete trigger to the album node of the album document type.

This trigger is called immediately before the album node is deleted.

<xs:element name = "album" >
  <xs:annotation>
    <xs:appinfo>
      <tsd:elementInfo>
        <tsd:logical>
          <tsd:trigger>		  
            <tsd:onDelete type = action>
              encyclopedia_album.deleteRelatedReviews
            </tsd:onDelete>
          </tsd:trigger>	
        </tsd:logical>
      </tsd:elementInfo>
    </xs:appinfo>
  </xs:annotation>
  ...
</xs:element>

Sample trigger code is shown in the following example. The deleted album document node is passed to the method deleteRelatedReviews, which implements the trigger. This allows us to extract the album/@albumNo attribute value, and then to delete any review instances that specify this value in their review/album/@albumNo node. These cascading delete operations are performed via server extension callbacks.

package tamino.SXS.jazz.encyclopedia.album;
import com.softwareag.ino.sxs.ASXJBase;

public class encyclopedia_album extends ASXJBase {

  /**
   * The default constructor
   * (No other constructor allowed.)
   */
  public encyclopedia_album() {
  }
  /**
   * Delete all reviews relating to this album instance (used as trigger function)
   * @param object_id parameter
   * @param element_id parameter
   * @param document parameter
   */
  public void deleteRelatedReviews(
    StringBuffer collection,
    StringBuffer doctype,
    StringBuffer inoId,
    String document)
    throws java.lang.Exception {
    // We rely on the fact that albumNo is an attribute of the root element
    // (the official way would be to employ a parser)
    int aPos = document.indexOf("albumNo");
    if (aPos < 0)
      throw new Exception("No albumNo attribute");
    int qaPos = document.indexOf('"', aPos + 8);
    int qePos = document.indexOf('"', qaPos + 1);
    String albumNo = document.substring(qaPos, qePos + 1);

    // create Delete query string for the callback
    String xmlQuery = "review[album/@albumNo=" + albumNo + "]";
    // create new string buffer for call back results
    StringBuffer response = new StringBuffer();
    // callback: delete all documents matching this query
    int ret = SxsXMLDelete("encyclopedia", xmlQuery, response);
    if (ret != 0)
      processError();
  }

  // Here we dive deep into the rather complex error 
  // handling for server extensions callbacks.
  private void processError() throws Exception {

    // identify the callback error
    int msgNo = SxsGetMsgNo();
    switch (msgNo) {
      case INO_ERROR :
        StringBuffer msgBuf = new StringBuffer();
        // Get the Tamino Server message number
        int inoMsgNo = SxsGetInoMsgNo();
        // Get the Tamino Server messageline
        int ret = SxsXMLGetMessage(msgBuf);
        throw (new Exception("INO_ERROR: " + inoMsgNo + " " + msgBuf));
      default :
        // Get the SXS message corresponding to callback error
        String msg = SxsGetMsgText();
        throw (new Exception("SXS Error: " + msgNo + " " + msg));
    }
  }
}

Note that triggers can be cascaded. We could, for example, equip review documents with an onDelete trigger that removes any critic instances that are only referred to by the deleted review instance. The deletion of an album document could thus cause the deletion of a critic document. This is conceptually debatable: both reviews and critics are first class business objects, so their existence should not depend on other business objects.