{-# LINE 1 "Database/HDBC/ODBC/Statement.hsc" #-}
-- -*- mode: haskell; -*-
{-# LINE 2 "Database/HDBC/ODBC/Statement.hsc" #-}
{-# CFILES hdbc-odbc-helper.c #-}
-- Above line for hugs
{-# LANGUAGE EmptyDataDecls #-}

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.Types
import Database.HDBC.ODBC.Utils
import Database.HDBC.ODBC.TypeConv

import Foreign.C.String (castCUCharToChar)
import Foreign.C.Types
import Foreign.ForeignPtr
import Foreign.Ptr
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

l :: String -> IO ()
l _ = return ()
-- l m = hPutStrLn stderr ("\n" ++ m)


{-# LINE 49 "Database/HDBC/ODBC/Statement.hsc" #-}

{-# LINE 50 "Database/HDBC/ODBC/Statement.hsc" #-}

{-# LINE 51 "Database/HDBC/ODBC/Statement.hsc" #-}


{-# LINE 55 "Database/HDBC/ODBC/Statement.hsc" #-}

{-# LINE 56 "Database/HDBC/ODBC/Statement.hsc" #-}

{-# LINE 57 "Database/HDBC/ODBC/Statement.hsc" #-}

fGetQueryInfo :: Conn -> ChildList -> String
              -> IO ([SqlColDesc], [(String, SqlColDesc)])
fGetQueryInfo iconn children query =
    do l "in fGetQueryInfo"
       sstate <- newSState iconn query
       addChild children (wrapStmt sstate)   -- We get error if we forget this one. Not sure why.
       fakeExecute' sstate

fakeExecute' :: SState -> IO ([SqlColDesc], [(String, SqlColDesc)])
fakeExecute' sstate  = withConn (dbo sstate) $ \cconn ->
                       withCStringLen (squery sstate) $ \(cquery, cqlen) ->
                       alloca $ \(psthptr::Ptr (Ptr CStmt)) ->
    do l "in fexecute"
       -- public_ffinish sstate  
       rc1 <- sqlAllocStmtHandle 3 cconn psthptr
{-# LINE 73 "Database/HDBC/ODBC/Statement.hsc" #-}
       sthptr <- peek psthptr
       wrappedsthptr <- withRawConn (dbo sstate)
                        (\rawconn -> wrapstmt sthptr rawconn)
       fsthptr <- newForeignPtr sqlFreeHandleSth_ptr wrappedsthptr
       checkError "execute allocHandle" (DbcHandle cconn) rc1

       sqlPrepare sthptr cquery (fromIntegral cqlen) >>= 
            checkError "execute prepare" (StmtHandle sthptr)

       -- parmCount <- getNumParams sthptr
       parmInfo <- fgetparminfo sthptr
       
       -- rc <- getNumResultCols sthptr
       colInfo <- fgetcolinfo sthptr
       return (parmInfo, colInfo)

-- | The Stament State
data SState = SState
  { stomv      :: MVar (Maybe Stmt)
  , dbo        :: Conn
  , squery     :: String
  , colinfomv  :: MVar [(String, SqlColDesc)]
  , bindColsMV :: MVar (Maybe [(BindCol, Ptr Int64)])
{-# LINE 96 "Database/HDBC/ODBC/Statement.hsc" #-}
  }

-- FIXME: we currently do no prepare optimization whatsoever.

newSState :: Conn -> String -> IO SState
newSState indbo query =
    do newstomv     <- newMVar Nothing
       newcolinfomv <- newMVar []
       newBindCols  <- newMVar Nothing
       return SState
         { stomv      = newstomv
         , dbo        = indbo
         , squery     = query
         , colinfomv  = newcolinfomv
         , bindColsMV = newBindCols
         }

wrapStmt :: SState -> Statement
wrapStmt sstate = Statement
  { execute        = fexecute sstate
  , executeRaw     = return ()
  , executeMany    = fexecutemany sstate
  , finish         = public_ffinish sstate
  , fetchRow       = ffetchrow sstate
  , originalQuery  = (squery sstate)
  , getColumnNames = readMVar (colinfomv sstate) >>= (return . map fst)
  , describeResult = readMVar (colinfomv sstate)
  }

newSth :: Conn -> ChildList -> String -> IO Statement
newSth indbo mchildren query =
    do l "in newSth"
       sstate <- newSState indbo query
       let retval = wrapStmt sstate
       addChild mchildren retval
       return retval

makesth :: Conn -> [Char] -> IO (ForeignPtr WrappedCStmt)
makesth iconn name = alloca $ \(psthptr::Ptr (Ptr CStmt)) ->
                     withConn iconn $ \cconn -> 
                     withCString "" $ \emptycs ->
    do rc1 <- sqlAllocStmtHandle 3 cconn psthptr
{-# LINE 138 "Database/HDBC/ODBC/Statement.hsc" #-}
       sthptr <- peek psthptr
       wrappedsthptr <- withRawConn iconn
                        (\rawconn -> wrapstmt sthptr rawconn)
       fsthptr <- newForeignPtr sqlFreeHandleSth_ptr wrappedsthptr
       checkError (name ++ " allocHandle") (DbcHandle cconn) rc1
       return fsthptr

wrapTheStmt :: Conn -> Stmt -> IO (Statement, SState)
wrapTheStmt iconn fsthptr =
    do sstate <- newSState iconn ""
       sstate <- newSState iconn ""
       swapMVar (stomv sstate) (Just fsthptr)
       let sth = wrapStmt sstate
       return (sth, sstate)

fgettables :: Conn -> IO [String]
fgettables iconn =
    do fsthptr <- makesth iconn "fgettables"
       l "fgettables: after makesth"
       withStmt fsthptr (\sthptr ->
                             simpleSqlTables sthptr >>=
                                checkError "gettables simpleSqlTables" 
                                               (StmtHandle sthptr)
                        )
       l "fgettables: after withStmt"
       (sth, sstate) <- wrapTheStmt iconn fsthptr
       withStmt fsthptr (\sthptr -> fgetcolinfo sthptr >>= swapMVar (colinfomv sstate))
       l "fgettables: after wrapTheStmt"
       results <- fetchAllRows' sth
       l ("fgettables: results: " ++ (show results))
       return $ map (\x -> fromSql (x !! 2)) results

fdescribetable :: Conn -> String -> IO [(String, SqlColDesc)]
fdescribetable iconn tablename = B.useAsCStringLen (BUTF8.fromString tablename) $ 
                                 \(cs, csl) ->
    do fsthptr <- makesth iconn "fdescribetable"
       withStmt fsthptr (\sthptr ->
                             simpleSqlColumns sthptr cs (fromIntegral csl) >>=
                               checkError "fdescribetable simpleSqlColumns"
                                          (StmtHandle sthptr)
                        )
       (sth, sstate) <- wrapTheStmt iconn fsthptr
       withStmt fsthptr (\sthptr -> fgetcolinfo sthptr >>= swapMVar (colinfomv sstate))
       results <- fetchAllRows' sth
       l (show results)
       return $ map fromOTypeCol results

{- For now, we try to just  handle things as simply as possible.
FIXME lots of room for improvement here (types, etc). -}
fexecute :: SState -> [SqlValue] -> IO Integer
fexecute sstate args =
  withConn (dbo sstate) $ \cconn ->
  B.useAsCStringLen (BUTF8.fromString (squery sstate)) $ \(cquery, cqlen) ->
  alloca $ \(psthptr::Ptr (Ptr CStmt)) ->
    do l $ "in fexecute: " ++ show (squery sstate) ++ show args
       public_ffinish sstate
       rc1 <- sqlAllocStmtHandle 3 cconn psthptr
{-# LINE 195 "Database/HDBC/ODBC/Statement.hsc" #-}
       sthptr <- peek psthptr
       wrappedsthptr <- withRawConn (dbo sstate)
                        (\rawconn -> wrapstmt sthptr rawconn)
       fsthptr <- newForeignPtr sqlFreeHandleSth_ptr wrappedsthptr
       checkError "execute allocHandle" (DbcHandle cconn) rc1

       sqlPrepare sthptr cquery (fromIntegral cqlen) >>= 
            checkError "execute prepare" (StmtHandle sthptr)

       bindArgs <- zipWithM (bindParam sthptr) args [1..]
       l $ "Ready for sqlExecute: " ++ show (squery sstate) ++ show args
       r <- sqlExecute sthptr
       mapM_ (\(x, y) -> free x >> free y) (catMaybes bindArgs)

       case r of
         100 -> return () -- Update that did nothing
{-# LINE 211 "Database/HDBC/ODBC/Statement.hsc" #-}
         x -> checkError "execute execute" (StmtHandle sthptr) x

       rc <- getNumResultCols sthptr

       case rc of
         0 -> do rowcount <- getSqlRowCount sthptr
                 ffinish fsthptr
                 swapMVar (colinfomv sstate) []
                 touchForeignPtr fsthptr
                 return (fromIntegral rowcount)
         colcount -> do fgetcolinfo sthptr >>= swapMVar (colinfomv sstate)
                        swapMVar (stomv sstate) (Just fsthptr)
                        touchForeignPtr fsthptr
                        return 0

getNumResultCols :: Ptr CStmt -> IO Int16
{-# LINE 227 "Database/HDBC/ODBC/Statement.hsc" #-}
getNumResultCols sthptr = alloca $ \pcount ->
    do sqlNumResultCols sthptr pcount >>= checkError "SQLNumResultCols" 
                                          (StmtHandle sthptr)
       peek pcount

-- Bind a parameter column before execution.
bindParam :: Ptr CStmt -> SqlValue -> Word16
        -> IO (Maybe (Ptr Int64, Ptr CChar))
{-# LINE 235 "Database/HDBC/ODBC/Statement.hsc" #-}
bindParam sthptr arg icol =  alloca $ \pdtype ->
                           alloca $ \pcolsize ->
                           alloca $ \pdecdigits ->
                           alloca $ \pnullable ->
{- We have to start by getting the SQL type of the column so we can
   send the correct type back to the server.  Sigh.  If the ODBC
   backend won't tell us the type, we fake it.

   We've got an annoying situation with error handling.  Must make
   sure that all data is freed, but if there's an error, we have to raise
   it and the caller never gets to freed the allocated data to-date.
   So, make sure we either free of have foreignized everything before
   control passes out of this function. -}

    do l $ "Binding col " ++ show icol ++ ": " ++ show arg
       rc1 <- sqlDescribeParam sthptr icol pdtype pcolsize pdecdigits pnullable
       l $ "rc1 is " ++ show (isOK rc1)
       when (not (isOK rc1)) $ -- Some drivers don't support that call
          do poke pdtype 1
{-# LINE 254 "Database/HDBC/ODBC/Statement.hsc" #-}
             poke pcolsize 0
             poke pdecdigits 0
       coltype <- peek pdtype
       colsize <- peek pcolsize
       decdigits <- peek pdecdigits
       l $ "Results: " ++ show (coltype, colsize, decdigits)
       case arg of
         SqlNull -> -- NULL parameter, bind it as such.
                    do l "Binding null"
                       rc2 <- sqlBindParameter sthptr (fromIntegral icol)
                              1
{-# LINE 265 "Database/HDBC/ODBC/Statement.hsc" #-}
                              1 coltype colsize decdigits
{-# LINE 266 "Database/HDBC/ODBC/Statement.hsc" #-}
                              nullPtr 0 nullDataHDBC
                       checkError ("bindparameter NULL " ++ show icol)
                                      (StmtHandle sthptr) rc2
                       return Nothing
         x -> do -- Otherwise, we have to allocate RAM, make sure it's
                 -- not freed now, and pass it along...
                  (csptr, cslen) <- cstrUtf8BString (fromSql x)
                  do pcslen <- malloc 
                     poke pcslen (fromIntegral cslen)
                     rc2 <- sqlBindParameter sthptr (fromIntegral icol)
                       1
{-# LINE 277 "Database/HDBC/ODBC/Statement.hsc" #-}
                       1 coltype 
{-# LINE 278 "Database/HDBC/ODBC/Statement.hsc" #-}
                       (if isOK rc1 then colsize else fromIntegral cslen + 1) decdigits
                       csptr (fromIntegral cslen + 1) pcslen
                     if isOK rc2
                        then do -- We bound it.  Make foreignPtrs and return.
                                return $ Just (pcslen, csptr)
                        else do -- Binding failed.  Free the data and raise
                                -- error.
                                free pcslen
                                free csptr
                                checkError ("bindparameter " ++ show icol) 
                                               (StmtHandle sthptr) rc2
                                return Nothing -- will never get hit

getSqlRowCount :: Ptr CStmt -> IO Int32
getSqlRowCount cstmt = alloca $ \prows ->
     do sqlRowCount cstmt prows >>= checkError "SQLRowCount" (StmtHandle cstmt)
        peek prows
        --note: As of ODBC-3.52, the row count is only a C int, ie 32bit.


cstrUtf8BString :: B.ByteString -> IO CStringLen
cstrUtf8BString bs = do
    B.unsafeUseAsCStringLen bs $ \(s,len) -> do
        res <- mallocBytes (len+1)
        -- copy in
        copyBytes res s len
        -- null terminate
        poke (plusPtr res len) (0::CChar)
        -- return ptr
        return (res, len)

ffetchrow :: SState -> IO (Maybe [SqlValue])
ffetchrow sstate = modifyMVar (stomv sstate) $ \stmt -> do
  l $ "ffetchrow"
  case stmt of
    Nothing -> do
      l "ffetchrow: no statement"
      return (stmt, Nothing)
    Just cmstmt -> withStmt cmstmt $ \cstmt -> do
      bindCols <- getBindCols sstate cstmt
      l "ffetchrow: fetching"
      rc <- sqlFetch cstmt
      if rc == 100
{-# LINE 321 "Database/HDBC/ODBC/Statement.hsc" #-}
        then do
          l "ffetchrow: no more rows"
          ffinish cmstmt
          return (Nothing, Nothing)
        else do
          l "ffetchrow: fetching data"
          checkError "sqlFetch" (StmtHandle cstmt) rc
          sqlValues <- if rc == 0 || rc == 1
{-# LINE 329 "Database/HDBC/ODBC/Statement.hsc" #-}
            then mapM (bindColToSqlValue cstmt) bindCols
            else raiseError "sqlGetData" rc (StmtHandle cstmt)
          return (stmt, Just sqlValues)

getBindCols :: SState -> Ptr CStmt -> IO [(BindCol, Ptr Int64)]
{-# LINE 334 "Database/HDBC/ODBC/Statement.hsc" #-}
getBindCols sstate cstmt = do
  l "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)

-- This is only for String data. For binary fix should be very easy. Just check the column type and use buflen instead of buflen - 1
getLongColData cstmt bindCol = do
   let (BindColString buf bufLen col) = bindCol
   l $ "buflen: " ++ show bufLen
   bs <- B.packCStringLen (buf, fromIntegral (bufLen - 1))
   l $ "sql_no_total col " ++ show (BUTF8.toString bs)
   bs2 <- getRestLongColData cstmt 1 col bs
{-# LINE 352 "Database/HDBC/ODBC/Statement.hsc" #-}
   return $ SqlByteString bs2


getRestLongColData cstmt cBinding icol acc = do
  l "getLongColData"
  alloca $ \plen ->
   allocaBytes colBufSizeMaximum $ \buf ->
     do res <- sqlGetData cstmt (fromIntegral icol) cBinding
                          buf (fromIntegral colBufSizeMaximum) plen
        if res == 0 || res == 1
{-# LINE 362 "Database/HDBC/ODBC/Statement.hsc" #-}
           then do
                len <- peek plen
                if len == 100
{-# LINE 365 "Database/HDBC/ODBC/Statement.hsc" #-}
                   then return acc
                   else do
                        let bufmax = fromIntegral $ colBufSizeMaximum - 1
                        bs <- B.packCStringLen (buf, fromIntegral (if len == -4 || len > bufmax then bufmax else len))
{-# LINE 369 "Database/HDBC/ODBC/Statement.hsc" #-}
                        l $ "sql_no_total col is: " ++ show (BUTF8.toString bs)
                        let newacc = B.append acc bs
                        if len /= -4 && len <= bufmax
{-# LINE 372 "Database/HDBC/ODBC/Statement.hsc" #-}
                           then return newacc
                           else getRestLongColData cstmt cBinding icol newacc
           else  raiseError "sqlGetData" res (StmtHandle cstmt)

-- TODO: This code does not deal well with data that is extremely large,
-- where multiple fetches are required.
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 ->
{-# LINE 385 "Database/HDBC/ODBC/Statement.hsc" #-}
              do len <- peek plen
                 case len of
                   -1 -> return SqlNull
{-# LINE 388 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -4 -> fail $ "Unexpected SQL_NO_TOTAL"
{-# LINE 389 "Database/HDBC/ODBC/Statement.hsc" #-}
                   _ -> do bs <- B.packCStringLen (buf, fromIntegral len)
                           l $ "col is: " ++ show (BUTF8.toString bs)
                           return (SqlByteString bs)
          1 ->
{-# LINE 393 "Database/HDBC/ODBC/Statement.hsc" #-}
              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
{-# LINE 401 "Database/HDBC/ODBC/Statement.hsc" #-}
                                       _ -> colBufSizeDefault - 1 -- strip off NUL
                      bs <- liftM2 (B.append) (B.packCStringLen (buf, firstbuf))
                            (B.packCStringLen (buf2, fromIntegral len2))
                      l $ "col is: " ++ (BUTF8.toString bs)
                      return (SqlByteString bs)
          _ -> raiseError "sqlGetData" res (StmtHandle cstmt)

-- | ffetchrowBaseline is used for benchmarking fetches without the
-- overhead of marshalling values.
ffetchrowBaseline sstate = do
  Just cmstmt <- readMVar (stomv sstate)
  withStmt cmstmt $ \cstmt -> do
    rc <- sqlFetch cstmt
    if rc == 100
{-# LINE 415 "Database/HDBC/ODBC/Statement.hsc" #-}
      then do ffinish cmstmt
              return Nothing
      else do return (Just [])

data ColBuf

-- These correspond to the C type identifiers found here:
--     http://msdn.microsoft.com/en-us/library/ms714556(v=VS.85).aspx
-- The Ptr values point to the appropriate C types
data BindCol
  = BindColString  (Ptr CChar) Int64 Word16
{-# LINE 426 "Database/HDBC/ODBC/Statement.hsc" #-}
  | BindColWString (Ptr CWchar) Int64 Word16
{-# LINE 427 "Database/HDBC/ODBC/Statement.hsc" #-}
  | BindColBit     (Ptr CUChar)
  | BindColTinyInt (Ptr CChar)
  | BindColShort   (Ptr CShort)
  | BindColLong    (Ptr CLong)
  | BindColBigInt  (Ptr Int64)
{-# LINE 432 "Database/HDBC/ODBC/Statement.hsc" #-}
  | BindColFloat   (Ptr CFloat)
  | BindColDouble  (Ptr CDouble)
  | BindColBinary  (Ptr CUChar) Int64 Word16
{-# LINE 435 "Database/HDBC/ODBC/Statement.hsc" #-}
  | BindColDate    (Ptr StructDate)
  | BindColTime    (Ptr StructTime)
  | BindColTimestamp (Ptr StructTimestamp)
  | BindColGetData Word16
{-# LINE 439 "Database/HDBC/ODBC/Statement.hsc" #-}


-- Intervals and GUIDs have not been implemented, since there is no
-- equivalent SqlValue for these.
--
--  | BindColInterval
--      typedef struct tagSQL_INTERVAL_STRUCT
--      {
--         SQLINTERVAL interval_type; 
--         SQLSMALLINT interval_sign;
--         union {
--               SQL_YEAR_MONTH_STRUCT   year_month;
--               SQL_DAY_SECOND_STRUCT   day_second;
--               } intval;
--      } SQL_INTERVAL_STRUCT;
--      typedef enum 
--      {
--         SQL_IS_YEAR = 1,
--         SQL_IS_MONTH = 2,
--         SQL_IS_DAY = 3,
--         SQL_IS_HOUR = 4,
--         SQL_IS_MINUTE = 5,
--         SQL_IS_SECOND = 6,
--         SQL_IS_YEAR_TO_MONTH = 7,
--         SQL_IS_DAY_TO_HOUR = 8,
--         SQL_IS_DAY_TO_MINUTE = 9,
--         SQL_IS_DAY_TO_SECOND = 10,
--         SQL_IS_HOUR_TO_MINUTE = 11,
--         SQL_IS_HOUR_TO_SECOND = 12,
--         SQL_IS_MINUTE_TO_SECOND = 13
--      } SQLINTERVAL;
--      
--      typedef struct tagSQL_YEAR_MONTH
--      {
--         SQLUINTEGER year;
--         SQLUINTEGER month; 
--      } SQL_YEAR_MONTH_STRUCT;
--      
--      typedef struct tagSQL_DAY_SECOND
--      {
--         SQLUINTEGER day;
--         SQLUINTEGER hour;
--         SQLUINTEGER minute;
--         SQLUINTEGER second;
--         SQLUINTEGER fraction;
--      } SQL_DAY_SECOND_STRUCT;
-- | BindColGUID (Ptr StructGUID)


-- | StructDate is used to marshal the DATE_STRUCT
-- This struct, and the ones which follow, are described here:
--     http://msdn.microsoft.com/en-us/library/ms714556(v=VS.85).aspx
data StructDate = StructDate
  Int16
{-# LINE 493 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word16
{-# LINE 494 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word16
{-# LINE 495 "Database/HDBC/ODBC/Statement.hsc" #-}
 deriving Show

instance Storable StructDate where
  sizeOf _    = (6)
{-# LINE 499 "Database/HDBC/ODBC/Statement.hsc" #-}
  alignment _ = alignment (undefined :: CLong)
  poke p (StructDate year month day) = do
    (\hsc_ptr -> pokeByteOff hsc_ptr 0)  p year
{-# LINE 502 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 2) p month
{-# LINE 503 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 4)   p day
{-# LINE 504 "Database/HDBC/ODBC/Statement.hsc" #-}
  peek p = return StructDate
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 0)  p)
{-# LINE 506 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 2) p)
{-# LINE 507 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 4)   p)
{-# LINE 508 "Database/HDBC/ODBC/Statement.hsc" #-}


-- | StructTime is used to marshals the TIME_STRUCT:
data StructTime = StructTime
  Word16
{-# LINE 513 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word16
{-# LINE 514 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word16
{-# LINE 515 "Database/HDBC/ODBC/Statement.hsc" #-}

instance Storable StructTime where
  sizeOf _    = (6)
{-# LINE 518 "Database/HDBC/ODBC/Statement.hsc" #-}
  alignment _ = alignment (undefined :: CLong)
  poke p (StructTime hour minute second) = do
    (\hsc_ptr -> pokeByteOff hsc_ptr 0)   p hour
{-# LINE 521 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 2) p minute
{-# LINE 522 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 4) p second
{-# LINE 523 "Database/HDBC/ODBC/Statement.hsc" #-}
  peek p = return StructTime
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 0)  p)
{-# LINE 525 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 2) p)
{-# LINE 526 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 4)   p)
{-# LINE 527 "Database/HDBC/ODBC/Statement.hsc" #-}

-- | StructTimestamp is used to marshal the TIMESTAMP_STRUCT;
data StructTimestamp = StructTimestamp
  Int16
{-# LINE 531 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word16
{-# LINE 532 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word16
{-# LINE 533 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word16
{-# LINE 534 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word16
{-# LINE 535 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word16
{-# LINE 536 "Database/HDBC/ODBC/Statement.hsc" #-}
  Word32
{-# LINE 537 "Database/HDBC/ODBC/Statement.hsc" #-}

instance Storable StructTimestamp where
  sizeOf _    = (16)
{-# LINE 540 "Database/HDBC/ODBC/Statement.hsc" #-}
  alignment _ = alignment (undefined :: CLong)
  poke p (StructTimestamp year month day hour minute second fraction) = do
    (\hsc_ptr -> pokeByteOff hsc_ptr 0)      p year
{-# LINE 543 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 2)     p month
{-# LINE 544 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 4)       p day
{-# LINE 545 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 6)      p hour
{-# LINE 546 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 8)    p minute
{-# LINE 547 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 10)    p second
{-# LINE 548 "Database/HDBC/ODBC/Statement.hsc" #-}
    (\hsc_ptr -> pokeByteOff hsc_ptr 12)  p fraction
{-# LINE 549 "Database/HDBC/ODBC/Statement.hsc" #-}
  peek p = return StructTimestamp
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 0)     p)
{-# LINE 551 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 2)    p)
{-# LINE 552 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 4)      p)
{-# LINE 553 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 6)     p)
{-# LINE 554 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 8)   p)
{-# LINE 555 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 10)   p)
{-# LINE 556 "Database/HDBC/ODBC/Statement.hsc" #-}
    `ap` ((\hsc_ptr -> peekByteOff hsc_ptr 12) p)
{-# LINE 557 "Database/HDBC/ODBC/Statement.hsc" #-}

-- | StructGUID
-- data StructGUID = StructGUID
--   #{type DWORD}     -- ^ Data1
--   #{type WORD}      -- ^ Data2
--   #{type WORD}      -- ^ Data3
--   [#{type BYTE}]    -- ^ Data4[8]
-- 
-- instance Storable StructGUID where
--   sizeOf _ = #{size SQLGUID}
--   alignment _ = alignment (undefined :: CLong)
--   poke p (StructGUID data1 data2 data3 data4) = do
--     #{poke SQLGUID, Data1} p data1
--     #{poke SQLGUID, Data2} p data2
--     #{poke SQLGUID, Data3} p data3
--     pokeArray (p `plusPtr` #{offset SQLGUID, Data4}) data4
--   peek p = return StructGUID
--     `ap` (#{peek SQLGUID, Data1} p)
--     `ap` (#{peek SQLGUID, Data2} p)
--     `ap` (#{peek SQLGUID, Data3} p)
--     `ap` (peekArray 8 (p `plusPtr` #{offset SQLGUID, Data4}))


-- | This function binds the data in a column to a value of type
-- BindCol, using the default conversion scheme described here:
--     http://msdn.microsoft.com/en-us/library/ms716298(v=VS.85).aspx
-- The corresponding C types are here:
--     http://msdn.microsoft.com/en-us/library/ms714556(v=VS.85).aspx
-- These values are then ready for fetching.
-- Documentation about SQLBindCol can be found here:
--     http://msdn.microsoft.com/en-us/library/ms711010(v=vs.85).aspx
--
-- Our implementation follows this code:
--     http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Fcli%2Frzadpfndecol.htm
-- We have to make use of the column type and length information.
-- These are given by SQLDescribeCol, which is stored in colinfomv.
-- SQLDescribeCol can tell use the data type, and the size of a column (in
-- characters, so add 1 for the null terminator), or the number of decimal
-- digits that can be held.
-- To find out type, and how much memory to allocate, we could also use:
--    SQLColAttribute( ..., SQL_DESC_TYPE , ... )
--    SQLColAttribute( ..., SQL_DESC_OCTET_LENGTH , ... )
--
-- Further examples of how to use SQLBindCol are here, though these make use
-- of SQLDescribeCol:
--     http://msdn.microsoft.com/en-us/library/ms710118(v=vs.85).aspx
-- This implementation makes use of Column-Wise binding. Further improvements
-- might be had by using Row-Wise binding.
mkBindCol :: SState -> Ptr CStmt -> Int16 -> IO (BindCol, Ptr Int64)
{-# LINE 606 "Database/HDBC/ODBC/Statement.hsc" #-}
mkBindCol sstate cstmt col = do
  l "mkBindCol"
  colInfo <- readMVar (colinfomv sstate)
  let colDesc = (snd (colInfo !! ((fromIntegral col) -1)))
  case colType colDesc of
    SqlCharT          -> mkBindColString    cstmt col' (colSize colDesc)
    SqlVarCharT       -> mkBindColString    cstmt col' (colSize colDesc)
    SqlLongVarCharT   -> mkBindColString    cstmt col' (colSize colDesc)
    SqlWCharT         -> mkBindColWString   cstmt col' (colSize colDesc)
    SqlWVarCharT      -> mkBindColWString   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)
--    SqlIntervalT i    -> mkBindColInterval  cstmt col' (colSize colDesc) i
--    SqlGUIDT          -> mkBindColGUID      cstmt col' (colSize colDesc)
    _                 -> mkBindColGetData   col'
-- The following are not supported by ODBC:
--    SqlUTCDateTimeT
--    SqlUTCTimeT
--    SqlTimeWithZoneT
--    SqlTimestampWithZoneT
 where
  col' = fromIntegral col

colBufSizeDefault = 1024
colBufSizeMaximum = 4096

-- The functions that follow do the marshalling from C into a Haskell type
mkBindColString cstmt col mColSize = do
  l "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
{-# LINE 655 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColString buf (fromIntegral bufLen) col, pStrLen)
mkBindColWString cstmt col mColSize = do
  l "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
{-# LINE 663 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColWString buf (fromIntegral bufLen) col, pStrLen)
mkBindColBit cstmt col mColSize = do
  l "mkBindCol: BindColBit"
  let bufLen  = sizeOf (undefined :: CChar)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (-7) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 670 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColBit buf, pStrLen)
mkBindColTinyInt cstmt col mColSize = do
  l "mkBindCol: BindColTinyInt"
  let bufLen  = sizeOf (undefined :: CUChar)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (-26) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 677 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColTinyInt buf, pStrLen)
mkBindColShort cstmt col mColSize = do
  l "mkBindCol: BindColShort"
  let bufLen  = sizeOf (undefined :: CShort)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (-15) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 684 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColShort buf, pStrLen)
mkBindColLong cstmt col mColSize = do
  l "mkBindCol: BindColSize"
  let bufLen  = sizeOf (undefined :: CLong)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (-16) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 691 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColLong buf, pStrLen)
mkBindColBigInt cstmt col mColSize = do
  l "mkBindCol: BindColBigInt"
  let bufLen  = sizeOf (undefined :: CInt)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (-25) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 698 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColBigInt buf, pStrLen)
mkBindColFloat cstmt col mColSize = do
  l "mkBindCol: BindColFloat"
  let bufLen  = sizeOf (undefined :: CFloat)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (7) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 705 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColFloat buf, pStrLen)
mkBindColDouble cstmt col mColSize = do
  l "mkBindCol: BindColDouble"
  let bufLen  = sizeOf (undefined :: CDouble)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (8) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 712 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColDouble buf, pStrLen)
mkBindColBinary cstmt col mColSize = do
  l "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
{-# LINE 720 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColBinary buf (fromIntegral bufLen) col, pStrLen)
mkBindColDate cstmt col mColSize = do
  l "mkBindCol: BindColDate"
  let bufLen = sizeOf (undefined :: StructDate)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (91) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 727 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColDate buf, pStrLen)
mkBindColTime cstmt col mColSize = do
  l "mkBindCol: BindColTime"
  let bufLen = sizeOf (undefined :: StructTime)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (92) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 734 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColTime buf, pStrLen)
mkBindColTimestamp cstmt col mColSize = do
  l "mkBindCol: BindColTimestamp"
  let bufLen = sizeOf (undefined :: StructTimestamp)
  buf     <- malloc
  pStrLen <- malloc
  sqlBindCol cstmt col (93) (castPtr buf) (fromIntegral bufLen) pStrLen
{-# LINE 741 "Database/HDBC/ODBC/Statement.hsc" #-}
  return (BindColTimestamp buf, pStrLen)
mkBindColGetData col = do
  l "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 ()

-- | This assumes that SQL_ATTR_MAX_LENGTH is set to zero, otherwise, we
-- cannot detect truncated columns. See "returning Data in Bound Columns":
--     http://msdn.microsoft.com/en-us/library/ms712424(v=vs.85).aspx
-- Also note that the strLen value of SQL_NTS denotes a null terminated string,
-- but is only valid as input, so we don't make use of it here:
--     http://msdn.microsoft.com/en-us/library/ms713532(v=VS.85).aspx
bindColToSqlValue :: Ptr CStmt -> (BindCol, Ptr Int64) -> IO SqlValue
{-# LINE 769 "Database/HDBC/ODBC/Statement.hsc" #-}
bindColToSqlValue pcstmt (BindColGetData col, _) = do
  l "bindColToSqlValue: BindColGetData"
  getColData pcstmt 1 col
{-# LINE 772 "Database/HDBC/ODBC/Statement.hsc" #-}
bindColToSqlValue pcstmt (bindCol, pStrLen) = do
  l "bindColToSqlValue"
  strLen <- peek pStrLen
  case strLen of
    -1 -> return SqlNull
{-# LINE 777 "Database/HDBC/ODBC/Statement.hsc" #-}
    -4  -> getLongColData pcstmt bindCol    
{-# LINE 778 "Database/HDBC/ODBC/Statement.hsc" #-}
    _                      -> bindColToSqlValue' pcstmt bindCol strLen

-- | This is a worker function for `bindcolToSqlValue`. Note that the case
-- where the data is null should already be handled by this stage.
bindColToSqlValue' :: Ptr CStmt -> BindCol -> Int64 -> IO SqlValue
{-# LINE 783 "Database/HDBC/ODBC/Statement.hsc" #-}
bindColToSqlValue' pcstmt (BindColString buf bufLen col) strLen
  | bufLen >= strLen = do
      bs <- B.packCStringLen (buf, fromIntegral strLen)
      l $ "bindColToSqlValue BindColString " ++ show bs ++ " " ++ show strLen
      return $ SqlByteString bs
  | otherwise = getColData pcstmt 1 col
{-# LINE 789 "Database/HDBC/ODBC/Statement.hsc" #-}
bindColToSqlValue' pcstmt (BindColWString buf bufLen col) strLen
  | bufLen >= strLen = do
      bs <- B.packCStringLen (castPtr buf, fromIntegral strLen)
      l $ "bindColToSqlValue BindColWString " ++ show bs ++ " " ++ show strLen
      return $ SqlByteString bs
  | otherwise = getColData pcstmt 1 col
{-# LINE 795 "Database/HDBC/ODBC/Statement.hsc" #-}
bindColToSqlValue' _ (BindColBit     buf) strLen = do
  bit <- peek buf
  l $ "bindColToSqlValue BindColBit " ++ show bit
  return $ SqlChar (castCUCharToChar bit)
bindColToSqlValue' _ (BindColTinyInt buf) strLen = do
  tinyInt <- peek buf
  l $ "bindColToSqlValue BindColTinyInt " ++ show tinyInt
  return $ SqlChar (castCCharToChar tinyInt)
bindColToSqlValue' _ (BindColShort   buf) strLen = do
  short <- peek buf
  l $ "bindColToSqlValue BindColShort" ++ show short
  return $ SqlInt32 (fromIntegral short)
bindColToSqlValue' _ (BindColLong    buf) strLen = do
  long <- peek buf
  l $ "bindColToSqlValue BindColLong " ++ show long
  return $ SqlInt32 (fromIntegral long)
bindColToSqlValue' _ (BindColBigInt  buf) strLen = do
  bigInt <- peek buf
  l $ "bindColToSqlValue BindColBigInt " ++ show bigInt
  return $ SqlInt64 (fromIntegral bigInt)
bindColToSqlValue' _ (BindColFloat   buf) strLen = do
  float <- peek buf
  l $ "bindColToSqlValue BindColFloat " ++ show float
  return $ SqlDouble (realToFrac float)
bindColToSqlValue' _ (BindColDouble  buf) strLen = do
  double <- peek buf
  l $ "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)
      l $ "bindColToSqlValue BindColBinary " ++ show bs
      return $ SqlByteString bs
  | otherwise = getColData pcstmt (-2) col
{-# LINE 829 "Database/HDBC/ODBC/Statement.hsc" #-}
bindColToSqlValue' _ (BindColDate buf) strLen = do
  StructDate year month day <- peek buf
  l $ "bindColToSqlValue BindColDate"
  return $ SqlLocalDate $ fromGregorian
    (fromIntegral year) (fromIntegral month) (fromIntegral day)
bindColToSqlValue' _ (BindColTime buf) strLen = do
  StructTime hour minute second <- peek buf
  l $ "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
  l $ "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 :: Ptr CStmt -> 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

-- FIXME: needs a faster algorithm.
fexecutemany :: SState -> [[SqlValue]] -> IO ()
fexecutemany sstate arglist =
    mapM_ (fexecute sstate) arglist >> return ()

-- Finish and change state
public_ffinish :: SState -> IO ()
public_ffinish sstate = do
  l "public_ffinish"
  modifyMVar_ (stomv sstate) freeMStmt
  modifyMVar_ (bindColsMV sstate) freeBindCols
 where
  freeMStmt Nothing    = return Nothing
  freeMStmt (Just sth) = ffinish sth >> return Nothing
  freeBindCols Nothing = return Nothing
  freeBindCols (Just bindCols) = do
    l "public_ffinish: freeing bindcols"
    mapM_ (\(bindCol, pSqlLen) -> freeBindCol bindCol >> free pSqlLen) bindCols
    return Nothing

ffinish :: Stmt -> IO ()
ffinish stmt = withRawStmt stmt $ sqlFreeHandleSth_app 


foreign import ccall safe "hdbc-odbc-helper.h wrapobjodbc"
  wrapstmt :: Ptr CStmt -> Ptr WrappedCConn -> IO (Ptr WrappedCStmt)

foreign import ccall safe "sql.h SQLDescribeCol"
{-# LINE 896 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlDescribeCol :: Ptr CStmt   
                 -> Int16 -- ^ Column number
{-# LINE 898 "Database/HDBC/ODBC/Statement.hsc" #-}
                 -> CString     -- ^ Column name
                 -> Int16 -- ^ Buffer length
{-# LINE 900 "Database/HDBC/ODBC/Statement.hsc" #-}
                 -> Ptr (Int16) -- ^ name length ptr
{-# LINE 901 "Database/HDBC/ODBC/Statement.hsc" #-}
                 -> Ptr (Int16) -- ^ data type ptr
{-# LINE 902 "Database/HDBC/ODBC/Statement.hsc" #-}
                 -> Ptr (Word64) -- ^ column size ptr
{-# LINE 903 "Database/HDBC/ODBC/Statement.hsc" #-}
                 -> Ptr (Int16) -- ^ decimal digits ptr
{-# LINE 904 "Database/HDBC/ODBC/Statement.hsc" #-}
                 -> Ptr (Int16) -- ^ nullable ptr
{-# LINE 905 "Database/HDBC/ODBC/Statement.hsc" #-}
                 -> IO Int16
{-# LINE 906 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "sql.h SQLGetData"
{-# LINE 908 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlGetData :: Ptr CStmt       -- ^ statement handle
             -> Word16 -- ^ Column number
{-# LINE 910 "Database/HDBC/ODBC/Statement.hsc" #-}
             -> Int16 -- ^ target type
{-# LINE 911 "Database/HDBC/ODBC/Statement.hsc" #-}
             -> CString -- ^ target value pointer (void * in C)
             -> Int64 -- ^ buffer len
{-# LINE 913 "Database/HDBC/ODBC/Statement.hsc" #-}
             -> Ptr (Int64)
{-# LINE 914 "Database/HDBC/ODBC/Statement.hsc" #-}
             -> IO Int16
{-# LINE 915 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "sql.h SQLBindCol"
{-# LINE 917 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlBindCol :: Ptr CStmt            -- ^ statement handle
             -> Word16 -- ^ Column number
{-# LINE 919 "Database/HDBC/ODBC/Statement.hsc" #-}
             -> Int16  -- ^ target type
{-# LINE 920 "Database/HDBC/ODBC/Statement.hsc" #-}
             -> Ptr ColBuf           -- ^ target value pointer (void * in C)
             -> Int64       -- ^ buffer len
{-# LINE 922 "Database/HDBC/ODBC/Statement.hsc" #-}
             -> Ptr (Int64) -- ^ strlen_or_indptr
{-# LINE 923 "Database/HDBC/ODBC/Statement.hsc" #-}
             -> IO Int16
{-# LINE 924 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "hdbc-odbc-helper.h sqlFreeHandleSth_app"
  sqlFreeHandleSth_app :: Ptr WrappedCStmt -> IO ()

foreign import ccall safe "hdbc-odbc-helper.h &sqlFreeHandleSth_finalizer"
  sqlFreeHandleSth_ptr :: FunPtr (Ptr WrappedCStmt -> IO ())

foreign import ccall safe "sql.h SQLPrepare"
{-# LINE 932 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlPrepare :: Ptr CStmt -> CString -> Int32 
{-# LINE 933 "Database/HDBC/ODBC/Statement.hsc" #-}
             -> IO Int16
{-# LINE 934 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "sql.h SQLExecute"
{-# LINE 936 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlExecute :: Ptr CStmt -> IO Int16
{-# LINE 937 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "sql.h SQLAllocHandle"
{-# LINE 939 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlAllocStmtHandle :: Int16 -> Ptr CConn ->
{-# LINE 940 "Database/HDBC/ODBC/Statement.hsc" #-}
                        Ptr (Ptr CStmt) -> IO Int16
{-# LINE 941 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "sql.h SQLNumResultCols"
{-# LINE 943 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlNumResultCols :: Ptr CStmt -> Ptr Int16 
{-# LINE 944 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> IO Int16
{-# LINE 945 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "sql.h SQLRowCount"
{-# LINE 947 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlRowCount :: Ptr CStmt -> Ptr Int32 -> IO Int16
{-# LINE 948 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "sql.h SQLBindParameter"
{-# LINE 950 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlBindParameter :: Ptr CStmt -- ^ Statement handle
                   -> Word16 -- ^ Parameter Number
{-# LINE 952 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Int16 -- ^ Input or output
{-# LINE 953 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Int16 -- ^ Value type
{-# LINE 954 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Int16 -- ^ Parameter type
{-# LINE 955 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Word64 -- ^ column size
{-# LINE 956 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Int16 -- ^ decimal digits
{-# LINE 957 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> CString   -- ^ Parameter value pointer
                   -> Int64 -- ^ buffer length
{-# LINE 959 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Ptr Int64 -- ^ strlen_or_indptr
{-# LINE 960 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> IO Int16
{-# LINE 961 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "hdbc-odbc-helper.h &nullDataHDBC"
  nullDataHDBC :: Ptr Int64
{-# LINE 964 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "sql.h SQLDescribeParam"
{-# LINE 966 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlDescribeParam :: Ptr CStmt 
                   -> Word16 -- ^ parameter number
{-# LINE 968 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Ptr Int16 -- ^ data type ptr
{-# LINE 969 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Ptr Word64 -- ^ parameter size ptr
{-# LINE 970 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Ptr Int16 -- ^ dec digits ptr
{-# LINE 971 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> Ptr Int16 -- ^ nullable ptr
{-# LINE 972 "Database/HDBC/ODBC/Statement.hsc" #-}
                   -> IO Int16
{-# LINE 973 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "sql.h SQLFetch"
{-# LINE 975 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlFetch :: Ptr CStmt -> IO Int16
{-# LINE 976 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "hdbc-odbc-helper.h simpleSqlTables"
  simpleSqlTables :: Ptr CStmt -> IO Int16
{-# LINE 979 "Database/HDBC/ODBC/Statement.hsc" #-}

foreign import ccall safe "hdbc-odbc-helper.h simpleSqlColumns"
  simpleSqlColumns :: Ptr CStmt -> Ptr CChar -> 
                      Int16 -> IO Int16
{-# LINE 983 "Database/HDBC/ODBC/Statement.hsc" #-}

fgetparminfo :: Ptr CStmt -> IO [SqlColDesc]
fgetparminfo cstmt =
    do ncols <- getNumParams cstmt
       mapM getname [1..ncols]
    where getname icol = -- alloca $ \colnamelp ->
                         -- allocaBytes 128 $ \cscolname ->
                         alloca $ \datatypeptr ->
                         alloca $ \colsizeptr ->
                         alloca $ \nullableptr ->
              do poke datatypeptr 127 -- to test if sqlDescribeParam actually writes something to the area
                 res <- sqlDescribeParam cstmt (fromInteger $ toInteger icol) -- cscolname 127 colnamelp 
                                  datatypeptr colsizeptr nullPtr nullableptr
                 putStrLn $ show res
                 -- We need proper error handling here. Not all ODBC drivers supports SQLDescribeParam.
                 -- Not supporting SQLDescribeParam is quite allright according to the ODBC standard.
                 datatype <- peek datatypeptr
                 colsize  <- peek colsizeptr
                 nullable <- peek nullableptr
                 return $ snd $ fromOTypeInfo "" datatype colsize nullable

getNumParams :: Ptr CStmt -> IO Int16
getNumParams sthptr = alloca $ \pcount ->
    do sqlNumParams sthptr pcount >>= checkError "SQLNumResultCols" 
                                          (StmtHandle sthptr)
       peek pcount

foreign import ccall safe "sql.h SQLNumParams"
{-# LINE 1011 "Database/HDBC/ODBC/Statement.hsc" #-}
  sqlNumParams :: Ptr CStmt -> Ptr Int16 
{-# LINE 1012 "Database/HDBC/ODBC/Statement.hsc" #-}
               -> IO Int16
{-# LINE 1013 "Database/HDBC/ODBC/Statement.hsc" #-}