Transactions

1.1. What is an ABL Transaction?

 

 

  • Transactions ensure that the data in your database maintains integrity.
  • Imagine we need to update an Order and one of its Order Lines. Both updates must happen together. If one fails, the other should not be saved.

 

Without a transaction, the two updates are independent. If the second update fails, the Order ShipDate is already saved but the OrderLine Quantity is not. This leaves the database in an inconsistent state.

 

PROCEDURE WithoutTransaction:
   DEFINE BUFFER bufOrder FOR Order.
   DEFINE BUFFER bufOrderLine FOR OrderLine.

   FIND bufOrder EXCLUSIVE-LOCK
        WHERE bufOrder.OrderNum = 1 NO-ERROR.

   IF AVAILABLE bufOrder THEN
      bufOrder.ShipDate = TODAY + 7.

   FIND bufOrderLine EXCLUSIVE-LOCK
        WHERE bufOrderLine.OrderNum = 1
        AND   bufOrderLine.LineNum = 1 NO-ERROR.

   IF AVAILABLE bufOrderLine THEN
      bufOrderLine.Qty = 10.
   ELSE
      MESSAGE "OrderLine not found, Order was already updated"
         VIEW-AS ALERT-BOX WARNING.
END PROCEDURE.

 

By wrapping both updates in a DO TRANSACTION block, we guarantee that either both changes are committed together or neither of them is saved. This is exactly what we mean by data integrity.

 

PROCEDURE WithTransaction:
   DEFINE BUFFER bufOrder FOR Order.
   DEFINE BUFFER bufOrderLine FOR OrderLine.

   DO TRANSACTION:
      FIND bufOrder EXCLUSIVE-LOCK
           WHERE bufOrder.OrderNum = 1 NO-ERROR.

      IF AVAILABLE bufOrder THEN
         bufOrder.ShipDate = TODAY + 7.

      FIND bufOrderLine EXCLUSIVE-LOCK
           WHERE bufOrderLine.OrderNum = 1
           AND   bufOrderLine.LineNum = 1 NO-ERROR.

      IF AVAILABLE bufOrderLine THEN
         bufOrderLine.Qty = 10.
      ELSE
         UNDO, LEAVE.
   END.
END PROCEDURE.
  • When all operations inside a transaction complete successfully, the changes are committed (saved to the database). If something goes wrong, all changes are rolled back (leaving the database exactly as it was before the transaction started).

1.2. TRANSACTION Keyword

 

  • In ABL, a transaction can be started in two ways: explicitly, by using the TRANSACTION keyword, or implicitly, by ABL itself when it detects that a block modifies data.

 

Explicit transaction

 

  • An explicit transaction is one you declare yourself using the TRANSACTION keyword. You can also start a transaction by adding the TRANSACTION keyword to a DO, FOR, or REPEAT block.

 

DO FOR TRANSACTION example:

 

PROCEDURE UpdateCustomer:
   DEFINE BUFFER bufCustomer FOR Customer.
   DEFINE BUFFER bufOrder FOR Order.

   DO FOR bufCustomer TRANSACTION:
      FIND bufCustomer EXCLUSIVE-LOCK
           WHERE bufCustomer.CustNum = 1 NO-ERROR.
      IF AVAILABLE bufCustomer THEN
         bufCustomer.Balance = bufCustomer.Balance + 1000.
   END.
END PROCEDURE.

 

By adding bufCustomer to the DO FOR clause, we are telling ABL that bufCustomer is scoped to this block — meaning the record lock is held only for the duration of this block and released automatically when END is reached. The transaction also commits at that point.

 

DO TRANSACTION example:

 

PROCEDURE UpdateOrderAndLine:
   DEFINE INPUT PARAMETER ipOrderNum AS INTEGER NO-UNDO.

   DEFINE BUFFER bufOrder FOR Order.
   DEFINE BUFFER bufOrderLine FOR OrderLine.

   DO TRANSACTION:
      FIND bufOrder EXCLUSIVE-LOCK
           WHERE bufOrder.OrderNum = ipOrderNum NO-ERROR.

      IF NOT AVAILABLE bufOrder THEN
         RETURN.

      ASSIGN
         bufOrder.ShipDate    = TODAY + 7
         bufOrder.PromiseDate = TODAY + 14.

      FOR EACH bufOrderLine OF bufOrder EXCLUSIVE-LOCK:
         IF HasStock(bufOrderLine.Itemnum, bufOrderLine.Qty) THEN
            bufOrderLine.OrderLineStatus = "In Warehouse".
         ELSE
            bufOrderLine.OrderLineStatus = "Pending".
      END.
   END.
