hmssql
Safe HaskellNone
LanguageHaskell2010

Hmssql.Transaction

Description

Transactions. withTransaction runs an action in one, committing if it returns and rolling back if it throws; the begin, commit and rollback functions are the pieces, for manual control.

A transaction begins with a Transaction Manager request (TM_BEGIN_XACT, what BEGIN TRANSACTION does, and the only begin SQL Server accepts on a MARS connection); it is committed and rolled back by T-SQL batches. The connection tracks the server's transaction descriptor from ENVCHANGE tokens, so inTransaction is what the server says, and a transaction the server has already rolled back (after a deadlock, or an error under XACT_ABORT ON) is not rolled back twice, and not committed by mistake.

Synopsis

Documentation

withTransaction :: Connection -> IO a -> IO a Source #

Run the action in a transaction. It commits if the action returns, and rolls back if the action throws, an asynchronous exception from killThread or timeout included. Beginning the transaction is a round trip of its own before the action runs, and the COMMIT is another.

The connection is held for the whole transaction, so requests from other threads wait until it ends instead of running inside it. The action must not wait for another thread that uses the connection.

On a MARS connection (connectMars) every request the body makes, and every stream it opens, runs in the transaction. No response may be unfinished when it begins or when the body returns (TransactionRequestsActive): begin first, open the streams inside with withQueryS, and let them end before the body returns. If the body throws, the responses it left open are canceled before the rollback.

Throws TransactionAlreadyActive if a transaction is already active; TransactionEndedEarly (after cleaning up) if the body returns but the transaction it started is gone; and TransactionStillOpen (after rolling everything back) if the body began a nested transaction and didn't end it. If the rollback after a failure itself fails, the connection is marked broken (RollbackFailed) rather than left in an unknown state, and the exception that caused the rollback propagates, unless an asynchronous exception interrupted the rollback.

withTransactionLevel :: IsolationLevel -> Connection -> IO a -> IO a Source #

withTransaction at the given isolation level. The begin request sets the level, and the COMMIT or ROLLBACK batch sets it back to READ COMMITTED, the session's level at login, however the transaction ends, so no round trips are added. The session is assumed to be at READ COMMITTED outside withTransactionLevel, and a level the application set by hand is not preserved.

data TransactionError Source #

Constructors

TransactionAlreadyActive

A transaction was already active on the connection. SQL Server's nested transactions don't nest (an inner ROLLBACK ends the outer transaction too), so they are refused rather than emulated.

TransactionEndedEarly

The transaction ended before the body returned, so nothing was committed: the server rolled it back after an error the body caught (under XACT_ABORT ON, or as a deadlock victim) or after the Attention that canceled a stream under XACT_ABORT ON (see queryS); or the body committed or rolled it back itself, or called reset. (A body that calls release gets ConnectionReleased instead.)

TransactionRequestsActive [Text]

On a MARS connection (connectMars): another response on the connection had not been read to its end or canceled when a transaction was to begin or to be committed. The server refuses both while another request is active (errors 3988 and 3981, the second of which loses the transaction), so hmssql checks first, by what has been read. Before a begin, nothing was sent; before the commit of withTransaction, the responses were canceled and the transaction rolled back; before commit, the transaction is still open. Carries the SQL text of each unfinished response, so that a stream left open can be found. Read every stream to its end (not only to its last row), cancel it, or open it with withQueryS.

TransactionStillOpen

The connection was still in a transaction after the COMMIT: the body began a nested one (BEGIN TRANSACTION, begin) and didn't end it, so the COMMIT only lowered @@TRANCOUNT and committed nothing. Everything was then rolled back.

begin :: Connection -> IO () Source #

Begin a transaction (as BEGIN TRANSACTION does, by a Transaction Manager request), for manual control. Unlike withTransaction it doesn't refuse a nested transaction, hold the connection for the transaction's duration, or roll back on an exception. On a MARS connection it throws TransactionRequestsActive if another response is unfinished.

commit :: Connection -> IO () Source #

COMMIT TRANSACTION. Throws SqlServerError if no transaction is active (the server's error 3902). On a MARS connection it throws TransactionRequestsActive if another response is unfinished; nothing is sent then, and the transaction stays open.

rollback :: Connection -> IO () Source #

ROLLBACK TRANSACTION, if a transaction is active; nothing if the server has already ended it. Responses still open on the connection are canceled first (on a MARS connection, all of them: the server keeps a rolled-back transaction, doomed, until they have ended).

inTransaction :: Connection -> IO Bool Source #

Whether a transaction is active on the connection: the server said one began (a begin-transaction ENVCHANGE), and hasn't said it ended.