{-# LINE 1 "Database/HSQL/ODBC.hsc" #-}
{-# OPTIONS -fglasgow-exts #-}
{-# LINE 2 "Database/HSQL/ODBC.hsc" #-}

------------------------------------------------------------------------------
{-| Module      :  Database.HSQL.ODBC
    Copyright   :  (c) Krasimir Angelov 2003
    License     :  BSD-style

    Maintainer  :  kr.angelov@gmail.com
    Stability   :  provisional
    Portability :  portable

    The module provides interface to ODBC
-}
------------------------------------------------------------------------------

module Database.HSQL.ODBC(connect, driverConnect, module Database.HSQL) where

import Database.HSQL
import Database.HSQL.Types
import Data.Word(Word32, Word16)
import Data.Int(Int32, Int16)
import Data.Maybe
import Foreign
import Foreign.C
import Control.Monad(unless)
import Control.OldException(throwDyn)
import Control.Concurrent.MVar
import System.IO.Unsafe
import System.Time

{-# LINE 33 "Database/HSQL/ODBC.hsc" #-}


{-# LINE 35 "Database/HSQL/ODBC.hsc" #-}

{-# LINE 36 "Database/HSQL/ODBC.hsc" #-}

type SQLHANDLE = Ptr ()
type HENV = SQLHANDLE
type HDBC = SQLHANDLE
type HSTMT = SQLHANDLE
type HENVRef = ForeignPtr ()

type SQLSMALLINT  = Int16
{-# LINE 44 "Database/HSQL/ODBC.hsc" #-}
type SQLUSMALLINT = Word16
{-# LINE 45 "Database/HSQL/ODBC.hsc" #-}
type SQLINTEGER   = Int32
{-# LINE 46 "Database/HSQL/ODBC.hsc" #-}
type SQLUINTEGER  = Word32
{-# LINE 47 "Database/HSQL/ODBC.hsc" #-}
type SQLRETURN	  = SQLSMALLINT
type SQLLEN       = SQLINTEGER
type SQLULEN      = SQLINTEGER


{-# LINE 54 "Database/HSQL/ODBC.hsc" #-}

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

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

foreign import ccall "HsODBC.h SQLAllocEnv" 
{-# LINE 58 "Database/HSQL/ODBC.hsc" #-}
 sqlAllocEnv:: Ptr HENV -> IO SQLRETURN


{-# LINE 64 "Database/HSQL/ODBC.hsc" #-}
foreign import ccall "HsODBC.h &SQLFreeEnv" 
 sqlFreeEnv_p:: FunPtr (HENV -> IO ())

{-# LINE 67 "Database/HSQL/ODBC.hsc" #-}

foreign import ccall "HsODBC.h SQLAllocConnect" 
{-# LINE 69 "Database/HSQL/ODBC.hsc" #-}
 sqlAllocConnect:: HENV -> Ptr HDBC -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLFreeConnect" 
{-# LINE 71 "Database/HSQL/ODBC.hsc" #-}
 sqlFreeConnect:: HDBC -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLConnect" 
{-# LINE 73 "Database/HSQL/ODBC.hsc" #-}
 sqlConnect:: HDBC -> CString -> Int -> CString -> Int -> CString -> Int 
           -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLDriverConnect" 
{-# LINE 76 "Database/HSQL/ODBC.hsc" #-}
 sqlDriverConnect:: HDBC -> Ptr () -> CString -> SQLSMALLINT -> CString 
                 -> SQLSMALLINT -> Ptr SQLSMALLINT -> SQLUSMALLINT 
                 -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLDisconnect" 
{-# LINE 80 "Database/HSQL/ODBC.hsc" #-}
 sqlDisconnect:: HDBC -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLAllocStmt" 
{-# LINE 82 "Database/HSQL/ODBC.hsc" #-}
 sqlAllocStmt:: HDBC -> Ptr HSTMT -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLFreeStmt" 
{-# LINE 84 "Database/HSQL/ODBC.hsc" #-}
 sqlFreeStmt:: HSTMT -> SQLUSMALLINT -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLNumResultCols" 
{-# LINE 86 "Database/HSQL/ODBC.hsc" #-}
 sqlNumResultCols:: HSTMT -> Ptr SQLUSMALLINT -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLDescribeCol" 
{-# LINE 88 "Database/HSQL/ODBC.hsc" #-}
 sqlDescribeCol:: HSTMT -> SQLUSMALLINT -> CString -> SQLSMALLINT 
               -> Ptr SQLSMALLINT -> Ptr SQLSMALLINT -> Ptr SQLULEN 
               -> Ptr SQLSMALLINT -> Ptr SQLSMALLINT 
               -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLBindCol" 
{-# LINE 93 "Database/HSQL/ODBC.hsc" #-}
 sqlBindCol:: HSTMT -> SQLUSMALLINT -> SQLSMALLINT -> Ptr a 
           -> SQLLEN -> Ptr SQLINTEGER 
           -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLFetch" 
{-# LINE 97 "Database/HSQL/ODBC.hsc" #-}
 sqlFetch:: HSTMT -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLGetDiagRec" 
{-# LINE 99 "Database/HSQL/ODBC.hsc" #-}
 sqlGetDiagRec:: SQLSMALLINT -> SQLHANDLE -> SQLSMALLINT -> CString 
              -> Ptr SQLINTEGER -> CString -> SQLSMALLINT -> Ptr SQLSMALLINT 
              -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLExecDirect" 
{-# LINE 103 "Database/HSQL/ODBC.hsc" #-}
 sqlExecDirect:: HSTMT -> CString -> Int -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLSetConnectOption" 
{-# LINE 105 "Database/HSQL/ODBC.hsc" #-}
 sqlSetConnectOption:: HDBC -> SQLUSMALLINT -> SQLULEN -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLTransact" 
{-# LINE 107 "Database/HSQL/ODBC.hsc" #-}
 sqlTransact:: HENV -> HDBC -> SQLUSMALLINT -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLGetData" 
{-# LINE 109 "Database/HSQL/ODBC.hsc" #-}
 sqlGetData:: HSTMT -> SQLUSMALLINT -> SQLSMALLINT -> Ptr () 
           -> SQLINTEGER -> Ptr SQLINTEGER 
           -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLTables" 
{-# LINE 113 "Database/HSQL/ODBC.hsc" #-}
 sqlTables:: HSTMT -> CString -> SQLSMALLINT -> CString -> SQLSMALLINT 
          -> CString -> SQLSMALLINT -> CString -> SQLSMALLINT 
          -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLColumns" 
{-# LINE 117 "Database/HSQL/ODBC.hsc" #-}
 sqlColumns:: HSTMT -> CString -> SQLSMALLINT -> CString -> SQLSMALLINT 
           -> CString -> SQLSMALLINT -> CString -> SQLSMALLINT 
           -> IO SQLRETURN
foreign import ccall "HsODBC.h SQLMoreResults" 
{-# LINE 121 "Database/HSQL/ODBC.hsc" #-}
 sqlMoreResults:: HSTMT -> IO SQLRETURN


{-# LINE 128 "Database/HSQL/ODBC.hsc" #-}

------------------------------------------------------------------------------
-- routines for handling exceptions
------------------------------------------------------------------------------

handleSqlResult :: SQLSMALLINT -> SQLHANDLE -> SQLRETURN -> IO ()
handleSqlResult handleType handle res
	| res == (0) || res == (100) = return ()
{-# LINE 136 "Database/HSQL/ODBC.hsc" #-}
	| res == (1) = do
{-# LINE 137 "Database/HSQL/ODBC.hsc" #-}

{-# LINE 141 "Database/HSQL/ODBC.hsc" #-}
	    return ()

{-# LINE 143 "Database/HSQL/ODBC.hsc" #-}
	| res == (-2) = throwDyn SqlInvalidHandle
{-# LINE 144 "Database/HSQL/ODBC.hsc" #-}
	| res == (2) = throwDyn SqlStillExecuting
{-# LINE 145 "Database/HSQL/ODBC.hsc" #-}
	| res == (99) = throwDyn SqlNeedData
{-# LINE 146 "Database/HSQL/ODBC.hsc" #-}
	| res == (-1) = do
{-# LINE 147 "Database/HSQL/ODBC.hsc" #-}
	    e <- getSqlError
	    throwDyn e
	| otherwise = error (show res)
	where
	  getSqlError =
	    allocaBytes 256 $ \pState ->
	    alloca $ \pNative ->
	    allocaBytes 256 $ \pMsg ->
	    alloca $ \pTextLen -> do
		  res <- sqlGetDiagRec handleType handle 1 
                                       pState pNative pMsg 256 pTextLen
		  if res == (100)
{-# LINE 159 "Database/HSQL/ODBC.hsc" #-}
		    then return SqlNoData
		    else do
		      state  <- peekCString pState
		      native <- peek pNative
		      msg    <- peekCString pMsg
		      return (SqlError { seState=state
                                       , seNativeError=fromIntegral native
                                       , seErrorMsg=msg })

------------------------------------------------------------------------------
-- keeper of HENV
------------------------------------------------------------------------------

{-# NOINLINE myEnvironment #-}
myEnvironment :: HENVRef
myEnvironment = unsafePerformIO $ alloca $ \ (phEnv :: Ptr HENV) -> do
	res <- sqlAllocEnv phEnv
	hEnv <- peek phEnv
	handleSqlResult 0 nullPtr res
	newForeignPtr sqlFreeEnv_p hEnv

------------------------------------------------------------------------------
-- Connect/Disconnect
------------------------------------------------------------------------------

-- | Makes a new connection to the ODBC data source
connect :: String               -- ^ Data source name
        -> String               -- ^ User identifier
        -> String               -- ^ Authentication string (password)
        -> IO Connection        -- ^ the returned value represents the new connection
connect server user authentication = connectHelper $ \hDBC ->
        withCString server $ \pServer ->
	withCString user $ \pUser ->
	withCString authentication $ \pAuthentication ->
	sqlConnect hDBC 
                   pServer (-3) 
{-# LINE 195 "Database/HSQL/ODBC.hsc" #-}
                   pUser (-3) 
{-# LINE 196 "Database/HSQL/ODBC.hsc" #-}
                   pAuthentication (-3)
{-# LINE 197 "Database/HSQL/ODBC.hsc" #-}

-- | 'driverConnect' is an alternative to 'connect'. It supports data sources that 
-- require more connection information than the three arguments in 'connect'
-- and data sources that are not defined in the system information.
driverConnect :: String               -- ^ Connection string
              -> IO Connection        -- ^ the returned value represents the new connection
driverConnect connString = connectHelper $ \hDBC -> 
        withCString connString $ \pConnString ->
	allocaBytes 1024 $ \pOutConnString ->
	alloca $ \pLen ->
	sqlDriverConnect hDBC nullPtr pConnString (-3) pOutConnString 1024 pLen (0)
{-# LINE 208 "Database/HSQL/ODBC.hsc" #-}

connectHelper :: (HDBC -> IO SQLRETURN) -> IO Connection
connectHelper connectFunction = withForeignPtr myEnvironment $ \hEnv -> do
        hDBC <- alloca $ \ (phDBC :: Ptr HDBC) -> do
	    res <- sqlAllocConnect hEnv phDBC
	    handleSqlResult (1) hEnv res
{-# LINE 214 "Database/HSQL/ODBC.hsc" #-}
	    peek phDBC
	res <- connectFunction hDBC
	handleSqlResult (2) hDBC res
{-# LINE 217 "Database/HSQL/ODBC.hsc" #-}
	refFalse <- newMVar False
	let connection = (Connection
			{ connDisconnect = disconnect hDBC
			, connExecute    = execute hDBC
			, connQuery      = query connection hDBC
			, connTables     = tables connection hDBC
			, connDescribe   = describe connection hDBC
			, connBeginTransaction = beginTransaction myEnvironment hDBC
			, connCommitTransaction = commitTransaction myEnvironment hDBC
			, connRollbackTransaction = rollbackTransaction myEnvironment hDBC
			, connClosed     = refFalse
			})
	return connection
	where
		disconnect :: HDBC -> IO ()
		disconnect hDBC = do
			sqlDisconnect hDBC >>= handleSqlResult (2) hDBC
{-# LINE 234 "Database/HSQL/ODBC.hsc" #-}
			sqlFreeConnect hDBC >>= handleSqlResult (2) hDBC
{-# LINE 235 "Database/HSQL/ODBC.hsc" #-}

		execute :: HDBC -> String -> IO ()
		execute hDBC query = allocaBytes (4) $
{-# LINE 238 "Database/HSQL/ODBC.hsc" #-}
                  \pStmt -> do
			res <- sqlAllocStmt hDBC pStmt
			handleSqlResult (2) hDBC res
{-# LINE 241 "Database/HSQL/ODBC.hsc" #-}
			hSTMT <- peek pStmt
			withCStringLen query $ \(pQuery,len) -> do
			     res <- sqlExecDirect hSTMT pQuery len
                             handleSqlResult (3) hSTMT res
{-# LINE 245 "Database/HSQL/ODBC.hsc" #-}
			res <- sqlFreeStmt hSTMT (1)
{-# LINE 246 "Database/HSQL/ODBC.hsc" #-}
			handleSqlResult (3) hSTMT res
{-# LINE 247 "Database/HSQL/ODBC.hsc" #-}

		stmtBufferSize = 256

		withStatement :: Connection -> HDBC -> (HSTMT -> IO SQLRETURN) -> IO Statement
		withStatement connection hDBC f = 
			allocaBytes (276) $ \pFIELD -> do
{-# LINE 253 "Database/HSQL/ODBC.hsc" #-}
			res <- sqlAllocStmt hDBC (((\hsc_ptr -> hsc_ptr `plusPtr` 0)) pFIELD)
{-# LINE 254 "Database/HSQL/ODBC.hsc" #-}
			handleSqlResult (2) hDBC res
{-# LINE 255 "Database/HSQL/ODBC.hsc" #-}
			hSTMT <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) pFIELD
{-# LINE 256 "Database/HSQL/ODBC.hsc" #-}
			let handleResult res = handleSqlResult (3) hSTMT res
{-# LINE 257 "Database/HSQL/ODBC.hsc" #-}

{-# LINE 261 "Database/HSQL/ODBC.hsc" #-}
			f hSTMT >>= handleResult
			fields <- moveToFirstResult hSTMT pFIELD
			buffer <- mallocBytes (fromIntegral stmtBufferSize)
			refFalse <- newMVar False
			let statement = Statement
				{ stmtConn   = connection
				, stmtClose  = closeStatement hSTMT buffer
				, stmtFetch  = fetch hSTMT
				, stmtGetCol = getColValue hSTMT buffer
				, stmtFields = fields
				, stmtClosed = refFalse
				}
			return statement
			where
				moveToFirstResult :: HSTMT -> Ptr a -> IO [FieldDef]
				moveToFirstResult hSTMT pFIELD = do
				  res <- sqlNumResultCols hSTMT (((\hsc_ptr -> hsc_ptr `plusPtr` 4)) pFIELD)
{-# LINE 278 "Database/HSQL/ODBC.hsc" #-}
				  handleSqlResult (3) hSTMT res
{-# LINE 279 "Database/HSQL/ODBC.hsc" #-}
				  count <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) pFIELD
{-# LINE 280 "Database/HSQL/ODBC.hsc" #-}
				  if count == 0
				    then do

{-# LINE 286 "Database/HSQL/ODBC.hsc" #-}
				      res <- sqlMoreResults hSTMT
				      handleSqlResult (3) hSTMT res
{-# LINE 288 "Database/HSQL/ODBC.hsc" #-}
				      if res == (100)
{-# LINE 289 "Database/HSQL/ODBC.hsc" #-}
				        then return []
				        else moveToFirstResult hSTMT pFIELD
				    else
				      getFieldDefs hSTMT pFIELD 1 count

				getFieldDefs :: HSTMT -> Ptr a -> SQLUSMALLINT -> SQLUSMALLINT -> IO [FieldDef]
				getFieldDefs hSTMT pFIELD n count
					| n > count  = return []
					| otherwise = do
						res <- sqlDescribeCol hSTMT n (((\hsc_ptr -> hsc_ptr `plusPtr` 6)) pFIELD) (255) (((\hsc_ptr -> hsc_ptr `plusPtr` 262)) pFIELD) (((\hsc_ptr -> hsc_ptr `plusPtr` 264)) pFIELD) (((\hsc_ptr -> hsc_ptr `plusPtr` 268)) pFIELD) (((\hsc_ptr -> hsc_ptr `plusPtr` 272)) pFIELD) (((\hsc_ptr -> hsc_ptr `plusPtr` 274)) pFIELD)
{-# LINE 299 "Database/HSQL/ODBC.hsc" #-}
						handleSqlResult (3) hSTMT res
{-# LINE 300 "Database/HSQL/ODBC.hsc" #-}
						name <- peekCString (((\hsc_ptr -> hsc_ptr `plusPtr` 6)) pFIELD)
{-# LINE 301 "Database/HSQL/ODBC.hsc" #-}
						dataType <- ((\hsc_ptr -> peekByteOff hsc_ptr 264)) pFIELD
{-# LINE 302 "Database/HSQL/ODBC.hsc" #-}
						columnSize <- ((\hsc_ptr -> peekByteOff hsc_ptr 268)) pFIELD
{-# LINE 303 "Database/HSQL/ODBC.hsc" #-}
						decimalDigits <- ((\hsc_ptr -> peekByteOff hsc_ptr 272)) pFIELD
{-# LINE 304 "Database/HSQL/ODBC.hsc" #-}
						(nullable :: SQLSMALLINT) <- ((\hsc_ptr -> peekByteOff hsc_ptr 274)) pFIELD
{-# LINE 305 "Database/HSQL/ODBC.hsc" #-}
						let sqlType = mkSqlType dataType columnSize decimalDigits
						fields <- getFieldDefs hSTMT pFIELD (n+1) count
						return ((name,sqlType,toBool nullable):fields)

		mkSqlType :: SQLSMALLINT -> SQLULEN -> SQLSMALLINT -> SqlType
		mkSqlType (1)         size    _    = SqlChar (fromIntegral size)
{-# LINE 311 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (12)      size    _    = SqlVarChar (fromIntegral size)
{-# LINE 312 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-1)  size    _    = SqlLongVarChar (fromIntegral size)
{-# LINE 313 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (3)      size    prec = SqlDecimal (fromIntegral size) (fromIntegral prec)
{-# LINE 314 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (2)      size    prec = SqlNumeric (fromIntegral size) (fromIntegral prec)
{-# LINE 315 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (5)     _       _    = SqlSmallInt
{-# LINE 316 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (4)      _       _    = SqlInteger
{-# LINE 317 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (7)         _       _    = SqlReal
{-# LINE 318 "Database/HSQL/ODBC.hsc" #-}
		-- From: http://msdn.microsoft.com/library/en-us/odbc/htm/odappdpr_2.asp
		-- "Depending on the implementation, the precision of SQL_FLOAT can be either 24 or 53:
		-- if it is 24, the SQL_FLOAT data type is the same as SQL_REAL;
		-- if it is 53, the SQL_FLOAT data type is the same as SQL_DOUBLE."
		mkSqlType (6)        _        _    = SqlFloat
{-# LINE 323 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (8)		_    	_    = SqlDouble
{-# LINE 324 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-7)          _       	_    = SqlBit
{-# LINE 325 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-6)      _    	_    = SqlTinyInt
{-# LINE 326 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-5)       _    	_    = SqlBigInt
{-# LINE 327 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-2)       size    _    = SqlBinary (fromIntegral size)
{-# LINE 328 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-3)    size    _    = SqlVarBinary (fromIntegral size)
{-# LINE 329 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-4)size    _    = SqlLongVarBinary (fromIntegral size)
{-# LINE 330 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (9)         _    	_    = SqlDate
{-# LINE 331 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (10)         _    	_    = SqlTime
{-# LINE 332 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (11)	_    	_    = SqlDateTime
{-# LINE 333 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-8)        size	_    = SqlWChar (fromIntegral size)
{-# LINE 334 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-9)     size    _    = SqlWVarChar (fromIntegral size)
{-# LINE 335 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType (-10)	size    _    = SqlWLongVarChar (fromIntegral size)
{-# LINE 336 "Database/HSQL/ODBC.hsc" #-}
		mkSqlType tp                        _       _    = SqlUnknown (fromIntegral tp)

		query :: Connection -> HDBC -> String -> IO Statement
		query connection hDBC q = withStatement connection hDBC doQuery
    			where doQuery hSTMT = withCStringLen q (uncurry (sqlExecDirect hSTMT))

		beginTransaction myEnvironment hDBC = do
			sqlSetConnectOption hDBC (102) (0)
{-# LINE 344 "Database/HSQL/ODBC.hsc" #-}
			return ()

		commitTransaction myEnvironment hDBC = withForeignPtr myEnvironment $ \hEnv -> do
			sqlTransact hEnv hDBC (0)
{-# LINE 348 "Database/HSQL/ODBC.hsc" #-}
			sqlSetConnectOption hDBC (102) (1)
{-# LINE 349 "Database/HSQL/ODBC.hsc" #-}
			return ()

		rollbackTransaction myEnvironment hDBC = withForeignPtr myEnvironment $ \hEnv -> do
			sqlTransact hEnv hDBC (1)
{-# LINE 353 "Database/HSQL/ODBC.hsc" #-}
			sqlSetConnectOption hDBC (102) (1)
{-# LINE 354 "Database/HSQL/ODBC.hsc" #-}
			return ()

		tables :: Connection -> HDBC -> IO [String]
		tables connection hDBC = do
			stmt <- withStatement connection hDBC sqlTables'
			-- SQLTables returns (column names may vary):
			-- Column name     #   Type
			-- TABLE_NAME      3   VARCHAR
			collectRows (\s -> getFieldValue s "TABLE_NAME") stmt
			where sqlTables' hSTMT = sqlTables hSTMT nullPtr 0 nullPtr 0 nullPtr 0 nullPtr 0

		describe :: Connection -> HDBC -> String -> IO [FieldDef]
		describe connection hDBC table = do
			stmt <- withStatement connection hDBC (sqlColumns' table)
			collectRows getColumnInfo stmt
			where
				sqlColumns' table hSTMT =
				  withCStringLen table (\(pTable,len) ->
				        sqlColumns hSTMT nullPtr 0 nullPtr 0 pTable (fromIntegral len) nullPtr 0)
					-- SQLColumns returns (column names may vary):
					-- Column name     #   Type
					-- COLUMN_NAME     4   Varchar not NULL
					-- DATA_TYPE       5   Smallint not NULL
					-- COLUMN_SIZE     7   Integer
					-- DECIMAL_DIGITS  9   Smallint
					-- NULLABLE       11   Smallint not NULL

				getColumnInfo stmt = do
				  column_name <- getFieldValue stmt "COLUMN_NAME"
				  (data_type::Int) <- getFieldValue stmt "DATA_TYPE"
				  (column_size::Int) <- getFieldValue' stmt "COLUMN_SIZE" 0
				  (decimal_digits::Int) <- getFieldValue' stmt "DECIMAL_DIGITS" 0
				  (nullable::Int) <- getFieldValue stmt "NULLABLE"
				  let sqlType = mkSqlType (fromIntegral data_type) (fromIntegral column_size) (fromIntegral decimal_digits)
				  return (column_name, sqlType, toBool nullable)

		fetch :: HSTMT -> IO Bool
		fetch hSTMT = do
			res <- sqlFetch hSTMT
			handleSqlResult (3) hSTMT res
{-# LINE 394 "Database/HSQL/ODBC.hsc" #-}
			return (res /= (100))
{-# LINE 395 "Database/HSQL/ODBC.hsc" #-}

		getColValue :: HSTMT -> CString -> Int -> FieldDef -> (FieldDef -> CString -> Int -> IO a) -> IO a
		getColValue hSTMT buffer colNumber fieldDef f = do
			(res,len_or_ind) <- getData buffer (fromIntegral stmtBufferSize)
			if len_or_ind == (-1)
{-# LINE 400 "Database/HSQL/ODBC.hsc" #-}
			  then f fieldDef nullPtr 0
			  else if res == (1)
{-# LINE 402 "Database/HSQL/ODBC.hsc" #-}
				 then getLongData len_or_ind
				 else f fieldDef buffer (fromIntegral len_or_ind)
			where
				getData :: CString -> SQLINTEGER -> IO (SQLRETURN, SQLINTEGER)
				getData buffer size = alloca $ \lenP -> do
					res <- sqlGetData hSTMT (fromIntegral colNumber+1) (1) (castPtr buffer) size lenP
{-# LINE 408 "Database/HSQL/ODBC.hsc" #-}
					handleSqlResult (3) hSTMT res
{-# LINE 409 "Database/HSQL/ODBC.hsc" #-}
					len_or_ind <- peek lenP
					return (res, len_or_ind)

				-- gets called only when there is more data than would
				-- fit in the normal buffer. This call to
				-- SQLGetData() will fetch the rest of the data.
				-- We create a new buffer big enough to hold the
				-- old and the new data, copy the old data into
				-- it and put the new data in buffer after the old.
				getLongData len = allocaBytes (fromIntegral newBufSize) $ \newBuf -> do
					copyBytes newBuf buffer stmtBufferSize
		        	-- The last byte of the old data with always be null,
					-- so it is overwritten with the first byte of  the new data.
					let newDataStart = newBuf `plusPtr` (stmtBufferSize - 1)
					    newDataLen = newBufSize - (fromIntegral stmtBufferSize - 1)
					(res,_) <- getData newDataStart newDataLen
					f fieldDef newBuf (fromIntegral newBufSize-1)
					where
						newBufSize = len+1 -- to allow for terminating null character

		closeStatement :: HSTMT -> CString -> IO ()
		closeStatement hSTMT buffer = do
			free buffer
			sqlFreeStmt hSTMT (1) >>= handleSqlResult (3) hSTMT
{-# LINE 433 "Database/HSQL/ODBC.hsc" #-}