Copyright | (c) 2004 Oleg Kiselyov, Alistair Bayley |
---|---|
License | BSD-style |
Maintainer | oleg@pobox.com, alistair@abayley.org |
Stability | experimental |
Portability | non-portable |
Safe Haskell | None |
Language | Haskell2010 |
- data ISession sess => DBM mark sess a
- class ISession sess
- data ConnectA sess
- withSession :: (Typeable a, ISession sess) => ConnectA sess -> (forall mark. DBM mark sess a) -> IO a
- withContinuedSession :: (Typeable a, ISession sess) => ConnectA sess -> (forall mark. DBM mark sess a) -> IO (a, ConnectA sess)
- commit :: ISession s => DBM mark s ()
- rollback :: ISession s => DBM mark s ()
- beginTransaction :: (MonadReader s (ReaderT s IO), ISession s) => IsolationLevel -> DBM mark s ()
- withTransaction :: ISession s => IsolationLevel -> DBM mark s a -> DBM mark s a
- data IsolationLevel
- execDDL :: Command stmt s => stmt -> DBM mark s ()
- execDML :: Command stmt s => stmt -> DBM mark s Int
- inquire :: EnvInquiry key s result => key -> DBM mark s result
- data DBException
- formatDBException :: DBException -> String
- basicDBExceptionReporter :: CaughtMonadIO m => DBException -> m ()
- reportRethrow :: CaughtMonadIO m => DBException -> m a
- reportRethrowMsg :: CaughtMonadIO m => String -> DBException -> m a
- catchDB :: CaughtMonadIO m => m a -> (DBException -> m a) -> m a
- catchDBError :: CaughtMonadIO m => Int -> m a -> (DBException -> m a) -> m a
- ignoreDBError :: CaughtMonadIO m => Int -> m a -> m a
- throwDB :: DBException -> a
- type ColNum = Int
- type RowNum = Int
- type SqlState = (SqlStateClass, SqlStateSubClass)
- type SqlStateClass = String
- type SqlStateSubClass = String
- data PreparedStmt mark stmt
- withPreparedStatement :: (Typeable a, IPrepared stmt sess bstmt bo) => PreparationA sess stmt -> (PreparedStmt mark stmt -> DBM mark sess a) -> DBM mark sess a
- withBoundStatement :: (Typeable a, IPrepared stmt s bstmt bo) => PreparedStmt mark stmt -> [BindA s stmt bo] -> (bstmt -> DBM mark s a) -> DBM mark s a
- class ISession sess => Statement stmt sess q | stmt sess -> q
- class ISession sess => Command stmt sess
- class ISession sess => EnvInquiry inquirykey sess result | inquirykey sess -> result
- data PreparationA sess stmt
- class ISession sess => IPrepared stmt sess bound_stmt bo | stmt -> bound_stmt, stmt -> bo
- data BindA sess stmt bo
- class ISession sess => DBBind a sess stmt bo | stmt -> bo where
- bindP :: DBBind a sess stmt bo => a -> BindA sess stmt bo
- class ISession sess => IQuery q sess b | q -> sess, q -> b where
- doQuery :: (Statement stmt sess q, QueryIteratee (DBM mark sess) q i seed b, IQuery q sess b) => stmt -> i -> seed -> DBM mark sess seed
- class DBType a q b | q -> b
- type IterResult seedType = Either seedType seedType
- type IterAct m seedType = seedType -> m (IterResult seedType)
- currentRowNum :: IQuery q sess b => q -> IO Int
- data NextResultSet mark stmt = NextResultSet (PreparedStmt mark stmt)
- data RefCursor a = RefCursor a
- cursorIsEOF :: DBCursor mark (DBM mark s) a -> DBM mark s Bool
- cursorCurrent :: DBCursor mark (DBM mark s) a -> DBM mark s a
- cursorNext :: DBCursor mark (DBM mark s) a -> DBM mark s (DBCursor mark (DBM mark s) a)
- withCursor :: (Typeable a, Statement stmt sess q, QueryIteratee (DBM mark sess) q i seed b, IQuery q sess b) => stmt -> i -> seed -> (DBCursor mark (DBM mark sess) seed -> DBM mark sess a) -> DBM mark sess a
- type Position = Int
- ifNull :: Maybe a -> a -> a
- result :: Monad m => IterAct m a
- result' :: Monad m => IterAct m a
Usage
Iteratee Functions
result and result'
Rank-2 types, ($), and the monomorphism restriction
Bind Parameters
Multiple (and nested) Result Sets
Sessions and Transactions
The ISession
class describes a database session to a particular
DBMS. Oracle has its own Session object, SQLite has its own
session object (which maintains the connection handle to the database
engine and other related stuff). Session objects for different databases
normally have different types -- yet they all belong to the class ISession
so we can do generic operations like commit
, execDDL
, etc.
in a database-independent manner.
disconnect, beginTransaction, commit, rollback
A wrapper around the action to open the database. That wrapper is not
exported to the end user. The only reason for the wrapper is to
guarantee that the only thing to do with the result of
connect
function is to pass it out
directly to withSession
.
withSession :: (Typeable a, ISession sess) => ConnectA sess -> (forall mark. DBM mark sess a) -> IO a Source #
Typeable constraint is to prevent the leakage of Session and other marked objects.
withContinuedSession :: (Typeable a, ISession sess) => ConnectA sess -> (forall mark. DBM mark sess a) -> IO (a, ConnectA sess) Source #
Persistent database connections. This issue has been brought up by Shanky Surana. The following design is inspired by that exchange.
beginTransaction :: (MonadReader s (ReaderT s IO), ISession s) => IsolationLevel -> DBM mark s () Source #
withTransaction :: ISession s => IsolationLevel -> DBM mark s a -> DBM mark s a Source #
Perform an action as a transaction: commit afterwards, unless there was an exception, in which case rollback.
data IsolationLevel Source #
ReadUncommitted | |
ReadCommitted | |
RepeatableRead | |
Serialisable | |
Serializable | for alternative spellers |
execDDL :: Command stmt s => stmt -> DBM mark s () Source #
DDL operations don't manipulate data, so we return no information. If there is a problem, an exception will be raised.
inquire :: EnvInquiry key s result => key -> DBM mark s result Source #
Allows arbitrary actions to be run the DBM monad.
The back-end developer must supply instances of EnvInquiry,
which is hidden away in Database.InternalEnumerator.
An example of this is LastInsertRowid
.
Exceptions and handlers
data DBException Source #
DBError SqlState Int String | DBMS error message. |
DBFatal SqlState Int String | |
DBUnexpectedNull RowNum ColNum | the iteratee function used for queries accepts both nullable (Maybe) and non-nullable types. If the query itself returns a null in a column where a non-nullable type was specified, we can't handle it, so DBUnexpectedNull is thrown. |
DBNoData | Thrown by cursor functions if you try to fetch after the end. |
formatDBException :: DBException -> String Source #
A show for DBException
s.
basicDBExceptionReporter :: CaughtMonadIO m => DBException -> m () Source #
This simple handler reports the error to stdout
and swallows it
i.e. it doesn't propagate.
reportRethrow :: CaughtMonadIO m => DBException -> m a Source #
This handler reports the error and propagates it (usually to force the program to halt).
reportRethrowMsg :: CaughtMonadIO m => String -> DBException -> m a Source #
Same as reportRethrow, but you can prefix some text to the error (perhaps to indicate which part of your program raised it).
catchDB :: CaughtMonadIO m => m a -> (DBException -> m a) -> m a Source #
Catch DBException
s thrown in the DBM
monad.
catchDBError :: CaughtMonadIO m => Int -> m a -> (DBException -> m a) -> m a Source #
If you want to trap a specific error number, use this. It passes anything else up.
ignoreDBError :: CaughtMonadIO m => Int -> m a -> m a Source #
Analogous to catchDBError
, but ignores specific errors instead
(propagates anything else).
throwDB :: DBException -> a Source #
Throw a DBException. It's just a type-specific throwDyn
.
type SqlState = (SqlStateClass, SqlStateSubClass) Source #
type SqlStateClass = String Source #
type SqlStateSubClass = String Source #
Preparing and Binding
data PreparedStmt mark stmt Source #
withPreparedStatement Source #
:: (Typeable a, IPrepared stmt sess bstmt bo) | |
=> PreparationA sess stmt | preparation action to create prepared statement;
this action is usually created by |
-> (PreparedStmt mark stmt -> DBM mark sess a) | DBM action that takes a prepared statement |
-> DBM mark sess a |
Prepare a statement and run a DBM action over it. This gives us the ability to re-use a statement, for example by passing different bind values for each execution.
:: (Typeable a, IPrepared stmt s bstmt bo) | |
=> PreparedStmt mark stmt | prepared statement created by withPreparedStatement |
-> [BindA s stmt bo] | bind values |
-> (bstmt -> DBM mark s a) | action to run over bound statement |
-> DBM mark s a |
Applies a prepared statement to bind variables to get a bound statement,
which is passed to the provided action.
Note that by the time it is passed to the action, the query or command
has usually been executed.
A bound statement would normally be an instance of
Statement
, so it can be passed to
doQuery
in order to process the result-set, and also an instance of
Command
, so that we can write
re-usable DML statements (inserts, updates, deletes).
class ISession sess => Statement stmt sess q | stmt sess -> q Source #
Statement
defines the API for query objects i.e.
which types can be queries.
makeQuery
class ISession sess => Command stmt sess Source #
Command
is not a query: command deletes or updates rows, creates/drops
tables, or changes database state.
executeCommand
returns the number of affected rows (or 0 if DDL i.e. not DML).
executeCommand
class ISession sess => EnvInquiry inquirykey sess result | inquirykey sess -> result Source #
inquire
data PreparationA sess stmt Source #
This type is not visible to the end user (cf. ConnectA). It forms a private `communication channel' between Database.Enumerator and a back end.
class ISession sess => IPrepared stmt sess bound_stmt bo | stmt -> bound_stmt, stmt -> bo Source #
bindRun, destroyStmt
data BindA sess stmt bo Source #
The binding object (bo) below is very abstract, on purpose. It may be |IO a|, it may be String, it may be a function, etc. The binding object can hold the result of marshalling, or bo can hold the current counter, etc. Different databases do things very differently: compare PostgreSQL and the Stub (which models Oracle).
class ISession sess => DBBind a sess stmt bo | stmt -> bo where Source #
The class DBBind is not used by the end-user. It is used to tie up low-level database access and the enumerator. A database-specific library must provide a set of instances for DBBind. The latter are the dual of DBType.
bindP :: DBBind a sess stmt bo => a -> BindA sess stmt bo Source #
This is really just a wrapper that lets us write lists of
heterogenous bind values e.g. [bindP "string", bindP (0::Int), ...]
Iteratees and Cursors
class ISession sess => IQuery q sess b | q -> sess, q -> b where Source #
The class IQuery describes the class of query objects. Each database (that is, each Session object) has its own Query object. We may assume that a Query object includes (at least, conceptually) a (pointer to) a Session object, so a Query object determines the Session object. A back-end provides an instance (or instances) of IQuery. The end user never seens the IQuery class (let alone its methods).
fetchOneRow, currentRowNum, freeBuffer, destroyQuery
currentRowNum :: q -> IO Int Source #
:: (Statement stmt sess q, QueryIteratee (DBM mark sess) q i seed b, IQuery q sess b) | |
=> stmt | query |
-> i | iteratee function |
-> seed | seed value |
-> DBM mark sess seed |
The left-fold interface.
class DBType a q b | q -> b Source #
A 'buffer' means a column buffer: a data structure that points to a block of memory allocated for the values of one particular column. Since a query normally fetches a row of several columns, we typically deal with a list of column buffers. Although the column data are typed (e.g., Integer, CalendarDate, etc), column buffers hide that type. Think of the column buffer as Dynamics. The class DBType below describes marshalling functions, to fetch a typed value out of the 'untyped' columnBuffer.
The class DBType is not used by the end-user. It is used to tie up low-level database access and the enumerator. A database-specific library must provide a set of instances for DBType.
allocBufferFor, fetchCol
type IterResult seedType = Either seedType seedType Source #
IterResult
and IterAct
give us some type sugar.
Without them, the types of iteratee functions become
quite unwieldy.
type IterAct m seedType = seedType -> m (IterResult seedType) Source #
data NextResultSet mark stmt Source #
NextResultSet (PreparedStmt mark stmt) |
cursorIsEOF :: DBCursor mark (DBM mark s) a -> DBM mark s Bool Source #
cursorIsEOF's return value tells you if there are any more rows or not.
If you call cursorNext
when there are no more rows,
a DBNoData
exception is thrown.
Cursors are automatically closed and freed when:
cursorCurrent :: DBCursor mark (DBM mark s) a -> DBM mark s a Source #
Returns the results fetched so far, processed by iteratee function.
cursorNext :: DBCursor mark (DBM mark s) a -> DBM mark s (DBCursor mark (DBM mark s) a) Source #
Advance the cursor. Returns the cursor. The return value is usually ignored.
:: (Typeable a, Statement stmt sess q, QueryIteratee (DBM mark sess) q i seed b, IQuery q sess b) | |
=> stmt | query |
-> i | iteratee function |
-> seed | seed value |
-> (DBCursor mark (DBM mark sess) seed -> DBM mark sess a) | action taking cursor parameter |
-> DBM mark sess a |
Ensures cursor resource is properly tidied up in exceptional cases. Propagates exceptions after closing cursor. The Typeable constraint is to prevent cursors and other marked values (like cursor computations) from escaping.
Utilities
Useful utility function, for SQL weenies.
result :: Monad m => IterAct m a Source #
Another useful utility function.
Use this to return a value from an iteratee function (the one passed to
doQuery
).
Note that you should probably nearly always use the strict version.
result' :: Monad m => IterAct m a Source #
A strict version. This is recommended unless you have a specific need for laziness,
as the lazy version will gobble stack and heap.
If you have a large result-set (in the order of 10-100K rows or more),
it is likely to exhaust the standard 1M GHC stack.
Whether or not result
eats memory depends on what x
does:
if it's a delayed computation then it almost certainly will.
This includes consing elements onto a list,
and arithmetic operations (counting, summing, etc).