module Database.HDBC.ODBC.Statement (
fGetQueryInfo,
newSth,
fgettables,
fdescribetable
) where
import Database.HDBC.Types
import Database.HDBC
import Database.HDBC.DriverUtils
import Database.HDBC.ODBC.Api.Errors
import Database.HDBC.ODBC.Api.Imports
import Database.HDBC.ODBC.Api.Types
import Database.HDBC.ODBC.Utils
import Database.HDBC.ODBC.Log
import Database.HDBC.ODBC.TypeConv
import Database.HDBC.ODBC.Wrappers
import Foreign.C.String (castCUCharToChar)
import Foreign.C.Types
import Foreign.ForeignPtr
import Foreign.Ptr
import Control.Applicative
import Control.Concurrent.MVar
import Foreign.C.String
import Foreign.Marshal
import Foreign.Storable
import Control.Monad
import Data.Word
import Data.Time.Calendar (fromGregorian)
import Data.Time.LocalTime (TimeOfDay(TimeOfDay), LocalTime(LocalTime))
import Data.Int
import Data.Maybe (catMaybes, fromMaybe)
import qualified Data.ByteString as B
import qualified Data.ByteString.UTF8 as BUTF8
import qualified Data.ByteString.Unsafe as B
import Unsafe.Coerce (unsafeCoerce)
import System.IO (hPutStrLn, stderr)
import Debug.Trace
import qualified Data.Foldable as F
fGetQueryInfo :: DbcWrapper -> ChildList -> String
-> IO ([SqlColDesc], [(String, SqlColDesc)])
fGetQueryInfo iconn children query =
do hdbcTrace "in fGetQueryInfo"
sstate <- newSState iconn query
addChild children (wrapStmt sstate)
fakeExecute' sstate
fakeExecute' :: SState -> IO ([SqlColDesc], [(String, SqlColDesc)])
fakeExecute' sstate = do
hdbcTrace "fakeExecute'"
withStmtOrDie (sstmt sstate) $ \hStmt ->
withCStringLen (squery sstate) $ \(cquery, cqlen) -> do
hdbcTrace "fakeExecute' got stmt handle"
sqlPrepare hStmt cquery (fromIntegral cqlen) >>=
checkError "fakeExecute' prepare" (StmtHandle hStmt)
parmInfo <- fgetparminfo hStmt
colInfo <- fgetcolinfo hStmt
return (parmInfo, colInfo)
data SState = SState
{ sstmt :: StmtWrapper
, squery :: String
, stmtPrepared :: MVar Bool
, colinfomv :: MVar [(String, SqlColDesc)]
, bindColsMV :: MVar (Maybe [(BindCol, Ptr Int64)])
}
newSState :: DbcWrapper -> String -> IO SState
newSState indbo query = SState
<$> sqlAllocStmt indbo
<*> pure query
<*> newMVar False
<*> newMVar []
<*> newMVar Nothing
wrapStmt :: SState -> Statement
wrapStmt sstate = Statement
{ execute = fexecute sstate
, executeRaw = return ()
, executeMany = fexecutemany sstate
, finish = ffinish sstate
, fetchRow = ffetchrow sstate
, originalQuery = (squery sstate)
, getColumnNames = readMVar (colinfomv sstate) >>= (return . map fst)
, describeResult = readMVar (colinfomv sstate)
}
newSth :: DbcWrapper -> ChildList -> String -> IO Statement
newSth indbo mchildren query =
do hdbcTrace "in newSth"
sstate <- newSState indbo query
let retval = wrapStmt sstate
addChild mchildren retval
return retval
fgettables :: DbcWrapper -> IO [String]
fgettables iconn = do
hdbcTrace "fgettables"
sstate <- newSState iconn ""
withStmtOrDie (sstmt sstate) $ \hStmt -> do
hdbcTrace "fgettables got stmt handle"
simpleSqlTables hStmt >>= checkError "gettables simpleSqlTables" (StmtHandle hStmt)
fgetcolinfo hStmt >>= swapMVar (colinfomv sstate)
results <- fetchAllRows' $ wrapStmt sstate
return $ map (\x -> fromSql (x !! 2)) results
fdescribetable :: DbcWrapper -> String -> IO [(String, SqlColDesc)]
fdescribetable iconn tablename = do
hdbcTrace "fdescribetable"
B.useAsCStringLen (BUTF8.fromString tablename) $ \(cs, csl) -> do
sstate <- newSState iconn tablename
withStmtOrDie (sstmt sstate) $ \hStmt -> do
hdbcTrace "fdescribetable got stmt handle"
simpleSqlColumns hStmt cs (fromIntegral csl) >>= checkError "fdescribetable simpleSqlColumns" (StmtHandle hStmt)
fgetcolinfo hStmt >>= swapMVar (colinfomv sstate)
results <- fetchAllRows' $ wrapStmt sstate
hdbcTrace $ show results
return $ map fromOTypeCol results
fexecute :: SState -> [SqlValue] -> IO Integer
fexecute sstate args = do
hdbcTrace $ "fexecute: " ++ show (squery sstate) ++ show args
(finish, result) <- withStmtOrDie (sstmt sstate) $ \hStmt -> do
hdbcTrace "fexecute got stmt handle"
modifyMVar_ (stmtPrepared sstate) $ \prep -> do
unless prep $
B.useAsCStringLen (BUTF8.fromString (squery sstate)) $ \(cquery, cqlen) -> do
sqlPrepare hStmt cquery (fromIntegral cqlen) >>= checkError "execute prepare" (StmtHandle hStmt)
return True
bindArgs <- zipWithM (bindParam hStmt) args [1..]
hdbcTrace $ "Ready for sqlExecute: " ++ show (squery sstate) ++ show args
r <- sqlExecute hStmt
mapM_ (\(x, y) -> free x >> free y) (catMaybes bindArgs)
case r of
100 -> return ()
x -> checkError "execute execute" (StmtHandle hStmt) x
rc <- getNumResultCols hStmt
case rc of
0 -> do rowcount <- getSqlRowCount hStmt
return (True, fromIntegral rowcount)
colcount -> do fgetcolinfo hStmt >>= swapMVar (colinfomv sstate)
return (False, 0)
when finish $ ffinish sstate
return result
getNumResultCols :: SQLHSTMT -> IO Int16
getNumResultCols sthptr = alloca $ \pcount ->
do sqlNumResultCols sthptr pcount >>= checkError "SQLNumResultCols"
(StmtHandle sthptr)
peek pcount
bindParam :: SQLHSTMT -> SqlValue -> Word16
-> IO (Maybe (Ptr Int64, Ptr CChar))
bindParam sthptr arg icol = alloca $ \pdtype ->
alloca $ \pcolsize ->
alloca $ \pdecdigits ->
alloca $ \pnullable ->
do hdbcTrace $ "Binding col " ++ show icol ++ ": " ++ show arg
rc1 <- sqlDescribeParam sthptr icol pdtype pcolsize pdecdigits pnullable
hdbcTrace $ "rc1 is " ++ show (sqlSucceeded rc1)
coltype <- if sqlSucceeded rc1 then Just <$> peek pdtype else return Nothing
colsize <- if sqlSucceeded rc1 then Just <$> peek pcolsize else return Nothing
decdigits <- if sqlSucceeded rc1 then Just <$> peek pdecdigits else return Nothing
hdbcTrace $ "Results: " ++ show (coltype, colsize, decdigits)
case arg of
SqlNull ->
do hdbcTrace "Binding null"
rc2 <- sqlBindParameter sthptr (fromIntegral icol)
1
1
(fromMaybe 1 coltype)
(fromMaybe 0 colsize)
(fromMaybe 0 decdigits)
nullPtr 0 nullDataHDBC
checkError ("bindparameter NULL " ++ show icol)
(StmtHandle sthptr) rc2
return Nothing
x -> do
boundValue <- bindSqlValue x
do pcslen <- malloc
poke pcslen . fromIntegral $ bvBufferSize boundValue
rc2 <- sqlBindParameter sthptr (fromIntegral icol)
1
(bvValueType boundValue)
(fromMaybe (bvDefaultColumnType boundValue) coltype)
(fromMaybe (bvDefaultColumnSize boundValue) colsize)
(fromMaybe (bvDefaultDecDigits boundValue) decdigits)
(castPtr $ bvBuffer boundValue)
(bvBufferSize boundValue)
pcslen
if sqlSucceeded rc2
then do
return $ Just (pcslen, castPtr $ bvBuffer boundValue)
else do
free pcslen
free (bvBuffer boundValue)
checkError ("bindparameter " ++ show icol)
(StmtHandle sthptr) rc2
return Nothing
getSqlRowCount :: SQLHSTMT -> IO Int32
getSqlRowCount cstmt = alloca $ \prows ->
do sqlRowCount cstmt prows >>= checkError "SQLRowCount" (StmtHandle cstmt)
peek prows
data BoundValue = BoundValue {
bvValueType :: !(Int16)
, bvDefaultColumnType :: !(Int16)
, bvDefaultColumnSize :: !(Word64)
, bvDefaultDecDigits :: !(Int16)
, bvBuffer :: !(Ptr ())
, bvBufferSize :: !(Int64)
} deriving (Show)
bindSqlValue :: SqlValue -> IO BoundValue
bindSqlValue sqlValue = case sqlValue of
SqlString s -> do
let utf8ByteString = BUTF8.fromString s
B.unsafeUseAsCStringLen utf8ByteString $ \(unsafeStrPtr, strLen) -> do
safeStrPtr <- mallocBytes strLen
copyBytes safeStrPtr unsafeStrPtr strLen
let result = BoundValue
{ bvValueType = 1
, bvDefaultColumnType = 8
, bvDefaultColumnSize = fromIntegral strLen
, bvDefaultDecDigits = 0
, bvBuffer = castPtr safeStrPtr
, bvBufferSize = fromIntegral strLen
}
hdbcTrace $ "bind SqlString " ++ s ++ ": " ++ show result
return result
SqlByteString bs -> B.unsafeUseAsCStringLen bs $ \(s,len) -> do
res <- mallocBytes len
copyBytes res s len
let result = BoundValue
{ bvValueType = 2
, bvDefaultColumnType = 2
, bvDefaultColumnSize = fromIntegral len
, bvDefaultDecDigits = 0
, bvBuffer = castPtr res
, bvBufferSize = fromIntegral len
}
hdbcTrace $ "bind SqlByteString " ++ show bs ++ ": " ++ show result
return $! result
x -> do
hdbcTrace $ "bind other " ++ show x
bsResult <- bindSqlValue $ SqlByteString (fromSql x)
let result = bsResult
{ bvValueType = 1
, bvDefaultColumnType = 1
}
hdbcTrace $ "bound other " ++ show x ++ ": " ++ show result
return $! result
ffetchrow :: SState -> IO (Maybe [SqlValue])
ffetchrow sstate = do
result <- withMaybeStmt (sstmt sstate) $ \maybeStmt ->
case maybeStmt of
Nothing -> do
hdbcTrace "ffetchrow: no statement"
return Nothing
Just hStmt -> do
hdbcTrace "ffetchrow"
bindCols <- getBindCols sstate hStmt
hdbcTrace "ffetchrow: fetching"
rc <- sqlFetch hStmt
if rc == 100
then do
hdbcTrace "ffetchrow: no more rows"
return Nothing
else do
hdbcTrace "ffetchrow: fetching data"
checkError "sqlFetch" (StmtHandle hStmt) rc
sqlValues <- if rc == 0 || rc == 1
then mapM (bindColToSqlValue hStmt) bindCols
else raiseError "sqlGetData" rc (StmtHandle hStmt)
return $ Just sqlValues
case result of
Just x -> return $ Just x
Nothing -> do
ffinish sstate
return Nothing
getBindCols :: SState -> SQLHSTMT -> IO [(BindCol, Ptr Int64)]
getBindCols sstate cstmt = do
hdbcTrace "getBindCols"
modifyMVar (bindColsMV sstate) $ \mBindCols ->
case mBindCols of
Nothing -> do
cols <- getNumResultCols cstmt
pBindCols <- mapM (mkBindCol sstate cstmt) [1 .. cols]
return (Just pBindCols, pBindCols)
Just bindCols -> do
return (mBindCols, bindCols)
getLongColData cstmt bindCol = do
let (BindColString buf bufLen col) = bindCol
hdbcTrace $ "buflen: " ++ show bufLen
bs <- B.packCStringLen (buf, fromIntegral (bufLen 1))
hdbcTrace $ "sql_no_total col " ++ show (BUTF8.toString bs)
bs2 <- getRestLongColData cstmt 1 col bs
return $ SqlByteString bs2
getRestLongColData cstmt cBinding icol acc = do
hdbcTrace "getLongColData"
alloca $ \plen ->
allocaBytes colBufSizeMaximum $ \buf ->
do res <- sqlGetData cstmt (fromIntegral icol) cBinding
buf (fromIntegral colBufSizeMaximum) plen
if res == 0 || res == 1
then do
len <- peek plen
if len == 100
then return acc
else do
let bufmax = fromIntegral $ colBufSizeMaximum 1
bs <- B.packCStringLen (buf, fromIntegral (if len == 4 || len > bufmax then bufmax else len))
hdbcTrace $ "sql_no_total col is: " ++ show (BUTF8.toString bs)
let newacc = B.append acc bs
if len /= 4 && len <= bufmax
then return newacc
else getRestLongColData cstmt cBinding icol newacc
else raiseError "sqlGetData" res (StmtHandle cstmt)
getColData cstmt cBinding icol = do
alloca $ \plen ->
allocaBytes colBufSizeDefault $ \buf ->
do res <- sqlGetData cstmt (fromIntegral icol) cBinding
buf (fromIntegral colBufSizeDefault) plen
case res of
0 ->
do len <- peek plen
case len of
1 -> return SqlNull
4 -> fail $ "Unexpected SQL_NO_TOTAL"
_ -> do bs <- B.packCStringLen (buf, fromIntegral len)
hdbcTrace $ "col is: " ++ show (BUTF8.toString bs)
return (SqlByteString bs)
1 ->
do len <- peek plen
allocaBytes (fromIntegral len + 1) $ \buf2 ->
do sqlGetData cstmt (fromIntegral icol) cBinding
buf2 (fromIntegral len + 1) plen
>>= checkError "sqlGetData" (StmtHandle cstmt)
len2 <- peek plen
let firstbuf = case cBinding of
2 -> colBufSizeDefault
_ -> colBufSizeDefault 1
bs <- liftM2 (B.append) (B.packCStringLen (buf, firstbuf))
(B.packCStringLen (buf2, fromIntegral len2))
hdbcTrace $ "col is: " ++ (BUTF8.toString bs)
return (SqlByteString bs)
_ -> raiseError "sqlGetData" res (StmtHandle cstmt)
ffetchrowBaseline sstate = do
hdbcTrace "ffetchrowBaseline"
result <- withStmtOrDie (sstmt sstate) $ \hStmt -> do
hdbcTrace "ffetchrowBaseline got stmt handle"
rc <- sqlFetch hStmt
if rc == 100
then return Nothing
else return (Just [])
case result of
Just x -> return $ Just x
Nothing -> do
ffinish sstate
return Nothing
data ColBuf
data BindCol
= BindColString (Ptr CChar) Int64 Word16
| BindColWString (Ptr CWchar) Int64 Word16
| BindColBit (Ptr CUChar)
| BindColTinyInt (Ptr CChar)
| BindColShort (Ptr CShort)
| BindColLong (Ptr CLong)
| BindColBigInt (Ptr Int64)
| BindColFloat (Ptr CFloat)
| BindColDouble (Ptr CDouble)
| BindColBinary (Ptr CUChar) Int64 Word16
| BindColDate (Ptr StructDate)
| BindColTime (Ptr StructTime)
| BindColTimestamp (Ptr StructTimestamp)
| BindColGetData Word16
data StructDate = StructDate
Int16
Word16
Word16
deriving Show
instance Storable StructDate where
sizeOf _ = (6)
alignment _ = alignment (undefined :: CLong)
poke p (StructDate year month day) = do
(\hsc_ptr -> pokeByteOff hsc_ptr 0) p year
(\hsc_ptr -> pokeByteOff hsc_ptr 2) p month
(\hsc_ptr -> pokeByteOff hsc_ptr 4) p day
peek p = return StructDate
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 0) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 2) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 4) p)
data StructTime = StructTime
Word16
Word16
Word16
instance Storable StructTime where
sizeOf _ = (6)
alignment _ = alignment (undefined :: CLong)
poke p (StructTime hour minute second) = do
(\hsc_ptr -> pokeByteOff hsc_ptr 0) p hour
(\hsc_ptr -> pokeByteOff hsc_ptr 2) p minute
(\hsc_ptr -> pokeByteOff hsc_ptr 4) p second
peek p = return StructTime
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 0) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 2) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 4) p)
data StructTimestamp = StructTimestamp
Int16
Word16
Word16
Word16
Word16
Word16
Word32
instance Storable StructTimestamp where
sizeOf _ = (16)
alignment _ = alignment (undefined :: CLong)
poke p (StructTimestamp year month day hour minute second fraction) = do
(\hsc_ptr -> pokeByteOff hsc_ptr 0) p year
(\hsc_ptr -> pokeByteOff hsc_ptr 2) p month
(\hsc_ptr -> pokeByteOff hsc_ptr 4) p day
(\hsc_ptr -> pokeByteOff hsc_ptr 6) p hour
(\hsc_ptr -> pokeByteOff hsc_ptr 8) p minute
(\hsc_ptr -> pokeByteOff hsc_ptr 10) p second
(\hsc_ptr -> pokeByteOff hsc_ptr 12) p fraction
peek p = return StructTimestamp
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 0) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 2) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 4) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 6) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 8) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 10) p)
`ap` ((\hsc_ptr -> peekByteOff hsc_ptr 12) p)
mkBindCol :: SState -> SQLHSTMT -> Int16 -> IO (BindCol, Ptr Int64)
mkBindCol sstate cstmt col = do
hdbcTrace "mkBindCol"
colInfo <- readMVar (colinfomv sstate)
let colDesc = (snd (colInfo !! ((fromIntegral col) 1)))
case colType colDesc of
SqlCharT -> mkBindColStringEC cstmt col' (colSize colDesc)
SqlVarCharT -> mkBindColStringEC cstmt col' (colSize colDesc)
SqlLongVarCharT -> mkBindColString cstmt col' (colSize colDesc)
SqlWCharT -> mkBindColWStringEC cstmt col' (colSize colDesc)
SqlWVarCharT -> mkBindColWStringEC cstmt col' (colSize colDesc)
SqlWLongVarCharT -> mkBindColWString cstmt col' (colSize colDesc)
SqlDecimalT -> mkBindColString cstmt col' (colSize colDesc)
SqlNumericT -> mkBindColString cstmt col' (colSize colDesc)
SqlBitT -> mkBindColBit cstmt col' (colSize colDesc)
SqlTinyIntT -> mkBindColTinyInt cstmt col' (colSize colDesc)
SqlSmallIntT -> mkBindColShort cstmt col' (colSize colDesc)
SqlIntegerT -> mkBindColLong cstmt col' (colSize colDesc)
SqlBigIntT -> mkBindColBigInt cstmt col' (colSize colDesc)
SqlRealT -> mkBindColFloat cstmt col' (colSize colDesc)
SqlFloatT -> mkBindColDouble cstmt col' (colSize colDesc)
SqlDoubleT -> mkBindColDouble cstmt col' (colSize colDesc)
SqlBinaryT -> mkBindColBinary cstmt col' (colSize colDesc)
SqlVarBinaryT -> mkBindColBinary cstmt col' (colSize colDesc)
SqlLongVarBinaryT -> mkBindColBinary cstmt col' (colSize colDesc)
SqlDateT -> mkBindColDate cstmt col' (colSize colDesc)
SqlTimeT -> mkBindColTime cstmt col' (colSize colDesc)
SqlTimestampT -> mkBindColTimestamp cstmt col' (colSize colDesc)
_ -> mkBindColGetData col'
where
col' = fromIntegral col
colBufSizeDefault = 1024
colBufSizeMaximum = 4096
utf8EncodingMaximum = 6
wcSize = 2
mkBindColString cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColString"
let colSize = min colBufSizeMaximum $ fromMaybe colBufSizeDefault mColSize
let bufLen = sizeOf (undefined :: CChar) * (colSize + 1)
buf <- mallocBytes bufLen
pStrLen <- malloc
sqlBindCol cstmt col (1) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColString buf (fromIntegral bufLen) col, pStrLen)
mkBindColStringEC cstmt col = mkBindColString cstmt col . fmap (* utf8EncodingMaximum)
mkBindColWString cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColWString"
let colSize = min colBufSizeMaximum $ fromMaybe colBufSizeDefault mColSize
let bufLen = sizeOf (undefined :: CWchar) * (colSize + 1)
buf <- mallocBytes bufLen
pStrLen <- malloc
sqlBindCol cstmt col (1) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColWString buf (fromIntegral bufLen) col, pStrLen)
mkBindColWStringEC cstmt col = mkBindColString cstmt col . fmap extendFactor where
extendFactor sz = sz * ((utf8EncodingMaximum + wcSize 1) `quot` wcSize)
mkBindColBit cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColBit"
let bufLen = sizeOf (undefined :: CChar)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (7) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColBit buf, pStrLen)
mkBindColTinyInt cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColTinyInt"
let bufLen = sizeOf (undefined :: CUChar)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (26) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColTinyInt buf, pStrLen)
mkBindColShort cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColShort"
let bufLen = sizeOf (undefined :: CShort)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (15) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColShort buf, pStrLen)
mkBindColLong cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColSize"
let bufLen = sizeOf (undefined :: CLong)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (16) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColLong buf, pStrLen)
mkBindColBigInt cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColBigInt"
let bufLen = sizeOf (undefined :: CInt)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (25) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColBigInt buf, pStrLen)
mkBindColFloat cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColFloat"
let bufLen = sizeOf (undefined :: CFloat)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (7) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColFloat buf, pStrLen)
mkBindColDouble cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColDouble"
let bufLen = sizeOf (undefined :: CDouble)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (8) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColDouble buf, pStrLen)
mkBindColBinary cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColBinary"
let colSize = min colBufSizeMaximum $ fromMaybe colBufSizeDefault mColSize
let bufLen = sizeOf (undefined :: CUChar) * (colSize + 1)
buf <- mallocBytes bufLen
pStrLen <- malloc
sqlBindCol cstmt col (2) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColBinary buf (fromIntegral bufLen) col, pStrLen)
mkBindColDate cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColDate"
let bufLen = sizeOf (undefined :: StructDate)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (91) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColDate buf, pStrLen)
mkBindColTime cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColTime"
let bufLen = sizeOf (undefined :: StructTime)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (92) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColTime buf, pStrLen)
mkBindColTimestamp cstmt col mColSize = do
hdbcTrace "mkBindCol: BindColTimestamp"
let bufLen = sizeOf (undefined :: StructTimestamp)
buf <- malloc
pStrLen <- malloc
sqlBindCol cstmt col (93) (castPtr buf) (fromIntegral bufLen) pStrLen
return (BindColTimestamp buf, pStrLen)
mkBindColGetData col = do
hdbcTrace "mkBindCol: BindColGetData"
return (BindColGetData col, nullPtr)
freeBindCol :: BindCol -> IO ()
freeBindCol (BindColString buf _ _) = free buf
freeBindCol (BindColWString buf _ _) = free buf
freeBindCol (BindColBit buf) = free buf
freeBindCol (BindColTinyInt buf) = free buf
freeBindCol (BindColShort buf) = free buf
freeBindCol (BindColLong buf) = free buf
freeBindCol (BindColBigInt buf) = free buf
freeBindCol (BindColFloat buf) = free buf
freeBindCol (BindColDouble buf) = free buf
freeBindCol (BindColBinary buf _ _) = free buf
freeBindCol (BindColDate buf) = free buf
freeBindCol (BindColTime buf) = free buf
freeBindCol (BindColTimestamp buf) = free buf
freeBindCol (BindColGetData _ ) = return ()
bindColToSqlValue :: SQLHSTMT -> (BindCol, Ptr Int64) -> IO SqlValue
bindColToSqlValue pcstmt (BindColGetData col, _) = do
hdbcTrace "bindColToSqlValue: BindColGetData"
getColData pcstmt 1 col
bindColToSqlValue pcstmt (bindCol, pStrLen) = do
hdbcTrace "bindColToSqlValue"
strLen <- peek pStrLen
case strLen of
1 -> return SqlNull
4 -> getLongColData pcstmt bindCol
_ -> bindColToSqlValue' pcstmt bindCol strLen
bindColToSqlValue' :: SQLHSTMT -> BindCol -> Int64 -> IO SqlValue
bindColToSqlValue' pcstmt (BindColString buf bufLen col) strLen
| bufLen >= strLen = do
bs <- B.packCStringLen (buf, fromIntegral strLen)
hdbcTrace $ "bindColToSqlValue BindColString " ++ show bs ++ " " ++ show strLen
return $ SqlByteString bs
| otherwise = getColData pcstmt 1 col
bindColToSqlValue' pcstmt (BindColWString buf bufLen col) strLen
| bufLen >= strLen = do
bs <- B.packCStringLen (castPtr buf, fromIntegral strLen)
hdbcTrace $ "bindColToSqlValue BindColWString " ++ show bs ++ " " ++ show strLen
return $ SqlByteString bs
| otherwise = getColData pcstmt 1 col
bindColToSqlValue' _ (BindColBit buf) strLen = do
bit <- peek buf
hdbcTrace $ "bindColToSqlValue BindColBit " ++ show bit
return $ SqlChar (castCUCharToChar bit)
bindColToSqlValue' _ (BindColTinyInt buf) strLen = do
tinyInt <- peek buf
hdbcTrace $ "bindColToSqlValue BindColTinyInt " ++ show tinyInt
return $ SqlChar (castCCharToChar tinyInt)
bindColToSqlValue' _ (BindColShort buf) strLen = do
short <- peek buf
hdbcTrace $ "bindColToSqlValue BindColShort" ++ show short
return $ SqlInt32 (fromIntegral short)
bindColToSqlValue' _ (BindColLong buf) strLen = do
long <- peek buf
hdbcTrace $ "bindColToSqlValue BindColLong " ++ show long
return $ SqlInt32 (fromIntegral long)
bindColToSqlValue' _ (BindColBigInt buf) strLen = do
bigInt <- peek buf
hdbcTrace $ "bindColToSqlValue BindColBigInt " ++ show bigInt
return $ SqlInt64 (fromIntegral bigInt)
bindColToSqlValue' _ (BindColFloat buf) strLen = do
float <- peek buf
hdbcTrace $ "bindColToSqlValue BindColFloat " ++ show float
return $ SqlDouble (realToFrac float)
bindColToSqlValue' _ (BindColDouble buf) strLen = do
double <- peek buf
hdbcTrace $ "bindColToSqlValue BindColDouble " ++ show double
return $ SqlDouble (realToFrac double)
bindColToSqlValue' pcstmt (BindColBinary buf bufLen col) strLen
| bufLen >= strLen = do
bs <- B.packCStringLen (castPtr buf, fromIntegral strLen)
hdbcTrace $ "bindColToSqlValue BindColBinary " ++ show bs
return $ SqlByteString bs
| otherwise = getColData pcstmt (2) col
bindColToSqlValue' _ (BindColDate buf) strLen = do
StructDate year month day <- peek buf
hdbcTrace $ "bindColToSqlValue BindColDate"
return $ SqlLocalDate $ fromGregorian
(fromIntegral year) (fromIntegral month) (fromIntegral day)
bindColToSqlValue' _ (BindColTime buf) strLen = do
StructTime hour minute second <- peek buf
hdbcTrace $ "bindColToSqlValue BindColTime"
return $ SqlLocalTimeOfDay $ TimeOfDay
(fromIntegral hour) (fromIntegral minute) (fromIntegral second)
bindColToSqlValue' _ (BindColTimestamp buf) strLen = do
StructTimestamp year month day hour minute second nanosecond <- peek buf
hdbcTrace $ "bindColToSqlValue BindColTimestamp"
return $ SqlLocalTime $ LocalTime
(fromGregorian (fromIntegral year) (fromIntegral month) (fromIntegral day))
(TimeOfDay (fromIntegral hour) (fromIntegral minute)
(fromIntegral second + (fromIntegral nanosecond / 1000000000)))
bindColToSqlValue' _ (BindColGetData _) _ =
error "bindColToSqlValue': unexpected BindColGetData!"
fgetcolinfo :: SQLHSTMT -> IO [(String, SqlColDesc)]
fgetcolinfo cstmt =
do ncols <- getNumResultCols cstmt
mapM getname [1..ncols]
where getname icol = alloca $ \colnamelp ->
allocaBytes 128 $ \cscolname ->
alloca $ \datatypeptr ->
alloca $ \colsizeptr ->
alloca $ \nullableptr ->
do sqlDescribeCol cstmt icol cscolname 127 colnamelp
datatypeptr colsizeptr nullPtr nullableptr
colnamelen <- peek colnamelp
colnamebs <- B.packCStringLen (cscolname, fromIntegral colnamelen)
let colname = BUTF8.toString colnamebs
datatype <- peek datatypeptr
colsize <- peek colsizeptr
nullable <- peek nullableptr
return $ fromOTypeInfo colname datatype colsize nullable
fexecutemany :: SState -> [[SqlValue]] -> IO ()
fexecutemany sstate arglist =
mapM_ (fexecute sstate) arglist >> return ()
freeBoundCols :: SState -> IO ()
freeBoundCols sstate = modifyMVar_ (bindColsMV sstate) $ \maybeBindCols -> do
F.mapM_ go maybeBindCols
return Nothing
where
go bindCols = do
hdbcTrace "freeBoundCols"
mapM_ (\(bindCol, pSqlLen) -> freeBindCol bindCol >> free pSqlLen) bindCols
ffinish :: SState -> IO ()
ffinish sstate = do
hdbcTrace "ffinish"
withMaybeStmt (sstmt sstate) $ F.mapM_ $ \hStmt -> do
c_sqlFreeStmt hStmt sQL_CLOSE >>= checkError "fexecute c_sqlFreeStmt sQL_CLOSE" (StmtHandle hStmt)
c_sqlFreeStmt hStmt sQL_UNBIND >>= checkError "fexecute c_sqlFreeStmt sQL_UNBIND" (StmtHandle hStmt)
c_sqlFreeStmt hStmt sQL_RESET_PARAMS >>= checkError "fexecute c_sqlFreeStmt sQL_RESET_PARAMS" (StmtHandle hStmt)
freeBoundCols sstate
ffinalize :: SState -> IO ()
ffinalize sstate = do
ffinish sstate
freeStmtIfNotAlready $ sstmt sstate
foreign import ccall safe "sql.h SQLDescribeCol"
sqlDescribeCol :: SQLHSTMT
-> Int16
-> CString
-> Int16
-> Ptr (Int16)
-> Ptr (Int16)
-> Ptr (Word64)
-> Ptr (Int16)
-> Ptr (Int16)
-> IO Int16
foreign import ccall safe "sql.h SQLGetData"
sqlGetData :: SQLHSTMT
-> Word16
-> Int16
-> CString
-> Int64
-> Ptr (Int64)
-> IO Int16
foreign import ccall safe "sql.h SQLBindCol"
sqlBindCol :: SQLHSTMT
-> Word16
-> Int16
-> Ptr ColBuf
-> Int64
-> Ptr (Int64)
-> IO Int16
foreign import ccall safe "sql.h SQLPrepare"
sqlPrepare :: SQLHSTMT -> CString -> Int32
-> IO Int16
foreign import ccall safe "sql.h SQLExecute"
sqlExecute :: SQLHSTMT -> IO Int16
foreign import ccall safe "sql.h SQLNumResultCols"
sqlNumResultCols :: SQLHSTMT -> Ptr Int16
-> IO Int16
foreign import ccall safe "sql.h SQLRowCount"
sqlRowCount :: SQLHSTMT -> Ptr Int32 -> IO Int16
foreign import ccall safe "sql.h SQLBindParameter"
sqlBindParameter :: SQLHSTMT
-> Word16
-> Int16
-> Int16
-> Int16
-> Word64
-> Int16
-> CString
-> Int64
-> Ptr Int64
-> IO Int16
foreign import ccall safe "hdbc-odbc-helper.h &nullDataHDBC"
nullDataHDBC :: Ptr Int64
foreign import ccall safe "sql.h SQLDescribeParam"
sqlDescribeParam :: SQLHSTMT
-> Word16
-> Ptr Int16
-> Ptr Word64
-> Ptr Int16
-> Ptr Int16
-> IO Int16
foreign import ccall safe "sql.h SQLFetch"
sqlFetch :: SQLHSTMT -> IO Int16
foreign import ccall safe "hdbc-odbc-helper.h simpleSqlTables"
simpleSqlTables :: SQLHSTMT -> IO Int16
foreign import ccall safe "hdbc-odbc-helper.h simpleSqlColumns"
simpleSqlColumns :: SQLHSTMT -> Ptr CChar ->
Int16 -> IO Int16
fgetparminfo :: SQLHSTMT -> IO [SqlColDesc]
fgetparminfo cstmt =
do ncols <- getNumParams cstmt
mapM getname [1..ncols]
where getname icol =
alloca $ \datatypeptr ->
alloca $ \colsizeptr ->
alloca $ \nullableptr ->
do poke datatypeptr 127
res <- sqlDescribeParam cstmt (fromInteger $ toInteger icol)
datatypeptr colsizeptr nullPtr nullableptr
putStrLn $ show res
datatype <- peek datatypeptr
colsize <- peek colsizeptr
nullable <- peek nullableptr
return $ snd $ fromOTypeInfo "" datatype colsize nullable
getNumParams :: SQLHSTMT -> IO Int16
getNumParams sthptr = alloca $ \pcount ->
do sqlNumParams sthptr pcount >>= checkError "SQLNumResultCols"
(StmtHandle sthptr)
peek pcount
foreign import ccall safe "sql.h SQLNumParams"
sqlNumParams :: SQLHSTMT -> Ptr Int16
-> IO Int16