A transaction is a set of related changes to the database that the database either completes in its entirety or discards, leaving no modification to the database. In other contexts this might be referred to as a physical transaction or commit unit. In the most basic case, the ABL Virtual Machine (AVM) assures that if you assign multiple field values to a database table, it either applies all of those changes or none of them. But in many cases, you need to update multiple related records in the database with the assurance that all of the changes are made together. For example, you might update an Order, its OrderLines, and the Customer for the Order in a single transaction. In the business logic for that operation you update the Customer Balance to reflect the total of all the OrderLines. You want to know that the adjustment you make to the Customer Balance is always written to the database along with the OrderLine records that are the detail for that adjustment, and that some subset of these changes are never made without the rest of them. If the changes cannot be completed for any reason (whether it is a validation error that your application detects, a database error, a system hardware failure, or anything else), the database needs to make all the changes successfully or back out any partial changes so that the records are restored to their state before the transaction began. Many users need to be able to update the same database concurrently with the same assurance. ABL transactions and the integrity of the OpenEdge® database, together with record locking, assure that this is always the case. Your application procedures define the scope of every transaction that updates the database so that you have complete control over the unit of work that is reliably committed or rolled back.

Transaction blocks

Transactions in ABL are scoped to blocks. You are already familiar with the concept of building procedures up out of nested blocks of procedural statements and how record scoping is affected by those blocks. Transaction scoping works in much the same way and is closely tied to the scoping of records to blocks. Some blocks in ABL procedures are transaction blocks and some are not, according to these rules:
  • You can explicitly include the TRANSACTION keyword on a FOR EACH or REPEAT block, or on a DO block with the optional error-handling phrase beginning ON ERROR. Any block that uses the TRANSACTION keyword becomes a transaction block.
  • Any block that directly updates the database or directly reads records with EXCLUSIVE-LOCK likewise becomes a transaction block. This can be a procedure block, a trigger block, or each iteration of a DO, FOR EACH, or REPEAT block.

    A direct database update can be, for example, a CREATE, DELETE, or ASSIGN statement. A block is said to be directly reading records with EXCLUSIVE-LOCK if at least one of the FIND or FOR EACH statements that has the EXCLUSIVE-LOCK keyword is not embedded in an inner block enclosed within the block in question.

    A DO block without the NO-ERROR phrase does not automatically have the transaction property. Also, a procedure that would start a transaction on its own can run from another procedure that has already started a transaction. In this case, you enclose the called procedure in the larger transaction of the calling procedure. Once a transaction is started, all database changes are part of that transaction until it ends. Each user or session can have just one active transaction at a time. If one transaction block is nested within another, the inner block forms a subtransaction that you can programmatically undo if it causes an error or fails some validation constraint. You learn about subtransactions later in Manage Transactions.