sqlite-easy-0.1.0.0: src/Database/Sqlite/Easy/Internal.hs
{-# language DerivingVia #-}
{-# language OverloadedStrings #-}
{-# language ScopedTypeVariables #-}
module Database.Sqlite.Easy.Internal where
import Database.SQLite3
import Data.String (IsString)
import Data.Text (Text)
import Control.Exception
import Data.Typeable
import Data.Pool
-- * Connection
-- | A SQLite3 connection string
newtype ConnectionString
= ConnectionString
{ unConnectionString :: Text
}
deriving IsString via Text
deriving Show
-- | Create a pool of a sqlite3 db with a specific connection string.
createSqlitePool :: ConnectionString -> IO (Pool Database)
createSqlitePool (ConnectionString connStr) =
newPool PoolConfig
{ createResource = open connStr
, freeResource = close
, poolCacheTTL = 180
, poolMaxResources = 50
}
-- | Open a database, run some stuff, close the database.
withDb :: ConnectionString -> (Database -> IO a) -> IO a
withDb (ConnectionString connStr) = bracket (open connStr) close
-- * Execution
-- | A SQL statement
newtype SQL
= SQL
{ unSQL :: Text
}
deriving (Semigroup, IsString) via Text
deriving Show
-- | Run a SQL statement on a database and fetch the results.
run :: SQL -> Database -> IO [[SQLData]]
run (SQL stmt) db = prepare db stmt >>= fetchAll
-- | Run a SQL statement with certain parameters on a database and fetch the results.
runWith :: SQL -> [SQLData] -> Database -> IO [[SQLData]]
runWith (SQL stmt) params db = do
preparedStmt <- prepare db stmt
bind preparedStmt params
fetchAll preparedStmt
-- | Run a statement and fetch all of the data.
fetchAll :: Statement -> IO [[SQLData]]
fetchAll stmt = do
res <- step stmt
case res of
Row -> do
row <- columns stmt
rows <- fetchAll stmt
pure (row : rows)
Done -> do
finalize stmt
pure []
-- * Transaction
-- | Run operations as a transaction.
-- If the action throws an error, the transaction is rolled back.
--
-- __Note__: Transactions do not nest.
--
-- For more information, visit: <https://www.sqlite.org/lang_transaction.html>
asTransaction :: Typeable a => Database -> IO a -> IO a
asTransaction db action = do
[] <- run "BEGIN" db
catches
(action <* run "COMMIT" db)
[ Handler $ \(CancelTransaction a) -> run "ROLLBACK" db *> pure a
, Handler $ \(ex :: SomeException) -> run "ROLLBACK" db *> throwIO ex
]
asTransaction' :: Database -> IO a -> IO a
asTransaction' db action = do
[] <- run "BEGIN" db
catches
(action <* run "COMMIT" db)
[ Handler $ \(ex :: SomeException) -> run "ROLLBACK" db *> throwIO ex
]
-- | Cancel a transaction by supplying the return value.
-- To be used inside transactions.
cancelTransaction :: Typeable a => a -> IO a
cancelTransaction = throwIO . CancelTransaction
data CancelTransaction a
= CancelTransaction a
instance Show (CancelTransaction a) where
show CancelTransaction{} = "CancelTransaction"
instance (Typeable a) => Exception (CancelTransaction a)