A transaction is a group of updates that must either be performed in their entirety or not at all. The TRANSACTION START statement starts a new transaction. All updates within the transaction are cached and only applied to the database when the TRANSACTION COMMIT statement is executed. Execution of the program then continues at the statement following the TRANSACTION COMMIT.
The TRANSACTION ABORT statement terminates the transaction, discarding any cached updates. Execution continues at the statement following the TRANSACTION ABORT.
The THEN and ELSE clauses are optional and are provided for compatibility with other products. Within QM any errors occurring in a TRANSACTION START or TRANSACTION COMMIT will result in run time errors.
Deletes and writes inside a transaction will fail unless the program holds an update lock on the record or the file. All locks obtained inside the transaction are retained until the transaction terminates and are then released. Locks already owned when the transaction begins will still be present after the transaction terminates, even if the record is updated or deleted within the transaction.
Closing a file inside a transaction appears to work in that the file variable is destroyed though the actual close is deferred until the transaction terminates and any updates have been applied to the file. Rolling back the transaction will not reinstate the file variable.
Updates to sequential records opened using OPENSEQ are not affected by transactions.
Transactions may be nested. If the TRANSACTION START statement is executed inside an active transaction, the active transaction is stacked and a new transaction commences. Termination of the new transaction reverts to the stacked transaction. The default behaviour of QM is that transactions are durable such that updates in a child transaction are applied to the data files on use of TRANSACTION COMMIT. The NON.DURABLE.TXN setting of the QMBasic $MODE compiler directive, makes transactions non-durable such that updates in a child transaction are inherited by the parent transaction on commit.
The following operations are banned inside transactions:
READU CUST1.REC FROM CUST.F, CUST1.ID ELSE
CUST1.REC<C.BALANCE> -= TRANSFER.VALUE
WRITE CUST1.REC TO CUST.F, CUST1.ID
READU CUST2.REC FROM CUST.F, CUST2.ID ELSE
CUST2.REC<C.BALANCE> += TRANSFER.VALUE
WRITE CUST2.REC TO CUST.F, CUST2.ID
The above program fragment transfers money between two customer accounts. The updates are only committed if the entire transaction is successful.