Release 10.1A: OpenEdge Development:
Progress 4GL Handbook

> ## Subtransactions

> You separated the Order update and the OrderLine updates out into two separate transactions by moving the end of the `DO TRANSACTION` block up after the Order block.

> | To look at what happens if you combine them all again:

> 1. Define a new label for the `DO TRANSACTION` block:

>     TransBlock:
>       DO TRANSACTION ON ERROR UNDO, LEAVE:
>         FIND ttOrder WHERE ttOrder.TransType = "" NO-ERROR.

> 2. Move this block’s `END` statement back all the way down to the end of the `FOR EACH` block, then change the `UNDO, LEAVE` statement to undo and leave that entire block:

>          IF bUpdateOline.ExtendedPrice > (ttOline.ExtendedPrice * 1.2) THEN
>          DO:
>             cMessage = cMessage +
>                        "Line " + STRING(OrderLine.LineNum) +
>                  ": Can't increase price by more than 20%." + CHR(10).
>              UNDO TransBlock, LEAVE TransBlock.
>          END.
>        END.   /* END ELSE DO If we updated the OrderLine */
>       END.       /* END DO FOR EACH ttOline */
>      END.         /* END DO Transaction */

> Now the transaction structure looks like the diagram in Figure 17–8.

> Figure 17–8: Third variation of transaction scope

> If there’s an error on any OrderLine, then the whole transaction is backed out, including any change to the Order.

> | To test this:

> 1. Edit one of the fields in the Order, such as the PO, and then make an invalid change to one of its OrderLines.

> 2. Click Save. You see an error message.

> 3. Click Fetch to refresh the Order.

> The changes you made to the Order have been undone along with changes to the OrderLines. (Note that the code isn’t set up to refresh the Order display if the transaction fails. This is an exercise you can do yourself.)

> But what if you want to undo a portion of a transaction? Progress supports the capability to do this. If your application has multiple nested blocks, each of which would be a transaction block if it stood on its own, then the outermost block is the transaction and all nested transaction blocks within it become subtransactions. A subtransaction block can be:

> - A procedure block that is run from a transaction block in another procedure.

> - Each iteration of a `FOR EACH` block nested within a transaction block.

> - Each iteration of a `REPEAT` block nested within a transaction block.

> - Each iteration of a `DO TRANSACTION`, `DO ON ERROR`, or `DO ON ENDKEY` block inside a transaction block.

> If an application error occurs during a subtransaction, all the work done since the beginning of the subtransaction is undone. You can nest subtransactions within other subtransactions. You can use the `UNDO` statement to programmatically undo a subtransaction.

> Note: If a system error occurs, for example from a power outage, then the work done during the entire transaction is undone.

> In the sample logic procedure, for example, with the `END` statement moved to the end, the `FOR EACH` block is really a subtransaction within the main transaction. An error inside this inner block undoes only the change made in that block. Likewise, if you change the `UNDO` statement back to `UNDO,` `NEXT`, then the Order changes are saved and only the current OrderLine changes are undone, as shown in Figure 17–9.

> Figure 17–9: Example subtransaction

> Note that a `FOR EACH`, `REPEAT`, or procedure block that does not directly contain statements that either modify the database or read records using an `EXCLUSIVE-LOCK` does not start a transaction on its own. However, if contained inside a transaction block, it does start a subtransaction.

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