module Database.HDBC.MySQL.Connection
(connectMySQL, MySQLConnectInfo(..), defaultMySQLConnectInfo)
where
import Control.Exception
import Control.Monad
import Foreign
import Foreign.C
import qualified Data.ByteString as B
import Data.IORef
import Data.List (isPrefixOf)
import Data.Time
import Data.Time.Clock.POSIX
import qualified Database.HDBC.Types as Types
import Database.HDBC.ColTypes as ColTypes
data MySQLConnectInfo = MySQLConnectInfo
{
mysqlHost :: String
, mysqlUser :: String
, mysqlPassword :: String
, mysqlDatabase :: String
, mysqlPort :: Int
, mysqlUnixSocket :: String
}
defaultMySQLConnectInfo :: MySQLConnectInfo
defaultMySQLConnectInfo = MySQLConnectInfo "127.0.0.1" "root" "" "test" 3306 ""
data Connection = Connection
{ disconnect :: IO ()
, commit :: IO ()
, rollback :: IO ()
, run :: String -> [Types.SqlValue] -> IO Integer
, prepare :: String -> IO Types.Statement
, clone :: IO Connection
, hdbcDriverName :: String
, hdbcClientVer :: String
, proxiedClientName :: String
, proxiedClientVer :: String
, dbServerVer :: String
, dbTransactionSupport :: Bool
, getTables :: IO [String]
, describeTable :: String -> IO [(String, ColTypes.SqlColDesc)]
}
instance Types.IConnection Connection where
disconnect = disconnect
commit = commit
rollback = rollback
run = run
prepare = prepare
clone = clone
hdbcDriverName = hdbcDriverName
hdbcClientVer = hdbcClientVer
proxiedClientName = proxiedClientName
proxiedClientVer = proxiedClientVer
dbServerVer = dbServerVer
dbTransactionSupport = dbTransactionSupport
getTables = getTables
describeTable = describeTable
data MYSQL
connectMySQL :: MySQLConnectInfo -> IO Connection
connectMySQL info = do
mysql_ <- mysql_init nullPtr
when (mysql_ == nullPtr) (error "mysql_init failed")
withCString (mysqlHost info) $ \host_ ->
withCString (mysqlUser info) $ \user_ ->
withCString (mysqlPassword info) $ \passwd_ ->
withCString (mysqlDatabase info) $ \db_ ->
withCString (mysqlUnixSocket info) $ \unixSocket_ ->
do rv <- mysql_real_connect mysql_ host_ user_ passwd_ db_
(fromIntegral $ mysqlPort info)
unixSocket_
when (rv == nullPtr) (connectionError mysql_)
wrap mysql_
where
wrap :: Ptr MYSQL -> IO Connection
wrap mysql_ = do
clientver <- peekCString =<< mysql_get_client_info
serverver <- peekCString =<< mysql_get_server_info mysql_
protover <- mysql_get_proto_info mysql_
mysql_autocommit mysql_ 0
mysql__ <- newForeignPtr_ mysql_
doStartTransaction mysql__
return $ Connection
{ disconnect = finalizeForeignPtr mysql__
, commit = doCommit mysql__ >> doStartTransaction mysql__
, rollback = doRollback mysql__ >> doStartTransaction mysql__
, run = doRun mysql__
, prepare = newStatement mysql__
, clone = connectMySQL info
, hdbcDriverName = "mysql"
, hdbcClientVer = clientver
, proxiedClientName = "mysql"
, proxiedClientVer = show protover
, dbServerVer = serverver
, dbTransactionSupport = True
, getTables = doGetTables mysql__
, describeTable = doDescribeTable mysql__
}
data MYSQL_STMT
data MYSQL_RES
data MYSQL_FIELD = MYSQL_FIELD
{ fieldName :: String
, fieldLength :: CULong
, fieldMaxLength :: CULong
, fieldType :: CInt
, fieldDecimals :: CUInt
, fieldFlags :: CUInt
}
instance Storable MYSQL_FIELD where
sizeOf _ = 80
alignment _ = alignment (undefined :: CInt)
peek p = do
fname <- peekCString =<< ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
flength <- ((\hsc_ptr -> peekByteOff hsc_ptr 28)) p
fmaxlen <- ((\hsc_ptr -> peekByteOff hsc_ptr 32)) p
ftype <- ((\hsc_ptr -> peekByteOff hsc_ptr 76)) p
fdec <- ((\hsc_ptr -> peekByteOff hsc_ptr 68)) p
fflags <- ((\hsc_ptr -> peekByteOff hsc_ptr 64)) p
return $ MYSQL_FIELD
{ fieldName = fname
, fieldLength = flength
, fieldMaxLength = fmaxlen
, fieldType = ftype
, fieldDecimals = fdec
, fieldFlags = fflags
}
poke _ _ = error "MYSQL_FIELD: poke"
data MYSQL_BIND = MYSQL_BIND
{ bindLength :: Ptr CULong
, bindIsNull :: Ptr CChar
, bindBuffer :: Ptr ()
, bindError :: Ptr CChar
, bindBufferType :: CInt
, bindBufferLength :: CULong
}
instance Storable MYSQL_BIND where
sizeOf _ = 60
alignment _ = alignment (undefined :: CInt)
peek _ = error "MYSQL_BIND: peek"
poke p (MYSQL_BIND len_ isNull_ buf_ err_ buftyp buflen) = do
memset (castPtr p) 0 60
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) p len_
((\hsc_ptr -> pokeByteOff hsc_ptr 4)) p isNull_
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) p buf_
((\hsc_ptr -> pokeByteOff hsc_ptr 12)) p err_
((\hsc_ptr -> pokeByteOff hsc_ptr 16)) p buftyp
((\hsc_ptr -> pokeByteOff hsc_ptr 20)) p buflen
data MYSQL_TIME = MYSQL_TIME
{ timeYear :: CUInt
, timeMonth :: CUInt
, timeDay :: CUInt
, timeHour :: CUInt
, timeMinute :: CUInt
, timeSecond :: CUInt
}
instance Storable MYSQL_TIME where
sizeOf _ = 36
alignment _ = alignment (undefined :: CInt)
peek p = do
year <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
month <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) p
day <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) p
hour <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) p
minute <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) p
second <- ((\hsc_ptr -> peekByteOff hsc_ptr 20)) p
return (MYSQL_TIME year month day hour minute second)
poke p t = do
memset (castPtr p) 0 36
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) p (timeYear t)
((\hsc_ptr -> pokeByteOff hsc_ptr 4)) p (timeMonth t)
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) p (timeDay t)
((\hsc_ptr -> pokeByteOff hsc_ptr 12)) p (timeHour t)
((\hsc_ptr -> pokeByteOff hsc_ptr 16)) p (timeMinute t)
((\hsc_ptr -> pokeByteOff hsc_ptr 20)) p (timeSecond t)
newStatement :: ForeignPtr MYSQL -> String -> IO Types.Statement
newStatement mysql__ query = withForeignPtr mysql__ $ \mysql_ -> do
stmt_ <- mysql_stmt_init mysql_
when (stmt_ == nullPtr) (connectionError mysql_)
stmt__ <- newForeignPtr_ stmt_
withCStringLen query $ \(query_, len) -> do
rv <- mysql_stmt_prepare stmt_ query_ (fromIntegral len)
when (rv /= 0) (statementError stmt_)
fields <- fieldsOf stmt_
results <- mapM resultOfField fields
when (not $ null results)
(withArray results $ \bind_ -> do
rv' <- mysql_stmt_bind_result stmt_ bind_
when (rv' /= 0) (statementError stmt_))
gptr <- newIORef nullFunPtr
g <- makeFinalizer $ freeBinds results gptr
writeIORef gptr g
addForeignPtrFinalizer g stmt__
return $ Types.Statement
{ Types.execute = execute mysql__ stmt__
, Types.executeMany = mapM_ $ execute mysql__ stmt__
, Types.finish = finalizeForeignPtr stmt__
, Types.fetchRow = fetchRow mysql__ stmt__ results
, Types.originalQuery = query
, Types.getColumnNames = return $ map fieldName fields
, Types.describeResult = return $ map sqlColDescOf fields
}
type Finalizer a = Ptr a -> IO ()
foreign import ccall "wrapper" makeFinalizer
:: Finalizer a -> IO (FunPtr (Finalizer a))
freeBinds :: [MYSQL_BIND] -> IORef (FunPtr (Finalizer a)) -> Ptr MYSQL_STMT -> IO ()
freeBinds binds selfPtrRef _ = do
readIORef selfPtrRef >>= freeHaskellFunPtr
mapM_ freeOneBind binds
where freeOneBind bind = do
free $ bindLength bind
free $ bindIsNull bind
free $ bindBuffer bind
free $ bindError bind
fieldsOf :: Ptr MYSQL_STMT -> IO [MYSQL_FIELD]
fieldsOf stmt_ = bracket acquire release fieldsOf'
where acquire = mysql_stmt_result_metadata stmt_
release res_ | res_ == nullPtr = return ()
| otherwise = mysql_free_result res_
fieldsOf' res_ | res_ == nullPtr = return []
| otherwise = fieldsOfResult res_
fieldsOfResult :: Ptr MYSQL_RES -> IO [MYSQL_FIELD]
fieldsOfResult res_ = do
field_ <- mysql_fetch_field res_
if (field_ == nullPtr)
then return []
else liftM2 (:) (peek field_) (fieldsOfResult res_)
execute :: ForeignPtr MYSQL -> ForeignPtr MYSQL_STMT -> [Types.SqlValue] -> IO Integer
execute mysql__ stmt__ params =
withForeignPtr mysql__ $ \_ ->
withForeignPtr stmt__ $ \stmt_ -> do
bindParams stmt_ params
rv <- mysql_stmt_execute stmt_
when (rv /= 0) (statementError stmt_)
nrows <- mysql_stmt_affected_rows stmt_
return $ fromIntegral (if nrows == (1 :: CULLong) then 0 else nrows)
bindParams :: Ptr MYSQL_STMT -> [Types.SqlValue] -> IO ()
bindParams stmt_ params = do
param_count <- mysql_stmt_param_count stmt_
let nparams = fromIntegral param_count
when (nparams /= length params)
(error "the number of parameter placeholders in the prepared SQL is different than the number of parameters provided")
let params' = take nparams $ params ++ repeat Types.SqlNull
binds <- mapM bindOfSqlValue params'
withArray binds $ \bind_ -> do
rv <- mysql_stmt_bind_param stmt_ bind_
when (rv /= 0) (statementError stmt_)
bindOfSqlValue :: Types.SqlValue -> IO MYSQL_BIND
bindOfSqlValue Types.SqlNull =
with (1 :: CChar) $ \isNull_ ->
return $ MYSQL_BIND
{ bindLength = nullPtr
, bindIsNull = isNull_
, bindBuffer = nullPtr
, bindError = nullPtr
, bindBufferType = 6
, bindBufferLength = 0
}
bindOfSqlValue (Types.SqlString s) =
bindOfSqlValue' (length s) (withCString s) 253
bindOfSqlValue (Types.SqlByteString s) =
bindOfSqlValue' (B.length s) (B.useAsCString s) 253
bindOfSqlValue (Types.SqlInteger n) =
bindOfSqlValue' (8::Int) (with (fromIntegral n :: CLLong)) 8
bindOfSqlValue (Types.SqlBool b) =
bindOfSqlValue' (1::Int) (with (if b then 1 else 0 :: CChar)) 1
bindOfSqlValue (Types.SqlChar c) =
bindOfSqlValue' (1::Int) (with c) 1
bindOfSqlValue (Types.SqlDouble d) =
bindOfSqlValue' (8::Int) (with (realToFrac d :: CDouble)) 5
bindOfSqlValue (Types.SqlInt32 n) =
bindOfSqlValue' (4::Int) (with n) 3
bindOfSqlValue (Types.SqlInt64 n) =
bindOfSqlValue' (8::Int) (with n) 8
bindOfSqlValue (Types.SqlRational n) =
bindOfSqlValue' (8::Int) (with (realToFrac n :: CDouble)) 5
bindOfSqlValue (Types.SqlWord32 n) =
bindOfSqlValue' (4::Int) (with n) 3
bindOfSqlValue (Types.SqlWord64 n) =
bindOfSqlValue' (8::Int) (with n) 8
bindOfSqlValue (Types.SqlEpochTime epoch) =
let t = utcToMysqlTime $ posixSecondsToUTCTime (fromIntegral epoch) in
bindOfSqlValue' (36::Int) (with t) 12
where utcToMysqlTime :: UTCTime -> MYSQL_TIME
utcToMysqlTime (UTCTime day difftime) =
let (y, m, d) = toGregorian day
t = floor $ (realToFrac difftime :: Double)
h = t `div` 3600
mn = t `div` 60 `mod` 60
s = t `mod` 60
in MYSQL_TIME (fromIntegral y) (fromIntegral m) (fromIntegral d) h mn s
bindOfSqlValue (Types.SqlTimeDiff n) =
let h = fromIntegral $ n `div` 3600
mn = fromIntegral $ n `div` 60 `mod` 60
s = fromIntegral $ n `mod` 60
t = MYSQL_TIME 0 0 0 h mn s in
bindOfSqlValue' (36::Int) (with t) 11
bindOfSqlValue' :: (Integral a, Storable b) =>
a ->
((Ptr b -> IO MYSQL_BIND) -> IO MYSQL_BIND) ->
CInt ->
IO MYSQL_BIND
bindOfSqlValue' len buf btype =
let buflen = fromIntegral len in
with (0 :: CChar) $ \isNull_ ->
with buflen $ \len_ ->
buf $ \buf_ ->
return $ MYSQL_BIND
{ bindLength = len_
, bindIsNull = isNull_
, bindBuffer = castPtr buf_
, bindError = nullPtr
, bindBufferType = btype
, bindBufferLength = buflen
}
resultOfField :: MYSQL_FIELD -> IO MYSQL_BIND
resultOfField field =
let ftype = fieldType field
btype = boundType ftype (fieldDecimals field)
size = boundSize btype (fieldLength field) in
do size_ <- new size
isNull_ <- new (0 :: CChar)
error_ <- new (0 :: CChar)
buffer_ <- mallocBytes (fromIntegral size)
return $ MYSQL_BIND { bindLength = size_
, bindIsNull = isNull_
, bindBuffer = buffer_
, bindError = error_
, bindBufferType = btype
, bindBufferLength = size
}
boundType :: CInt -> CUInt -> CInt
boundType 254 _ = 253
boundType 1 _ = 3
boundType 2 _ = 3
boundType 9 _ = 3
boundType 13 _ = 3
boundType 247 _ = 3
boundType 0 0 = 8
boundType 0 _ = 5
boundType 246 0 = 8
boundType 246 _ = 5
boundType 4 _ = 5
boundType 10 _ = 12
boundType 7 _ = 12
boundType 14 _ = 12
boundType 252 _ = 253
boundType t _ = t
boundSize :: CInt -> CULong -> CULong
boundSize 3 _ = 4
boundSize 5 _ = 8
boundSize 12 _ = 36
boundSize _ n = n
fetchRow :: ForeignPtr MYSQL -> ForeignPtr MYSQL_STMT -> [MYSQL_BIND] -> IO (Maybe [Types.SqlValue])
fetchRow mysql__ stmt__ results =
withForeignPtr mysql__ $ \_ ->
withForeignPtr stmt__ $ \stmt_ -> do
rv <- mysql_stmt_fetch stmt_
case rv of
0 -> row
101 -> row
100 -> return Nothing
_ -> statementError stmt_
where row = mapM cellValue results >>= \cells -> return $ Just cells
cellValue :: MYSQL_BIND -> IO Types.SqlValue
cellValue bind = do
isNull <- peek $ bindIsNull bind
if isNull == 0 then cellValue' else return Types.SqlNull
where cellValue' = do
len <- peek $ bindLength bind
let buftype = bindBufferType bind
buf = bindBuffer bind
nonNullCellValue buftype buf len
nonNullCellValue :: CInt -> Ptr () -> CULong -> IO Types.SqlValue
nonNullCellValue 3 p _ = do
n :: CLong <- peek $ castPtr p
return $ Types.SqlInteger (fromIntegral n)
nonNullCellValue 8 p _ = do
n :: CLLong <- peek $ castPtr p
return $ Types.SqlInteger (fromIntegral n)
nonNullCellValue 5 p _ = do
n :: CDouble <- peek $ castPtr p
return $ Types.SqlDouble (realToFrac n)
nonNullCellValue 253 p len =
peekCStringLen ((castPtr p), fromIntegral len) >>= return . Types.SqlString
nonNullCellValue 12 p _ = do
t :: MYSQL_TIME <- peek $ castPtr p
let epoch = (floor . toRational . utcTimeToPOSIXSeconds . mysqlTimeToUTC) t
return $ Types.SqlEpochTime epoch
where mysqlTimeToUTC :: MYSQL_TIME -> UTCTime
mysqlTimeToUTC (MYSQL_TIME y m d h mn s) =
let day = fromGregorian (fromIntegral y) (fromIntegral m) (fromIntegral d)
time = s + mn * 60 + h * 3600
in UTCTime day (secondsToDiffTime $ fromIntegral time)
nonNullCellValue 11 p _ = do
(MYSQL_TIME _ _ _ h mn s) <- peek $ castPtr p
let secs = 3600 * h + 60 * mn + s
return $ Types.SqlTimeDiff (fromIntegral secs)
nonNullCellValue t _ _ = return $ Types.SqlString ("unknown type " ++ show t)
sqlColDescOf :: MYSQL_FIELD -> (String, ColTypes.SqlColDesc)
sqlColDescOf f =
let typ = typeIdOf (fieldType f)
sz = Just $ fromIntegral $ fieldLength f
octlen = Just $ fromIntegral $ fieldLength f
digits = Just $ fromIntegral $ fieldDecimals f
nullable = Just $ (fieldFlags f .&. 1) == 0
in (fieldName f, ColTypes.SqlColDesc typ sz octlen digits nullable)
typeIdOf :: CInt -> ColTypes.SqlTypeId
typeIdOf 0 = ColTypes.SqlDecimalT
typeIdOf 1 = ColTypes.SqlTinyIntT
typeIdOf 2 = ColTypes.SqlSmallIntT
typeIdOf 3 = ColTypes.SqlIntegerT
typeIdOf 4 = ColTypes.SqlFloatT
typeIdOf 5 = ColTypes.SqlDoubleT
typeIdOf 6 = ColTypes.SqlUnknownT "NULL"
typeIdOf 7 = ColTypes.SqlTimestampT
typeIdOf 8 = ColTypes.SqlNumericT
typeIdOf 9 = ColTypes.SqlIntegerT
typeIdOf 10 = ColTypes.SqlDateT
typeIdOf 11 = ColTypes.SqlTimeT
typeIdOf 12 = ColTypes.SqlTimestampT
typeIdOf 13 = ColTypes.SqlNumericT
typeIdOf 14 = ColTypes.SqlDateT
typeIdOf 15 = ColTypes.SqlVarCharT
typeIdOf 16 = ColTypes.SqlBitT
typeIdOf 246 = ColTypes.SqlDecimalT
typeIdOf 247 = ColTypes.SqlUnknownT "ENUM"
typeIdOf 248 = ColTypes.SqlUnknownT "SET"
typeIdOf 249 = ColTypes.SqlBinaryT
typeIdOf 250 = ColTypes.SqlBinaryT
typeIdOf 251 = ColTypes.SqlBinaryT
typeIdOf 252 = ColTypes.SqlBinaryT
typeIdOf 253 = ColTypes.SqlVarCharT
typeIdOf 254 = ColTypes.SqlCharT
typeIdOf 255 = ColTypes.SqlUnknownT "GEOMETRY"
typeIdOf n = ColTypes.SqlUnknownT ("unknown type " ++ show n)
doRun :: ForeignPtr MYSQL -> String -> [Types.SqlValue] -> IO Integer
doRun mysql__ query params = do
stmt <- newStatement mysql__ query
Types.execute stmt params
doQuery :: String -> ForeignPtr MYSQL -> IO ()
doQuery stmt mysql__ = withForeignPtr mysql__ $ \mysql_ -> do
withCString stmt $ \stmt_ -> do
rv <- mysql_query mysql_ stmt_
when (rv /= 0) (connectionError mysql_)
doCommit :: ForeignPtr MYSQL -> IO ()
doCommit = doQuery "COMMIT"
doRollback :: ForeignPtr MYSQL -> IO ()
doRollback = doQuery "ROLLBACK"
doStartTransaction :: ForeignPtr MYSQL -> IO ()
doStartTransaction = doQuery "START TRANSACTION"
doGetTables :: ForeignPtr MYSQL -> IO [String]
doGetTables mysql__ = do
stmt <- newStatement mysql__ "SHOW TABLES"
Types.execute stmt []
rows <- unfoldRows stmt
return $ map (fromSql . head) rows
where fromSql :: Types.SqlValue -> String
fromSql (Types.SqlString s) = s
fromSql _ = error "SHOW TABLES returned a table whose name wasn't a string"
doDescribeTable :: ForeignPtr MYSQL -> String -> IO [(String, ColTypes.SqlColDesc)]
doDescribeTable mysql__ table = do
stmt <- newStatement mysql__ ("DESCRIBE " ++ table)
Types.execute stmt []
rows <- unfoldRows stmt
return $ map fromRow rows
where fromRow :: [Types.SqlValue] -> (String, ColTypes.SqlColDesc)
fromRow ((Types.SqlString colname)
:(Types.SqlString coltype)
:(Types.SqlString nullAllowed):_) =
let sqlTypeId = typeIdOfString coltype
nullable = Just $ nullAllowed == "YES"
in (colname, ColTypes.SqlColDesc sqlTypeId Nothing Nothing Nothing nullable)
fromRow _ = throwDyn $ Types.SqlError "" 0 "DESCRIBE failed"
typeIdOfString :: String -> ColTypes.SqlTypeId
typeIdOfString s
| "int" `isPrefixOf` s = ColTypes.SqlIntegerT
| "bigint" `isPrefixOf` s = ColTypes.SqlIntegerT
| "smallint" `isPrefixOf` s = ColTypes.SqlSmallIntT
| "mediumint" `isPrefixOf` s = ColTypes.SqlIntegerT
| "tinyint" `isPrefixOf` s = ColTypes.SqlTinyIntT
| "decimal" `isPrefixOf` s = ColTypes.SqlDecimalT
| "double" `isPrefixOf` s = ColTypes.SqlDoubleT
| "float" `isPrefixOf` s = ColTypes.SqlFloatT
| "char" `isPrefixOf` s = ColTypes.SqlCharT
| "varchar" `isPrefixOf` s = ColTypes.SqlVarCharT
| "text" `isPrefixOf` s = ColTypes.SqlBinaryT
| "timestamp" `isPrefixOf` s = ColTypes.SqlTimestampT
| "datetime" `isPrefixOf` s = ColTypes.SqlTimestampT
| "date" `isPrefixOf` s = ColTypes.SqlDateT
| "time" `isPrefixOf` s = ColTypes.SqlTimeT
| otherwise = ColTypes.SqlUnknownT s
unfoldRows :: Types.Statement -> IO [[Types.SqlValue]]
unfoldRows stmt = do
row <- Types.fetchRow stmt
case row of
Nothing -> return []
Just (vals) -> do rows <- unfoldRows stmt
return (vals : rows)
statementError :: Ptr MYSQL_STMT -> IO a
statementError stmt_ = do
errno <- mysql_stmt_errno stmt_
msg <- peekCString =<< mysql_stmt_error stmt_
throwDyn $ Types.SqlError "" (fromIntegral errno) msg
connectionError :: Ptr MYSQL -> IO a
connectionError mysql_ = do
errno <- mysql_errno mysql_
msg <- peekCString =<< mysql_error mysql_
throwDyn $ Types.SqlError "" (fromIntegral errno) msg
foreign import ccall unsafe mysql_get_client_info
:: IO CString
foreign import ccall unsafe mysql_get_server_info
:: Ptr MYSQL -> IO CString
foreign import ccall unsafe mysql_get_proto_info
:: Ptr MYSQL -> IO CUInt
foreign import ccall unsafe mysql_init
:: Ptr MYSQL
-> IO (Ptr MYSQL)
foreign import ccall unsafe mysql_real_connect
:: Ptr MYSQL
-> CString
-> CString
-> CString
-> CString
-> CInt
-> CString
-> IO (Ptr MYSQL)
foreign import ccall unsafe "&mysql_close" mysql_close
:: FunPtr (Ptr MYSQL -> IO ())
foreign import ccall unsafe mysql_stmt_init
:: Ptr MYSQL -> IO (Ptr MYSQL_STMT)
foreign import ccall unsafe mysql_stmt_prepare
:: Ptr MYSQL_STMT -> CString -> CInt -> IO CInt
foreign import ccall unsafe mysql_stmt_result_metadata
:: Ptr MYSQL_STMT -> IO (Ptr MYSQL_RES)
foreign import ccall unsafe mysql_stmt_bind_param
:: Ptr MYSQL_STMT -> Ptr MYSQL_BIND -> IO CChar
foreign import ccall unsafe mysql_stmt_bind_result
:: Ptr MYSQL_STMT -> Ptr MYSQL_BIND -> IO CChar
foreign import ccall unsafe mysql_stmt_param_count
:: Ptr MYSQL_STMT -> IO CULong
foreign import ccall unsafe mysql_free_result
:: Ptr MYSQL_RES -> IO ()
foreign import ccall unsafe mysql_stmt_execute
:: Ptr MYSQL_STMT -> IO CInt
foreign import ccall unsafe mysql_stmt_affected_rows
:: Ptr MYSQL_STMT -> IO CULLong
foreign import ccall unsafe mysql_fetch_field
:: Ptr MYSQL_RES -> IO (Ptr MYSQL_FIELD)
foreign import ccall unsafe mysql_stmt_fetch
:: Ptr MYSQL_STMT -> IO CInt
foreign import ccall unsafe "&mysql_stmt_close" mysql_stmt_close
:: FunPtr (Ptr MYSQL_STMT -> IO ())
foreign import ccall unsafe mysql_stmt_errno
:: Ptr MYSQL_STMT -> IO CInt
foreign import ccall unsafe mysql_stmt_error
:: Ptr MYSQL_STMT -> IO CString
foreign import ccall unsafe mysql_errno
:: Ptr MYSQL -> IO CInt
foreign import ccall unsafe mysql_error
:: Ptr MYSQL -> IO CString
foreign import ccall unsafe mysql_autocommit
:: Ptr MYSQL -> CChar -> IO CChar
foreign import ccall unsafe mysql_query
:: Ptr MYSQL -> CString -> IO CInt
foreign import ccall unsafe memset
:: Ptr () -> CInt -> CSize -> IO ()