odbc-0.0.1: Haskell binding to the ODBC API

Safe HaskellNone
LanguageHaskell2010

Database.ODBC

Contents

Description

ODBC database API.

Synopsis

Building

You have to compile your projects using the -threaded flag to GHC. In your .cabal file, this would look like:

ghc-options: -threaded

Basic library usage

An example program using this library:

{-# LANGUAGE OverloadedStrings #-}
import Database.ODBC
main :: IO ()
main = do
  conn <-
    connect
      "DRIVER={ODBC Driver 13 for SQL Server};SERVER=192.168.99.100;Uid=SA;Pwd=Passw0rd"
  exec conn "DROP TABLE IF EXISTS example"
  exec conn "CREATE TABLE example (id int, name ntext, likes_tacos bit)"
  exec conn "INSERT INTO example VALUES (1, 'Chris', 0), (2, 'Mary', 1)"
  rows <- query conn "SELECT * FROM example"
  print rows
  close conn

You need the OverloadedStrings extension so that you can write Text values for the queries and executions.

The output of this program is to print a list of rows of Value:

[[Just (IntValue 1),Just (TextValue "Chris"),Just (BoolValue False)],[Just (IntValue 2),Just (TextValue "Mary"),Just (BoolValue True)]]

Connect/disconnect

connect Source #

Arguments

:: MonadIO m 
=> Text

An ODBC connection string.

-> m Connection

A connection to the database. You should call close on it when you're done. If you forget to, then the connection will only be closed when there are no more references to the Connection value in your program, which might never happen. So take care.

Connect using the given connection string.

close Source #

Arguments

:: MonadIO m 
=> Connection

A connection to the database.

-> m () 

Close the connection. Further use of the Connection will throw an exception. Double closes also throw an exception to avoid architectural mistakes.

data Connection Source #

Connection to a database. Use of this connection is thread-safe. When garbage collected, the connection will be closed if not done already.

Executing queries

exec Source #

Arguments

:: MonadIO m 
=> Connection

A connection to the database.

-> Text

SQL statement.

-> m () 

Execute a statement on the database.

query Source #

Arguments

:: MonadIO m 
=> Connection

A connection to the database.

-> Text

SQL query.

-> m [[Maybe Value]]

A strict list of rows. This list is not lazy, so if you are retrieving a large data set, be aware that all of it will be loaded into memory.

Query and return a list of rows.

data Value Source #

A value used for input/output with the database.

Constructors

TextValue !Text

A Unicode text value.

BytesValue !ByteString

A vector of bytes. It might be a string, but we don't know the encoding. Use decodeUtf8 if the string is UTF-8 encoded, or decodeUtf16LE if it is UTF-16 encoded. For other encodings, see the Haskell text-icu package.

BoolValue !Bool

A simple boolean.

DoubleValue !Double

Floating point values that fit in a Double.

IntValue !Int

Integer values that fit in an Int.

Instances

Eq Value Source # 

Methods

(==) :: Value -> Value -> Bool #

(/=) :: Value -> Value -> Bool #

Data Value Source # 

Methods

gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Value -> c Value #

gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c Value #

toConstr :: Value -> Constr #

dataTypeOf :: Value -> DataType #

dataCast1 :: Typeable (* -> *) t => (forall d. Data d => c (t d)) -> Maybe (c Value) #

dataCast2 :: Typeable (* -> * -> *) t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Value) #

gmapT :: (forall b. Data b => b -> b) -> Value -> Value #

gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Value -> r #

gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Value -> r #

gmapQ :: (forall d. Data d => d -> u) -> Value -> [u] #

gmapQi :: Int -> (forall d. Data d => d -> u) -> Value -> u #

gmapM :: Monad m => (forall d. Data d => d -> m d) -> Value -> m Value #

gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Value -> m Value #

gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Value -> m Value #

Ord Value Source # 

Methods

compare :: Value -> Value -> Ordering #

(<) :: Value -> Value -> Bool #

(<=) :: Value -> Value -> Bool #

(>) :: Value -> Value -> Bool #

(>=) :: Value -> Value -> Bool #

max :: Value -> Value -> Value #

min :: Value -> Value -> Value #

Show Value Source # 

Methods

showsPrec :: Int -> Value -> ShowS #

show :: Value -> String #

showList :: [Value] -> ShowS #

Generic Value Source # 

Associated Types

type Rep Value :: * -> * #

Methods

from :: Value -> Rep Value x #

to :: Rep Value x -> Value #

NFData Value Source # 

Methods

rnf :: Value -> () #

type Rep Value Source # 

Streaming results

Loading all rows of a query result can be expensive and use a lot of memory. Another way to load data is by fetching one row at a time, called streaming.

Here's an example of finding the longest string from a set of rows. It outputs "Hello!". We only work on TextValue, we ignore for example the NULL row.

{-# LANGUAGE OverloadedStrings, LambdaCase #-}
import qualified Data.Text as T
import           Control.Exception
import           Database.ODBC
main :: IO ()
main =
  bracket
    (connect
       "DRIVER={ODBC Driver 13 for SQL Server};SERVER=192.168.99.100;Uid=SA;Pwd=Passw0rd")
    close
    (\conn -> do
       exec conn "DROP TABLE IF EXISTS example"
       exec conn "CREATE TABLE example (name ntext)"
       exec conn "INSERT INTO example VALUES ('foo'),('bar'),(NULL),('mu'),('Hello!')"
       longest <-
         stream
           conn
           "SELECT * FROM example"
           (\longest ->
              \case
                [Just (TextValue text)] ->
                  pure
                    (Continue
                       (if T.length text > T.length longest
                          then text
                          else longest))
                _ -> pure (Continue longest))
           ""
       print longest)

stream Source #

Arguments

:: (MonadIO m, MonadUnliftIO m) 
=> Connection

A connection to the database.

-> Text

SQL query.

-> (state -> [Maybe Value] -> m (Step state))

A stepping function that gets as input the current state and a row, returning either a new state or a final result.

-> state

A state that you can use for the computation. Strictly evaluated each iteration.

-> m state

Final result, produced by the stepper function.

Stream results like a fold with the option to stop at any time.

data Step a Source #

A step in the streaming process for the stream function.

Constructors

Stop !a

Stop with this value.

Continue !a

Continue with this value.

Instances

Show a => Show (Step a) Source # 

Methods

showsPrec :: Int -> Step a -> ShowS #

show :: Step a -> String #

showList :: [Step a] -> ShowS #

Exceptions

Proper connection handling should guarantee that a close happens at the right time. Here is a better way to write it:

{-# LANGUAGE OverloadedStrings #-}
import Control.Exception
import Database.ODBC
main :: IO ()
main =
  bracket
    (connect
       "DRIVER={ODBC Driver 13 for SQL Server};SERVER=192.168.99.100;Uid=SA;Pwd=Passw0rd")
    close
    (\conn -> do
       rows <- query conn "SELECT N'Hello, World!'"
       print rows)

If an exception occurs inside the lambda, bracket ensures that close is called.

data ODBCException Source #

A database exception. Any of the functions in this library may throw this exception type.

Constructors

UnsuccessfulReturnCode !String !Int16

An ODBC operation failed with the given return code.

AllocationReturnedNull !String

Allocating an ODBC resource failed.

UnknownDataType !String !Int16

An unsupported/unknown data type was returned from the ODBC driver.

DatabaseIsClosed !String

You tried to use the database connection after it was closed.

DatabaseAlreadyClosed

You attempted to close the database twice.

NoTotalInformation !Int

No total length information for column.