Try OpenEdge Now
skip to main content
ABL Essentials
Managing Transactions : Using the UNDO statement : Subtransactions
 

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. Let's see what happens if you combine the Order and OrderLine updates into a single transaction.
To combine the Order and OrderLine updates into a single transaction:
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. /* ELSE DO If we updated the OrderLine */
  END. /* DO FOR EACH ttOline */
END. /* DO TRANSACTION */
Now the transaction structure looks like the diagram in the following figure.
Figure 56. Third variation of transaction scope
If there is an error on any OrderLine, then the whole transaction is backed out, including any change to the Order.
To test what happens when an error occurs during an OrderLine update:
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? ABL 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 the following figure.
Figure 57. 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.