{-# LANGUAGE Rank2Types #-} -- | -- Module : Database.HDBC.Session -- Copyright : 2013-2016 Kei Hibino -- License : BSD3 -- -- Maintainer : ex8k.hibino@gmail.com -- Stability : experimental -- Portability : unknown -- -- This module provides a base bracketed function -- to call close correctly against opend DB connection. module Database.HDBC.Session ( -- * Bracketed session -- $bracketedSession withConnectionCommit, withConnectionIO, withConnectionIO', withConnection, -- * Show errors -- $showErrors showSqlError, handleSqlError' ) where import Database.HDBC (IConnection, handleSql, SqlError(seState, seNativeError, seErrorMsg)) import qualified Database.HDBC as HDBC import Control.Exception (bracket) {- $bracketedSession Bracket function implementation is provided by several packages, so this package provides base implementation which requires bracket function and corresponding lift function. -} {- $showErrors Functions to show 'SqlError' type not to show 'String' fields. -} -- | show 'SqlError' not to show 'String' fields. showSqlError :: SqlError -> String showSqlError se = unlines ["seState: '" ++ seState se ++ "'", "seNativeError: " ++ show (seNativeError se), "seErrorMsg: '" ++ seErrorMsg se ++ "'"] -- | Like 'handleSqlError', but not to show 'String' fields of SqlError. handleSqlError' :: IO a -> IO a handleSqlError' = handleSql (fail . reformat . showSqlError) where reformat = ("SQL error: \n" ++) . unlines . map (" " ++) . lines -- | Run a transaction on a HDBC IConnection and close the connection. withConnection :: (Monad m, IConnection conn) => (forall c. m c -> (c -> m ()) -> (c -> m a) -> m a) -- ^ bracket -> (forall b. IO b -> m b) -- ^ lift -> IO conn -- ^ Connect action -> (conn -> m a) -- ^ Transaction body -> m a withConnection bracket' lift connect tbody = bracket' (lift open') (lift . close') bodyWithRollback where open' = handleSqlError' connect close' :: IConnection conn => conn -> IO () close' = handleSqlError' . HDBC.disconnect bodyWithRollback conn = bracket' (return ()) -- Do rollback independent from driver default behavior when disconnect. (const . lift . handleSqlError' $ HDBC.rollback conn) (const $ tbody conn) -- | Run a transaction on a HDBC 'IConnection' and close the connection. -- Simple 'IO' version. withConnectionIO :: IConnection conn => IO conn -- ^ Connect action -> (conn -> IO a) -- ^ Transaction body -> IO a -- ^ Result transaction action withConnectionIO = withConnection bracket id -- | Same as 'withConnectionIO' other than issuing commit at the end of transaction body. -- In other words, the transaction with no exception is committed. -- Handy defintion for simple transactions. withConnectionCommit :: IConnection conn => IO conn -- ^ Connect action -> (conn -> IO a) -- ^ Transaction body -> IO a -- ^ Result transaction action withConnectionCommit conn body = withConnectionIO conn $ \c -> do x <- body c HDBC.commit c return x -- | Same as 'withConnectionIO' other than wrapping transaction body in 'handleSqlError''. withConnectionIO' :: IConnection conn => IO conn -- ^ Connect action -> (conn -> IO a) -- ^ Transaction body -> IO a -- ^ Result transaction action withConnectionIO' connect body = withConnectionIO connect $ handleSqlError' . body