END PROCEDURE.

 

This procedure updates an Order and all its related Order Lines in a single transaction. If the order is not found, it exits immediately. For each order line, HasStock() checks availability and sets the status accordingly. All changes commit together or not at all.

Implicit transaction

 

  • An implicit transaction is started automatically by ABL when a data-modifying statement (CREATE, UPDATE, DELETE) is found inside a block that is not already marked as a transaction.

 

The statements which start an implicit transaction are:

 

  • FOR blocks that directly update the database

 

PROCEDURE UpdateOrder:
   DEFINE BUFFER bufOrder FOR Order.
   DEFINE BUFFER bufCustomer FOR Customer.
   DEFINE VARIABLE iCount AS INTEGER NO-UNDO.

   FOR EACH bufOrder EXCLUSIVE-LOCK:
      iCount = iCount + 1.
      IF iCount >= 5 THEN
         UNDO, LEAVE.
      bufOrder.OrderDate = TODAY - 20.
   END.
END PROCEDURE.

 

Without a wrapping DO TRANSACTION, each iteration has its own implicit transaction. When iCount reaches 5, UNDO, LEAVE rolls back only the current iteration — the first 4 updates are already committed and cannot be undone.

 

PROCEDURE UpdateOrder:
   DEFINE BUFFER bufOrder FOR Order.
   DEFINE BUFFER bufCustomer FOR Customer.
   DEFINE VARIABLE iCount AS INTEGER NO-UNDO.

   #trans_block:
   DO TRANSACTION:
      FOR EACH bufOrder EXCLUSIVE-LOCK:
         iCount = iCount + 1.

         IF iCount >= 5 THEN
            UNDO #trans_block, LEAVE.

         bufOrder.OrderDate = TODAY - 20.
      END.
   END.
END PROCEDURE.

 

By wrapping the FOR EACH inside a labeled DO TRANSACTION block, all iterations share a single transaction. If the condition is met — in this case, when iCount reaches 5 — UNDO #trans_block, LEAVE rolls back every change made so far, not just the current iteration.

  • REPEAT blocks that directly update the database
  • Procedure blocks that directly update the database
  • DO blocks with the ON ERROR phrase that contain statements that update the database

 

PROCEDURE CreateOrder:
   DO ON ERROR UNDO, LEAVE:
      /* DB UPDATE OPERATION */
   END.
END PROCEDURE.

  • Even though we did not write the TRANSACTION keyword, ABL automatically promoted this DO block to a transaction block because it contains a CREATE statement. The transaction commits at the END of the block.

 

ABL decides the scope automatically, the transaction boundary may not be where you expect it to be.

 

 

UNDO

 

  • The AVM undoes a transaction automatically if it detects an error at the database level, for example, because of a unique key violation.
  • UNDO can be combined with LEAVE, NEXT, or RETRY depending on what you want to happen after the rollback.

 

 

1.3. Transaction Scoping and Compile Listing

 

What is “scope”?

  • Scoping defines which block owns the transaction.
  • The transaction and the buffer are both scoped to the procedure block.

How do we actually know what scope ABL assigned to our transaction? Looking at the code alone is not always enough, especially in large and complex procedures. This is where the COMPILE LISTING comes in.

Compile listing

 

To do this, use the LISTING option on the COMPILE statement.

To generate the listing file, create a separate procedure and use COMPILE with the LISTING option. The example below locates transaction.p using SEARCH(), then writes both the compiled output and the .lis file in the same directory. The directory containing transaction.p must be in your PROPATH, otherwise SEARCH() returns ?

 

DEFINE VARIABLE cFullPath AS CHARACTER NO-UNDO.

cFullPath = SEARCH("transaction.p").

COMPILE VALUE(cFullPath)
   LISTING VALUE(REPLACE(cFullPath, "transaction.p", "transaction.lis")).

 

The Transaction Scope Table uses the following columns:

 

  • Line — the line number in your source file
  • Blk. Type — the block type (Procedure, Do, For, etc.)
  • Tran — Yes if this block owns the transaction, No if it does not
  • Blk. Label — the procedure name or block label
  • Buffers — listed only under the transaction owner — this is the signal that those buffers are scoped to that block

 

