Release 10.1A: OpenEdge Development:
Progress Dynamics Basic Development

> ## Server-side validation

> Additional validation procedure hooks are designed to be used on the server, where there is free access to the database. This section discusses these entry points. For each of these procedures, remember that the client-side validation entry points described above apply only to a single SDO record. If multiple records are updated, then the client-side validation procedures will be called a number of times. By contrast, the first set of server-side, transaction-level validation routines described below could potentially apply to multiple record updates, and you should write them as `FOR` `EACH` blocks on the `RowObjUpd` table. As a result, when using the procedures defined below, you must write not only a `FOR` `EACH` loop on the `RowObjUpd` table, but also (if the logic applies only to specific operations such as `Add`, `Copy`, `Update`, or `Delete`) a check on each record within the loop that examines the value of the temp-table field `RowObjUpd.RowMod`. The field can have a value of `A` for `Add`, `C` for `Copy`, `D` for `Delete`, `U` for an `Updated` row, and ‘’ (blank) for the before image of an updated row (the field values as read from the database).

> In the case of an update, another standard `RowObjUpd` field, `RowNum`, holds a unique row number for each update. The updated row with `RowMod` `=` `‘U’` and the before image of the same row with `RowMod` `=` `‘’` will have the same `RowNum` field. Thus, a block of code that must look at both before and after values will generally be of the form:

>     DEFINE BUFFER OldRowObj FOR RowObjUpd.
>     FOR EACH RowObjUpd WHERE RowMod = ‘U’:
>       FIND OldRowObj WHERE RowObjUpd.RowNum = OldRowObj.RowNum AND
>         OldRowObj.RowMod = ‘’.
>       /* Now you can compare values in OldRowObj and RowObjUpd. */
>     END.

> Because of the nature of this SDO-specific logic that you must add to the validation, an alternative set of procedure hooks has been defined for Progress Dynamics to handle updates a row at a time, and with buffer names based on the table names. These alternative hooks are discussed in the "New Progress Dynamics validation procedures" section.

> ##### preTransactionValidate

> `preTransactionValidate` always executes on the server, immediately prior to the commencement of a transaction. As such, it can freely read the database, but should do so `NO-LOCK`. To maximize performance, `preTransactionValidate` should be restricted to the type of referential integrity check that can be implemented as a simple `CAN-FIND` statement. For example:

>     PROCEDURE preTransactionValidate:
>       FOR EACH RowObjUpd:
>         IF NOT CAN-FIND(…) THEN RETURN "…".
>     END PROCEDURE.

> Within this loop, use the `RowObjUpd.RowMod` field to determine the type of change for each row (`‘A’`dd, `‘C’`opy, `‘U’`pdate, or `‘D’elete`).

> Within `preTransactionValidate,` you can physically alter the values that will be written back to the database, if so desired. That is, any changes made to the records in the temp-table will be seen by later procedures within the transaction. Generally, `preTransactionValidate` is appropriate for code that makes changes to the updated records, and for code that must do comparisons with existing records in the database, but without making changes to them.

> Keep in mind that you do not want to place code here that might need to look at updated and modified records in one table. That is, some records are in the `RowObjUpd` table as updates and some are unmodified records in the database. For example, if you must total the `OrderLine` records of an `Order` in a validation procedure for the `OrderLine` table, you do not want to total records in the update temp-table and records in the database together. This will complicate your logic unnecessarily. Such logic should be left to the end of the transaction so that all the records are in the database together.

> Also, you also do not want to put logic here that does any other database updates. The transaction block is not yet open, and those updates would not be undone if the transaction fails. For example, if the validation logic in an `OrderLine` SDO must total all the `OrderLine` records for an `Order` and adjust the `Balance` of the associated `Customer` accordingly, and compare that against the `CreditLimit`, this validation should not be done in `preTransactionValidate`.

> ##### beginTransactionValidate

> The `beginTransactionValidate` procedure executes immediately after the start of the transaction, but before any database updates have been performed. There is little to do here except perhaps to gain an `EXCLUSIVE` lock on a parent table to prevent other user sessions from updating the same records (for example, in an `OrderLine` SDO, lock the `Order` table). In addition, if it is important to do some other validation before anything is actually written to the database, you should do that here.

> For example, if you want to assure that no other user will attempt to make changes to an order (an `Order` record and its `OrderLine` records) while you are updating any part of the order, you can read the `Order` record with an `EXCLUSIVE-LOCK` at the beginning of the transaction in the `OrderLine` object. Then, allow the transaction to make changes to one or more `OrderLine` records.

> ##### endTransactionValidate

> After all updates have been written to the database, but before the transaction is committed, `endTransactionValidate` is called. This is the ideal place to perform database cascade operations.

> One example: Now that all `OrderLine` records have been updated, recalculate the total amount owing. If done in the write trigger, then this behavior would execute once for every order line. By placing it in `endTransactionValidate`, the logic is performed once only, whether one, some, or all of the order lines were updated.

> Another example: On delete of a specific entity you might want to conditionally enforce cascade deletion of child records. It is not possible to put conditional cascade deletion into `Delete` triggers.

> ##### postTransactionValidate

> The `postTransactionValidate` procedure executes once the transaction has been committed, and only if the commit was itself successful. This could be a useful place to write log records, if required. Log records are often written from `Write` triggers and require the triggers to execute within the context of a specific user. If the log were to be written to a database, then `postTransactionValidate` would have to open a transaction of its own and manage that carefully.

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095