Skip to content

Commit

Permalink
More doc improvement.
Browse files Browse the repository at this point in the history
  • Loading branch information
bos committed Apr 29, 2011
1 parent 7572520 commit f1f1a6c
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 23 deletions.
120 changes: 100 additions & 20 deletions Database/MySQL/Simple.hs
Expand Up @@ -13,19 +13,36 @@

module Database.MySQL.Simple
(
FormatError(fmtMessage, fmtQuery, fmtParams)
-- * Types
Query
, Only(..)
, Query
, execute
, execute_
-- ** Exceptions
, FormatError(fmtMessage, fmtQuery, fmtParams)
, QueryError(qeMessage, qeQuery)
, ResultError(errSQLType, errHaskellType, errMessage)
-- * Connection management
, Base.connect
, Base.defaultConnectInfo
, Base.close
-- * Queries that return results
, query
, query_
-- * Statements that do not return results
, execute
, execute_
, Base.insertID
-- * Transaction handling
, withTransaction
, Base.autocommit
, Base.commit
, Base.rollback
-- * Helper functions
, formatQuery
) where

import Blaze.ByteString.Builder (fromByteString, toByteString)
import Control.Applicative ((<$>), pure)
import Control.Exception (Exception, throw)
import Control.Exception (Exception, onException, throw)
import Control.Monad.Fix (fix)
import Data.ByteString (ByteString)
import Data.Int (Int64)
Expand All @@ -35,10 +52,14 @@ import Database.MySQL.Base (Connection)
import Database.MySQL.Simple.Param (Action(..), inQuotes)
import Database.MySQL.Simple.QueryParams (QueryParams(..))
import Database.MySQL.Simple.QueryResults (QueryResults(..))
import Database.MySQL.Simple.Result (ResultError(..))
import Database.MySQL.Simple.Types (Only(..), Query(..))
import qualified Data.ByteString.Char8 as B
import qualified Database.MySQL.Base as Base

-- | Exception thrown if a 'Query' could not be formatted correctly.
-- This may occur if the number of \'@?@\' characters in the query
-- string does not match the number of parameters provided.
data FormatError = FormatError {
fmtMessage :: String
, fmtQuery :: Query
Expand All @@ -47,6 +68,26 @@ data FormatError = FormatError {

instance Exception FormatError

-- | Exception thrown if 'query' is used to perform an @INSERT@-like
-- operation, or 'execute' is used to perform a @SELECT@-like operation.
data QueryError = QueryError {
qeMessage :: String
, qeQuery :: Query
} deriving (Eq, Show, Typeable)

instance Exception QueryError

-- | Format a query string.
--
-- String parameters are escaped according to the character set in use
-- on the 'Connection'.
--
-- Exceptions that may be thrown:
--
-- * 'FormatError': the query string could not be formatted correctly.
--
-- * 'QueryError': the result contains a non-zero number of columns
-- (i.e. you should be using 'query' instead of 'execute').
formatQuery :: QueryParams q => Connection -> Query -> q -> IO ByteString
formatQuery conn q@(Query template) qs
| null xs && '?' `B.notElem` template = return template
Expand All @@ -62,40 +103,63 @@ formatQuery conn q@(Query template) qs
" '?' characters, but " ++
show (length xs) ++ " parameters") q xs

-- | Execute an @INSERT@, @UPDATE@, or other SQL query that is not
-- expected to return results.
--
-- Returns the number of rows affected.
--
-- Throws 'FormatError' if the string could not be formatted correctly.
execute :: (QueryParams q) => Connection -> Query -> q -> IO Int64
execute conn template qs = do
Base.query conn =<< formatQuery conn template qs
finishExecute conn
finishExecute template conn

-- | A version of 'execute' that does not perform query substitution.
execute_ :: Connection -> Query -> IO Int64
execute_ conn (Query stmt) = do
execute_ conn q@(Query stmt) = do
Base.query conn stmt
finishExecute conn
finishExecute q conn

finishExecute :: Connection -> IO Int64
finishExecute conn = do
finishExecute :: Query -> Connection -> IO Int64
finishExecute q conn = do
ncols <- Base.fieldCount (Left conn)
if ncols /= 0
then error "execute: executed a select!"
then throw $ QueryError ("execute resulted in " ++ show ncols ++
"-column result") q
else Base.affectedRows conn


-- | Perform a @SELECT@ or other SQL query that is expected to return
-- results.
--
-- All results are retrieved and converted before this function
-- returns.
--
-- Exceptions that may be thrown:
--
-- * 'FormatError': the query string could not be formatted correctly.
--
-- * 'QueryError': the result contains no columns (i.e. you should be
-- using 'execute' instead of 'query').
--
-- * 'ResultError': result conversion failed.
query :: (QueryParams q, QueryResults r)
=> Connection -> Query -> q -> IO [r]
query conn template qs = do
Base.query conn =<< formatQuery conn template qs
finishQuery conn

finishQuery template conn

-- | A version of 'query' that does not perform query substitution.
query_ :: (QueryResults r) => Connection -> Query -> IO [r]
query_ conn (Query q) = do
Base.query conn q
finishQuery conn
query_ conn q@(Query que) = do
Base.query conn que
finishQuery q conn

finishQuery :: (QueryResults r) => Connection -> IO [r]
finishQuery conn = do
finishQuery :: (QueryResults r) => Query -> Connection -> IO [r]
finishQuery q conn = do
r <- Base.storeResult conn
ncols <- Base.fieldCount (Right r)
if ncols == 0
then return []
then throw $ QueryError "query resulted in zero-column result" q
else do
fs <- Base.fetchFields r
flip fix [] $ \loop acc -> do
Expand All @@ -105,6 +169,22 @@ finishQuery conn = do
_ -> let !c = convertResults fs row
in loop (c:acc)

-- | Execute an action inside a SQL transaction.
--
-- You are assumed to have started the transaction yourself.
--
-- If your action succeeds, the transaction will be 'Base.commit'ted
-- before this function returns.
--
-- If your action throws any exception (not just a SQL exception), the
-- transaction will be rolled back 'Base.rollback' before the
-- exception is propagated.
withTransaction :: Connection -> IO a -> IO a
withTransaction conn act = do
r <- act `onException` Base.rollback conn
Base.commit conn
return r

fmtError :: String -> Query -> [Action] -> a
fmtError msg q xs = throw FormatError {
fmtMessage = msg
Expand Down
4 changes: 2 additions & 2 deletions Database/MySQL/Simple/Result.hs
Expand Up @@ -42,8 +42,8 @@ import qualified Data.Text as ST
import qualified Data.Text.Encoding as ST
import qualified Data.Text.Lazy as LT

-- | This exception is thrown if conversion from a SQL value to a
-- Haskell value fails.
-- | Exception thrown if conversion from a SQL value to a Haskell
-- value fails.
data ResultError = Incompatible { errSQLType :: String
, errHaskellType :: String
, errMessage :: String }
Expand Down
2 changes: 1 addition & 1 deletion mysql-simple.cabal
Expand Up @@ -44,7 +44,7 @@ library
blaze-builder,
blaze-textual,
bytestring >= 0.9,
mysql,
mysql >= 0.1.0.1,
old-locale,
text >= 0.11.0.2,
time
Expand Down

0 comments on commit f1f1a6c

Please sign in to comment.