Let’s walk through the COMPILE LISTING output for CreateOrderLine, section by section.

 

Section 1 — Code with line and block numbers

  • Line — the line number in your source code
  • Blk — no block numbers here because there is no explicit transaction block — everything is at procedure level

 

Section 2 — Transaction Scope Table

  • Line 6 Procedure Tran: Yes → the procedure itself owns the transaction
  • Buffers → both bufOrder and bufOrderLine are scoped to the procedure

 

Procedure level scoping

 

  • Occurs when ABL promotes the entire procedure to a transaction block.
  • There is no explicit transaction block — just a DO block with no TRANSACTION keyword. ABL promotes the entire procedure to a transaction.

 

BLOCK-LEVEL ON ERROR UNDO, THROW.

/* *************************** Main Block **************** */
PROCEDURE CreateOrderLine:
   DEFINE BUFFER bufOrder FOR Order.
   DEFINE BUFFER bufOrderLine FOR OrderLine.

   FIND bufOrder EXCLUSIVE-LOCK
        WHERE bufOrder.OrderNum = 1 NO-ERROR.

   IF AVAILABLE bufOrder THEN DO:

      CREATE bufOrderLine.
      ASSIGN
         bufOrderLine.OrderNum = bufOrder.OrderNum
         bufOrderLine.LineNum  = 1
         bufOrderLine.ItemNum  = 1
         bufOrderLine.Qty      = 5.
   END.
END PROCEDURE.
File Name              Line  Blk. Type  Tran  Blk. Label
---------------------- ----  ---------  ----  ----------------------
...ing\transaction.p      6  Procedure  Yes   Procedure CreateOrderLine
Buffers: sports2000.bufOrderLine
         sports2000.bufOrder
...ing\transaction.p     13  Do         No
...ing\transaction.p      0  Procedure  No

Block level scoping

 

  • Block level scoping means that the transaction is no longer owned by the procedure — instead, it is owned by a specific block inside the procedure.
  • This gives you precise control over where the transaction starts and ends, rather than letting ABL decide at the procedure level.

 

BLOCK-LEVEL ON ERROR UNDO, THROW.

/* *************************** Main Block **************** */
PROCEDURE CreateOrderLine:
   DEFINE BUFFER bufOrder FOR Order.
   DEFINE BUFFER bufOrderLine FOR OrderLine.

   DO FOR bufOrder, bufOrderLine TRANSACTION:

      FIND bufOrder EXCLUSIVE-LOCK
           WHERE bufOrder.OrderNum = 1 NO-ERROR.

      IF AVAILABLE bufOrder THEN DO:

         CREATE bufOrderLine.
         ASSIGN
            bufOrderLine.OrderNum = bufOrder.OrderNum
            bufOrderLine.LineNum  = 1
            bufOrderLine.ItemNum  = 1
            bufOrderLine.Qty      = 5.
      END.

   END.
END PROCEDURE.
File Name              Line  Blk. Type  Tran  Blk. Label
---------------------- ----  ---------  ----  ----------------------
..ing\transaction.p       7  Procedure  No    Procedure CreateOrderLine
..ing\transaction.p      11  Do         Yes
Buffers: sports2000.bufOrderLine
         sports2000.bufOrder
..ing\transaction.p      16  Do         No
...ing\transaction.p      0  Procedure  No

 

 

As we can see in the listing, the procedure itself shows “Tran: No” confirming that the procedure is no longer the transaction owner. The DO block at line 11 shows “Tran: Yes” — this is where the transaction lives.

Conclusion

 

In this article, we covered three important concepts for Progress OpenEdge: the TRANSACTION keyword, Transaction Scoping, and COMPILE LISTING. These concepts are essential for writing correct and maintainable code.

  • Always use explicit transactions — do not rely on ABL to decide the scope for you.
  • Use COMPILE LISTING to verify your transaction scope before deploying to production.

I hope this article helped you better understand how transactions work in ABL and how to avoid the common pitfalls that ABL’s implicit scoping can introduce.

 

 

 


 

Author: Denisa Ghiorghioiu, Junior Developer

Denisa is passionate about software development, web technologies, and building efficient systems. She enjoys solving problems and continuously expanding her skills across both frontend and backend development.

SEE HOW WE WORK.

FOLLOW US