Ending a transaction
Prev | Chapter 3. Tutorial | Next

## Ending a transaction

A transaction ends either successfully through an explicit commit command, or unsuccessfully in any of a number of ways. The following are the ways to end a transaction:

- The transaction may be committed through its `commit` member function:

      	    Xaction.commit();

  The commit operation is sent to the backend at the point where the `commit` call occurs. Any exceptions generated by the database transaction will be thrown from here at the latest. The only exceptions that may be generated by `Xaction` beyond this point are related to incorrect handling of the transaction object, eg. if an attempt is made to abort `Xaction` after it has been committed, or runtime errors such as memory running out.

  As a consequence, any streams or cursors nested within the transaction (to be discussed later) must have been closed before the `commit()`. To do otherwise could possibly allow a transaction to be committed before all related actions had completed. The library will throw an exception if any streams are still open when the transaction is ended.

- A transaction is aborted if it is destroyed without having been explicitly committed:

  -     		{
        		work Xaction(Conn, "DemoTransaction");

        		// (Queries)

        		} // Xaction destroyed here

  -     		work *XactionP = new work(Conn, "DemoTransaction");

        		// (Queries)

        		delete XactionP; // Xaction destroyed here

  -     		try
        		{
        		work Xaction(Conn, "DemoTransaction");

        		// (Queries)

        		Xaction.commit();
        		// If we get here, Xaction is committed
        		}
        		catch (...)
        		{
        		// If we get here, Xaction has been rolled back
        		}

  No matter where exactly the decision to abort is made, the actual abort operation is sent to the backend when the transaction's destructor is called. If the abort fails, eg. because the network connection has been lost, no error is reported [2] and the transaction will die of natural causes (either it has been closed by the backend already, or it soon will be if the connection is lost).

- If a database error occurs during the transaction, such as an SQL syntax error or lost connection to the backend, the transaction is aborted.

      	    work Xaction(Conn, "DemoTransaction");
      	    try
      	    {
      	    // (Queries)
      	    Xaction.exec("SELECT !?^H^H^H^H");	// Fails: SQL syntax error
      	    }
      	    catch (...)
      	    {
      	    }
      	    Xaction.commit(); // ERROR: Xaction has already aborted!

  For this reason, it is recommended always to include the "commit" operation inside the `try` block (if any) surrounding the transaction code, not after the `catch` block.

  Think of it as a natural extension of structural programming: the transaction is "nested" within the connection, and the transaction code can be "nested" in a `try`/`catch` block.

No more queries may be issued to the transaction regardless of how it ended; an exception will be thrown if the application attempts to continue the transaction after that time. Ending a transaction more than once is an error, except that aborting it multiple times is tolerated to facilitate error handling.

[2] Throwing an exception from a destructor to report the error would have serious effects on program correctness. Never throw exceptions from a destructor.

Prev | Up | Next
Performing a transaction | Home | Executing queries