{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module Database.ODBC.Internal
(
connect
, close
, withConnection
, Connection
, exec
, query
, Value(..)
, Binary(..)
, stream
, Step(..)
, ODBCException(..)
) where
import Control.Concurrent.Async
import Control.Concurrent.MVar
import Control.DeepSeq
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.IO.Unlift
import Data.ByteString (ByteString)
import qualified Data.ByteString.Unsafe as S
import Data.Coerce
import Data.Data
import Data.Int
import Data.String
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Foreign as T
import Data.Time
import Foreign hiding (void)
import Foreign.C
import GHC.Generics
newtype Connection = Connection
{connectionMVar :: MVar (Maybe (ForeignPtr EnvAndDbc))}
data ODBCException
= UnsuccessfulReturnCode !String
!Int16
!String
| AllocationReturnedNull !String
| UnknownDataType !String
!Int16
| DatabaseIsClosed !String
| DatabaseAlreadyClosed
| NoTotalInformation !Int
| DataRetrievalError !String
deriving (Typeable, Show, Eq)
instance Exception ODBCException
data Value
= TextValue !Text
| ByteStringValue !ByteString
| BinaryValue !Binary
| BoolValue !Bool
| DoubleValue !Double
| FloatValue !Float
| IntValue !Int
| ByteValue !Word8
| DayValue !Day
| TimeOfDayValue !TimeOfDay
| LocalTimeValue !LocalTime
deriving (Eq, Show, Typeable, Ord, Generic, Data)
instance NFData Value
newtype Binary = Binary
{ unBinary :: ByteString
} deriving (Show, Eq, Ord, Data, Generic, Typeable, NFData)
data Step a
= Stop !a
| Continue !a
deriving (Show)
data Column = Column
{ columnType :: !SQLSMALLINT
, columnSize :: !SQLULEN
, columnDigits :: !SQLSMALLINT
, columnNull :: !SQLSMALLINT
} deriving (Show)
connect ::
MonadIO m
=> Text
-> m Connection
connect string =
withBound
(do (ptr, envAndDbc) <-
uninterruptibleMask_
(do ptr <- assertNotNull "odbc_AllocEnvAndDbc" odbc_AllocEnvAndDbc
fmap (ptr, ) (newForeignPtr odbc_FreeEnvAndDbc (coerce ptr)))
withCStringLen
(T.unpack string)
(\(wstring,len) ->
uninterruptibleMask_
(do assertSuccess
ptr
"odbc_SQLDriverConnect"
(withForeignPtr
envAndDbc
(\dbcPtr ->
odbc_SQLDriverConnect
dbcPtr
(coerce wstring)
(fromIntegral len)))
addForeignPtrFinalizer odbc_SQLDisconnect envAndDbc))
mvar <- newMVar (Just envAndDbc)
pure (Connection mvar))
close ::
MonadIO m
=> Connection
-> m ()
close conn =
withBound
(do mstate <- modifyMVar (connectionMVar conn) (pure . (Nothing, ))
maybe (throwIO DatabaseAlreadyClosed) finalizeForeignPtr mstate)
withConnection :: MonadUnliftIO m =>
Text
-> (Connection -> m a)
-> m a
withConnection str inner = withRunInIO $ \io ->
withBound $ bracket (connect str) close (\h -> io (inner h))
exec ::
MonadIO m
=> Connection
-> Text
-> m ()
exec conn string =
withBound
(withHDBC
conn
"exec"
(\dbc -> withExecDirect dbc string (fetchAllResults dbc)))
query ::
MonadIO m
=> Connection
-> Text
-> m [[Maybe Value]]
query conn string =
withBound
(withHDBC
conn
"query"
(\dbc -> withExecDirect dbc string (fetchStatementRows dbc)))
stream ::
(MonadIO m, MonadUnliftIO m)
=> Connection
-> Text
-> (state -> [Maybe Value] -> m (Step state))
-> state
-> m state
stream conn string step state = do
unlift <- askUnliftIO
withBound
(withHDBC
conn
"stream"
(\dbc ->
withExecDirect
dbc
string
(fetchIterator dbc unlift step state)))
withHDBC :: Connection -> String -> (Ptr EnvAndDbc -> IO a) -> IO a
withHDBC conn label f =
withMVar
(connectionMVar conn)
(\mfptr ->
case mfptr of
Nothing -> throwIO (DatabaseIsClosed label)
Just envAndDbc -> do
v <- withForeignPtr envAndDbc f
touchForeignPtr envAndDbc
pure v)
withExecDirect :: Ptr EnvAndDbc -> Text -> (forall s. SQLHSTMT s -> IO a) -> IO a
withExecDirect dbc string cont =
withStmt
dbc
(\stmt -> do
void
(assertSuccessOrNoData
dbc
"odbc_SQLExecDirectW"
(T.useAsPtr
string
(\wstring len ->
odbc_SQLExecDirectW
dbc
stmt
(coerce wstring)
(fromIntegral len))))
cont stmt)
withStmt :: Ptr EnvAndDbc -> (forall s. SQLHSTMT s -> IO a) -> IO a
withStmt hdbc =
bracket
(assertNotNull "odbc_SQLAllocStmt" (odbc_SQLAllocStmt hdbc))
odbc_SQLFreeStmt
withBound :: MonadIO m => IO a -> m a
withBound = liftIO . flip withAsyncBound wait
fetchIterator ::
Ptr EnvAndDbc
-> UnliftIO m
-> (state -> [Maybe Value] -> m (Step state))
-> state
-> SQLHSTMT s
-> IO state
fetchIterator dbc (UnliftIO runInIO) step state0 stmt = do
SQLSMALLINT cols <-
liftIO
(withMalloc
(\sizep -> do
assertSuccess
dbc
"odbc_SQLNumResultCols"
(odbc_SQLNumResultCols stmt sizep)
peek sizep))
types <- mapM (describeColumn dbc stmt) [1 .. cols]
let loop state = do
do retcode0 <- odbc_SQLFetch dbc stmt
if | retcode0 == sql_no_data ->
do retcode <- odbc_SQLMoreResults dbc stmt
if retcode == sql_success || retcode == sql_success_with_info
then loop state
else pure state
| retcode0 == sql_success || retcode0 == sql_success_with_info ->
do row <-
sequence
(zipWith (getData dbc stmt) [SQLUSMALLINT 1 ..] types)
!state' <- runInIO (step state row)
case state' of
Stop state'' -> pure state''
Continue state'' -> loop state''
| otherwise ->
throwIO
(UnsuccessfulReturnCode
"odbc_SQLFetch"
(coerce retcode0)
"Unexpected return code")
if cols > 0
then loop state0
else pure state0
fetchAllResults :: Ptr EnvAndDbc -> SQLHSTMT s -> IO ()
fetchAllResults dbc stmt = do
retcode <-
assertSuccessOrNoData
dbc
"odbc_SQLMoreResults"
(odbc_SQLMoreResults dbc stmt)
when
(retcode == sql_success || retcode == sql_success_with_info)
(fetchAllResults dbc stmt)
fetchStatementRows :: Ptr EnvAndDbc -> SQLHSTMT s -> IO [[Maybe Value]]
fetchStatementRows dbc stmt = do
SQLSMALLINT cols <-
withMalloc
(\sizep -> do
assertSuccess
dbc
"odbc_SQLNumResultCols"
(odbc_SQLNumResultCols stmt sizep)
peek sizep)
types <- mapM (describeColumn dbc stmt) [1 .. cols]
let loop rows = do
do retcode0 <- odbc_SQLFetch dbc stmt
if | retcode0 == sql_no_data ->
do retcode <- odbc_SQLMoreResults dbc stmt
if retcode == sql_success || retcode == sql_success_with_info
then loop rows
else pure (rows [])
| retcode0 == sql_success || retcode0 == sql_success_with_info ->
do fields <-
sequence
(zipWith (getData dbc stmt) [SQLUSMALLINT 1 ..] types)
loop (rows . (fields :))
| otherwise ->
throwIO
(UnsuccessfulReturnCode
"odbc_SQLFetch"
(coerce retcode0)
"Unexpected return code")
if cols > 0
then loop id
else pure []
describeColumn :: Ptr EnvAndDbc -> SQLHSTMT s -> Int16 -> IO Column
describeColumn dbPtr stmt i =
T.useAsPtr
(T.replicate 1000 (fromString "0"))
(\namep namelen ->
(withMalloc
(\namelenp ->
(withMalloc
(\typep ->
withMalloc
(\sizep ->
withMalloc
(\digitsp ->
withMalloc
(\nullp -> do
assertSuccess
dbPtr
"odbc_SQLDescribeColW"
(odbc_SQLDescribeColW
stmt
(SQLUSMALLINT (fromIntegral i))
(coerce namep)
(SQLSMALLINT (fromIntegral namelen))
namelenp
typep
sizep
digitsp
nullp)
typ <- peek typep
size <- peek sizep
digits <- peek digitsp
isnull <- peek nullp
evaluate
Column
{ columnType = typ
, columnSize = size
, columnDigits = digits
, columnNull = isnull
}))))))))
getData :: Ptr EnvAndDbc -> SQLHSTMT s -> SQLUSMALLINT -> Column -> IO (Maybe Value)
getData dbc stmt i col =
if | colType == sql_longvarchar -> getBytesData dbc stmt i
| colType == sql_varchar -> getBytesData dbc stmt i
| colType == sql_char -> getBytesData dbc stmt i
| colType == sql_wvarchar -> getTextData dbc stmt i
| colType == sql_wlongvarchar -> getTextData dbc stmt i
| colType == sql_binary -> getBinaryData dbc stmt i
| colType == sql_varbinary -> getBinaryData dbc stmt i
| colType == sql_bit ->
withMalloc
(\bitPtr -> do
mlen <- getTypedData dbc stmt sql_c_bit i (coerce bitPtr) (SQLLEN 1)
case mlen of
Nothing -> pure Nothing
Just {} ->
fmap (Just . BoolValue . (/= (0 :: Word8))) (peek bitPtr))
| colType == sql_double ->
withMalloc
(\doublePtr -> do
mlen <-
getTypedData dbc stmt sql_c_double i (coerce doublePtr) (SQLLEN 8)
case mlen of
Nothing -> pure Nothing
Just {} -> do
!d <- fmap DoubleValue (peek doublePtr)
pure (Just d))
| colType == sql_float ->
withMalloc
(\floatPtr -> do
mlen <-
getTypedData dbc stmt sql_c_double i (coerce floatPtr) (SQLLEN 8)
case mlen of
Nothing -> pure Nothing
Just {} -> do
!d <- fmap DoubleValue (peek floatPtr)
pure (Just d))
| colType == sql_real ->
withMalloc
(\floatPtr -> do
mlen <-
getTypedData dbc stmt sql_c_double i (coerce floatPtr) (SQLLEN 8)
case mlen of
Nothing -> pure Nothing
Just {} -> do
!d <-
fmap
(FloatValue . (realToFrac :: Double -> Float))
(peek floatPtr)
pure (Just d))
| colType == sql_numeric || colType == sql_decimal ->
withMalloc
(\floatPtr -> do
mlen <-
getTypedData dbc stmt sql_c_double i (coerce floatPtr) (SQLLEN 8)
case mlen of
Nothing -> pure Nothing
Just {} -> do
!d <- fmap DoubleValue (peek floatPtr)
pure (Just d))
| colType == sql_integer ->
withMalloc
(\intPtr -> do
mlen <-
getTypedData dbc stmt sql_c_long i (coerce intPtr) (SQLLEN 4)
case mlen of
Nothing -> pure Nothing
Just {} ->
fmap
(Just . IntValue . fromIntegral)
(peek (intPtr :: Ptr Int32)))
| colType == sql_bigint ->
withMalloc
(\intPtr -> do
mlen <-
getTypedData dbc stmt sql_c_bigint i (coerce intPtr) (SQLLEN 8)
case mlen of
Nothing -> pure Nothing
Just {} ->
fmap
(Just . IntValue . fromIntegral)
(peek (intPtr :: Ptr Int64)))
| colType == sql_smallint ->
withMalloc
(\intPtr -> do
mlen <-
getTypedData dbc stmt sql_c_short i (coerce intPtr) (SQLLEN 2)
case mlen of
Nothing -> pure Nothing
Just {} ->
fmap
(Just . IntValue . fromIntegral)
(peek (intPtr :: Ptr Int16)))
| colType == sql_tinyint ->
withMalloc
(\intPtr -> do
mlen <-
getTypedData dbc stmt sql_c_short i (coerce intPtr) (SQLLEN 1)
case mlen of
Nothing -> pure Nothing
Just {} -> fmap (Just . ByteValue) (peek (intPtr :: Ptr Word8)))
| colType == sql_type_date ->
withMallocBytes
3
(\datePtr -> do
mlen <-
getTypedData dbc stmt sql_c_date i (coerce datePtr) (SQLLEN 3)
case mlen of
Nothing -> pure Nothing
Just {} ->
fmap
(Just . DayValue)
(fromGregorian <$>
(fmap fromIntegral (odbc_DATE_STRUCT_year datePtr)) <*>
(fmap fromIntegral (odbc_DATE_STRUCT_month datePtr)) <*>
(fmap fromIntegral (odbc_DATE_STRUCT_day datePtr))))
| colType == sql_ss_time2 ->
withCallocBytes
12
(\datePtr -> do
mlen <-
getTypedData
dbc
stmt
sql_c_time
i
(coerce datePtr)
(SQLLEN 12)
case mlen of
Nothing -> pure Nothing
Just {} ->
fmap
(Just . TimeOfDayValue)
(TimeOfDay <$>
(fmap fromIntegral (odbc_TIME_STRUCT_hour datePtr)) <*>
(fmap fromIntegral (odbc_TIME_STRUCT_minute datePtr)) <*>
(fmap fromIntegral (odbc_TIME_STRUCT_second datePtr))))
| colType == sql_type_timestamp ->
withMallocBytes
16
(\timestampPtr -> do
mlen <-
getTypedData
dbc
stmt
sql_c_type_timestamp
i
(coerce timestampPtr)
(SQLLEN 16)
case mlen of
Nothing -> pure Nothing
Just {} ->
fmap
(Just . LocalTimeValue)
(LocalTime <$>
(fromGregorian <$>
(fmap fromIntegral (odbc_TIMESTAMP_STRUCT_year timestampPtr)) <*>
(fmap
fromIntegral
(odbc_TIMESTAMP_STRUCT_month timestampPtr)) <*>
(fmap fromIntegral (odbc_TIMESTAMP_STRUCT_day timestampPtr))) <*>
(TimeOfDay <$>
(fmap fromIntegral (odbc_TIMESTAMP_STRUCT_hour timestampPtr)) <*>
(fmap
fromIntegral
(odbc_TIMESTAMP_STRUCT_minute timestampPtr)) <*>
((+) <$>
(fmap
fromIntegral
(odbc_TIMESTAMP_STRUCT_second timestampPtr)) <*>
(fmap
(\frac -> fromIntegral frac / 1000000000)
(odbc_TIMESTAMP_STRUCT_fraction timestampPtr))))))
| colType == sql_guid -> getGuid dbc stmt i
| otherwise ->
throwIO
(UnknownDataType
"getData"
(let SQLSMALLINT n = colType
in n))
where
colType = columnType col
getGuid :: Ptr EnvAndDbc -> SQLHSTMT s -> SQLUSMALLINT -> IO (Maybe Value)
getGuid dbc stmt column =
uninterruptibleMask_
(do bufferp <- callocBytes odbcGuidBytes
void
(getTypedData
dbc
stmt
sql_c_binary
column
(coerce bufferp)
(SQLLEN odbcGuidBytes))
!bs <- S.unsafePackMallocCStringLen (bufferp, odbcGuidBytes)
evaluate (Just (BinaryValue (Binary bs))))
getBytesData :: Ptr EnvAndDbc -> SQLHSTMT s -> SQLUSMALLINT -> IO (Maybe Value)
getBytesData dbc stmt column = do
mavailableBytes <- getSize dbc stmt sql_c_binary column
case mavailableBytes of
Just 0 -> pure (Just (ByteStringValue mempty))
Just availableBytes ->
uninterruptibleMask_
(do let allocBytes = availableBytes + 1
bufferp <- callocBytes (fromIntegral allocBytes)
void
(getTypedData
dbc
stmt
sql_c_binary
column
(coerce bufferp)
(SQLLEN (fromIntegral allocBytes)))
bs <-
S.unsafePackMallocCStringLen
(bufferp, fromIntegral availableBytes)
evaluate (Just (ByteStringValue bs)))
Nothing -> pure Nothing
getBinaryData :: Ptr EnvAndDbc -> SQLHSTMT s -> SQLUSMALLINT -> IO (Maybe Value)
getBinaryData dbc stmt column = do
mavailableBinary <- getSize dbc stmt sql_c_binary column
case mavailableBinary of
Just 0 -> pure (Just (BinaryValue (Binary mempty)))
Just availableBinary ->
uninterruptibleMask_
(do let allocBinary = availableBinary
bufferp <- callocBytes (fromIntegral allocBinary)
void
(getTypedData
dbc
stmt
sql_c_binary
column
(coerce bufferp)
(SQLLEN (fromIntegral allocBinary)))
bs <-
S.unsafePackMallocCStringLen
(bufferp, fromIntegral availableBinary)
evaluate (Just (BinaryValue (Binary bs))))
Nothing -> pure Nothing
getTextData :: Ptr EnvAndDbc -> SQLHSTMT s -> SQLUSMALLINT -> IO (Maybe Value)
getTextData dbc stmt column = do
mavailableChars <- getSize dbc stmt sql_c_wchar column
case mavailableChars of
Just 0 -> pure (Just (TextValue mempty))
Nothing -> pure Nothing
Just availableBytes -> do
let allocBytes = availableBytes + 2
withMallocBytes
(fromIntegral allocBytes)
(\bufferp -> do
void
(getTypedData
dbc
stmt
sql_c_wchar
column
(coerce bufferp)
(SQLLEN (fromIntegral allocBytes)))
t <- T.fromPtr bufferp (fromIntegral (div availableBytes 2))
let !v = TextValue t
pure (Just v))
getTypedData ::
Ptr EnvAndDbc
-> SQLHSTMT s
-> SQLCTYPE
-> SQLUSMALLINT
-> SQLPOINTER
-> SQLLEN
-> IO (Maybe Int64)
getTypedData dbc stmt ty column bufferp bufferlen =
withMalloc
(\copiedPtr -> do
assertSuccess
dbc
("getTypedData ty=" ++ show ty)
(odbc_SQLGetData dbc stmt column ty bufferp bufferlen copiedPtr)
copiedBytes <- peek copiedPtr
if copiedBytes == sql_null_data
then pure Nothing
else pure (Just (coerce copiedBytes :: Int64)))
getSize :: Ptr EnvAndDbc -> SQLHSTMT s -> SQLCTYPE -> SQLUSMALLINT -> IO (Maybe Int64)
getSize dbc stmt ty column =
withMalloc
(\availablePtr -> do
withMalloc
(\bufferp ->
assertSuccess
dbc
"getSize"
(odbc_SQLGetData
dbc
stmt
column
ty
(coerce (bufferp :: Ptr CChar))
0
availablePtr))
availableBytes <- peek availablePtr
if availableBytes == sql_null_data
then pure Nothing
else if availableBytes == sql_no_total
then throwIO
(NoTotalInformation
(let SQLUSMALLINT i = column
in fromIntegral i))
else pure (Just (coerce availableBytes :: Int64)))
assertNotNull :: (Coercible a (Ptr ())) => String -> IO a -> IO a
assertNotNull label m = do
val <- m
if coerce val == nullPtr
then throwIO (AllocationReturnedNull label)
else pure val
assertSuccess :: Ptr EnvAndDbc -> String -> IO RETCODE -> IO ()
assertSuccess dbc label m = do
retcode <- m
if retcode == sql_success || retcode == sql_success_with_info
then pure ()
else do
ptr <- odbc_error dbc
string <-
if nullPtr == ptr
then pure ""
else peekCString ptr
throwIO (UnsuccessfulReturnCode label (coerce retcode) string)
assertSuccessOrNoData :: Ptr EnvAndDbc -> String -> IO RETCODE -> IO RETCODE
assertSuccessOrNoData dbc label m = do
retcode <- m
if retcode == sql_success ||
retcode == sql_success_with_info || retcode == sql_no_data
then pure retcode
else do
ptr <- odbc_error dbc
string <-
if nullPtr == ptr
then pure ""
else peekCString ptr
throwIO (UnsuccessfulReturnCode label (coerce retcode) string)
data EnvAndDbc
newtype SQLHSTMT s = SQLHSTMT (Ptr (SQLHSTMT s))
newtype SQLPOINTER = SQLPOINTER (Ptr SQLPOINTER)
newtype SQLCTYPE =
SQLCTYPE Int16
deriving (Show, Eq, Storable, Integral, Enum, Real, Num, Ord)
newtype RETCODE = RETCODE Int16
deriving (Show, Eq)
newtype SQLUSMALLINT = SQLUSMALLINT Word16 deriving (Show, Eq, Storable, Integral, Enum, Real, Num, Ord)
newtype SQLUCHAR = SQLUCHAR Word8 deriving (Show, Eq, Storable)
newtype SQLCHAR = SQLCHAR CChar deriving (Show, Eq, Storable)
newtype SQLSMALLINT = SQLSMALLINT Int16 deriving (Show, Eq, Storable, Num, Integral, Enum, Ord, Real)
newtype SQLLEN = SQLLEN Int64 deriving (Show, Eq, Storable, Num)
newtype SQLULEN = SQLULEN Word64 deriving (Show, Eq, Storable)
newtype SQLINTEGER = SQLINTEGER Int64 deriving (Show, Eq, Storable, Num)
newtype SQLUINTEGER = SQLUINTEGER Word64 deriving (Show, Eq, Storable, Num, Integral, Enum, Ord, Real)
newtype SQLWCHAR = SQLWCHAR CWString deriving (Show, Eq, Storable)
data DATE_STRUCT
data TIME_STRUCT
data TIMESTAMP_STRUCT
foreign import ccall "odbc odbc_error"
odbc_error :: Ptr EnvAndDbc -> IO (Ptr CChar)
foreign import ccall "odbc odbc_AllocEnvAndDbc"
odbc_AllocEnvAndDbc :: IO (Ptr EnvAndDbc)
foreign import ccall "odbc &odbc_FreeEnvAndDbc"
odbc_FreeEnvAndDbc :: FunPtr (Ptr EnvAndDbc -> IO ())
foreign import ccall "odbc odbc_SQLDriverConnect"
odbc_SQLDriverConnect :: Ptr EnvAndDbc -> Ptr SQLCHAR -> SQLSMALLINT -> IO RETCODE
foreign import ccall "odbc &odbc_SQLDisconnect"
odbc_SQLDisconnect :: FunPtr (Ptr EnvAndDbc -> IO ())
foreign import ccall "odbc odbc_SQLAllocStmt"
odbc_SQLAllocStmt :: Ptr EnvAndDbc -> IO (SQLHSTMT s)
foreign import ccall "odbc odbc_SQLFreeStmt"
odbc_SQLFreeStmt :: SQLHSTMT s -> IO ()
foreign import ccall "odbc odbc_SQLExecDirectW"
odbc_SQLExecDirectW :: Ptr EnvAndDbc -> SQLHSTMT s -> SQLWCHAR -> SQLINTEGER -> IO RETCODE
foreign import ccall "odbc odbc_SQLFetch"
odbc_SQLFetch :: Ptr EnvAndDbc -> SQLHSTMT s -> IO RETCODE
foreign import ccall "odbc odbc_SQLMoreResults"
odbc_SQLMoreResults :: Ptr EnvAndDbc -> SQLHSTMT s -> IO RETCODE
foreign import ccall "odbc odbc_SQLNumResultCols"
odbc_SQLNumResultCols :: SQLHSTMT s -> Ptr SQLSMALLINT -> IO RETCODE
foreign import ccall "odbc odbc_SQLGetData"
odbc_SQLGetData
:: Ptr EnvAndDbc
-> SQLHSTMT s
-> SQLUSMALLINT
-> SQLCTYPE
-> SQLPOINTER
-> SQLLEN
-> Ptr SQLLEN
-> IO RETCODE
foreign import ccall "odbc odbc_SQLDescribeColW"
odbc_SQLDescribeColW
:: SQLHSTMT s
-> SQLUSMALLINT
-> Ptr SQLWCHAR
-> SQLSMALLINT
-> Ptr SQLSMALLINT
-> Ptr SQLSMALLINT
-> Ptr SQLULEN
-> Ptr SQLSMALLINT
-> Ptr SQLSMALLINT
-> IO RETCODE
foreign import ccall "odbc DATE_STRUCT_year" odbc_DATE_STRUCT_year
:: Ptr DATE_STRUCT -> IO SQLSMALLINT
foreign import ccall "odbc DATE_STRUCT_month" odbc_DATE_STRUCT_month
:: Ptr DATE_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc DATE_STRUCT_day" odbc_DATE_STRUCT_day
:: Ptr DATE_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc TIME_STRUCT_hour" odbc_TIME_STRUCT_hour
:: Ptr TIME_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc TIME_STRUCT_minute" odbc_TIME_STRUCT_minute
:: Ptr TIME_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc TIME_STRUCT_second" odbc_TIME_STRUCT_second
:: Ptr TIME_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc TIMESTAMP_STRUCT_year" odbc_TIMESTAMP_STRUCT_year
:: Ptr TIMESTAMP_STRUCT -> IO SQLSMALLINT
foreign import ccall "odbc TIMESTAMP_STRUCT_month" odbc_TIMESTAMP_STRUCT_month
:: Ptr TIMESTAMP_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc TIMESTAMP_STRUCT_day" odbc_TIMESTAMP_STRUCT_day
:: Ptr TIMESTAMP_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc TIMESTAMP_STRUCT_hour" odbc_TIMESTAMP_STRUCT_hour
:: Ptr TIMESTAMP_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc TIMESTAMP_STRUCT_minute" odbc_TIMESTAMP_STRUCT_minute
:: Ptr TIMESTAMP_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc TIMESTAMP_STRUCT_second" odbc_TIMESTAMP_STRUCT_second
:: Ptr TIMESTAMP_STRUCT -> IO SQLUSMALLINT
foreign import ccall "odbc TIMESTAMP_STRUCT_fraction" odbc_TIMESTAMP_STRUCT_fraction
:: Ptr TIMESTAMP_STRUCT -> IO SQLUINTEGER
withMalloc :: Storable a => (Ptr a -> IO b) -> IO b
withMalloc m = bracket malloc free m
withMallocBytes :: Int -> (Ptr a -> IO b) -> IO b
withMallocBytes n m = bracket (mallocBytes n) free m
withCallocBytes :: Int -> (Ptr a -> IO b) -> IO b
withCallocBytes n m = bracket (callocBytes n) free m
odbcGuidBytes :: Integral a => a
odbcGuidBytes = 16
sql_success :: RETCODE
sql_success = RETCODE 0
sql_success_with_info :: RETCODE
sql_success_with_info = RETCODE 1
sql_no_data :: RETCODE
sql_no_data = RETCODE 100
sql_null_data :: SQLLEN
sql_null_data = (-1)
sql_no_total :: SQLLEN
sql_no_total = (-4)
sql_char :: SQLSMALLINT
sql_char = 1
sql_numeric :: SQLSMALLINT
sql_numeric = 2
sql_decimal :: SQLSMALLINT
sql_decimal = 3
sql_integer :: SQLSMALLINT
sql_integer = 4
sql_smallint :: SQLSMALLINT
sql_smallint = 5
sql_float :: SQLSMALLINT
sql_float = 6
sql_real :: SQLSMALLINT
sql_real = 7
sql_double :: SQLSMALLINT
sql_double = 8
sql_type_date :: SQLSMALLINT
sql_type_date = 91
sql_ss_time2 :: SQLSMALLINT
sql_ss_time2 = -154
sql_varchar :: SQLSMALLINT
sql_varchar = 12
sql_wchar :: SQLSMALLINT
sql_wchar = (-8)
sql_wvarchar :: SQLSMALLINT
sql_wvarchar = (-9)
sql_wlongvarchar :: SQLSMALLINT
sql_wlongvarchar = (-10)
sql_time :: SQLSMALLINT
sql_time = 10
sql_type_timestamp :: SQLSMALLINT
sql_type_timestamp = 93
sql_longvarchar :: SQLSMALLINT
sql_longvarchar = (-1)
sql_binary :: SQLSMALLINT
sql_binary = (-2)
sql_varbinary :: SQLSMALLINT
sql_varbinary = (-3)
sql_bigint :: SQLSMALLINT
sql_bigint = (-5)
sql_tinyint :: SQLSMALLINT
sql_tinyint = (-6)
sql_bit :: SQLSMALLINT
sql_bit = (-7)
sql_guid :: SQLSMALLINT
sql_guid = (-11)
sql_c_wchar :: SQLCTYPE
sql_c_wchar = coerce sql_wchar
sql_c_binary :: SQLCTYPE
sql_c_binary = coerce sql_binary
sql_c_double :: SQLCTYPE
sql_c_double = coerce sql_double
sql_c_long :: SQLCTYPE
sql_c_long = coerce sql_integer
sql_c_bigint :: SQLCTYPE
sql_c_bigint = coerce (sql_bigint - 20)
sql_c_short :: SQLCTYPE
sql_c_short = coerce sql_smallint
sql_c_bit :: SQLCTYPE
sql_c_bit = coerce sql_bit
sql_c_date :: SQLCTYPE
sql_c_date = coerce (9 :: SQLSMALLINT)
sql_c_type_timestamp :: SQLCTYPE
sql_c_type_timestamp = coerce sql_type_timestamp
sql_c_time :: SQLCTYPE
sql_c_time = coerce sql_time