packages feed

hdbi-conduit-1.2.0: Data/Conduit/HDBI.hs

{-# LANGUAGE
  BangPatterns
, TypeFamilies
  #-}

module Data.Conduit.HDBI
       (
         -- * Conduit functions
         selectAll
       , selectAllRows
       , selectRawAll
       , selectRawAllRows
       , insertAll
       , insertAllRows
       , insertAllCount
       , insertAllRowsCount
         -- * Auxiliary conduit functions
       , statementSource
       , statementSink
       , statementSinkCount
         -- * ResourceT functions
       , allocConnection
       , allocStmt
       , executeStmt
       , executeStmtRow
       , executeStmtRaw
       ) where

import Control.Monad.IO.Class
import Control.Monad.Trans.Resource
import Data.Conduit
import Database.HDBI

allocConnection :: (Connection con, MonadResource m) => IO con -> m (ReleaseKey, con)
allocConnection con = allocate con disconnect

allocStmt :: (Statement stmt, MonadResource m) => IO stmt -> m (ReleaseKey, stmt)
allocStmt stmt = allocate stmt finish

executeStmt :: (Connection con, (ConnStatement con) ~ stmt, MonadResource m) => con -> Query -> [SqlValue] -> m (ReleaseKey, stmt)
executeStmt con query vals = do
  (key, stmt) <- allocStmt $ prepare con query
  liftIO $ execute stmt vals
  return (key, stmt)

executeStmtRow :: (Connection con, (ConnStatement con) ~ stmt, ToRow row, MonadResource m) => con -> Query -> row -> m (ReleaseKey, stmt)
executeStmtRow con query row = do
  (key, stmt) <- allocStmt $ prepare con query
  liftIO $ executeRow stmt row
  return (key, stmt)

executeStmtRaw :: (Connection con, (ConnStatement con) ~ stmt, MonadResource m) => con -> Query -> m (ReleaseKey, stmt)
executeStmtRaw con query = do
  (key, stmt) <- allocStmt $ prepare con query
  liftIO $ executeRaw stmt
  return (key, stmt)

-- | fetch all results of query
selectAll :: (Connection con, MonadResource m)
             => con
             -> Query            -- query to execute
             -> [SqlValue]       -- query parameters
             -> Source m [SqlValue]
selectAll con query params = statementSource fetch $ do
  st <- prepare con query
  execute st params
  return st

-- | same as `selectAll` but reburn stream of `FromRow` instances
selectAllRows :: (Connection con, MonadResource m, FromRow a)
                 => con
                 -> Query
                 -> [SqlValue]
                 -> Source m a
selectAllRows con query params = statementSource fetchRow $ do
  st <- prepare con query
  execute st params
  return st

-- | same as `selectAll` but without query parameters
selectRawAll :: (Connection con, MonadResource m)
                => con
                -> Query
                -> Source m [SqlValue]
selectRawAll con query = statementSource fetch $ do
  st <- prepare con query
  executeRaw st
  return st

-- | same as `selectRawAll` but return stream of `FromRow` instances
selectRawAllRows :: (Connection con, MonadResource m, FromRow a) => con -> Query -> Source m a
selectRawAllRows con query = statementSource fetchRow $ do
  st <- prepare con query
  executeRaw st
  return st

-- | same as `insertAll` but also count executed rows
insertAllCount :: (Connection con, MonadResource m, Num count) => con -> Query -> Sink [SqlValue] m count
insertAllCount con query = statementSinkCount execute $ prepare con query

-- | same as `insertAllRows` but also count executed rows
insertAllRowsCount :: (Connection con, MonadResource m, Num count, ToRow a) => con -> Query -> Sink a m count
insertAllRowsCount con query = statementSinkCount executeRow $ prepare con query

-- | perform `execute` for each bunch of values
insertAll :: (Connection con, MonadResource m)
             => con
             -> Query
             -> Sink [SqlValue] m ()
insertAll con query = statementSink execute $ prepare con query

-- | perfor `executeRow` for each input row
insertAllRows :: (Connection con, MonadResource m, ToRow a)
                 => con
                 -> Query
                 -> Sink a m ()
insertAllRows con query = statementSink executeRow $ prepare con query


-- | Get all values from the statement until action return ''Just a''
statementSource :: (Statement stmt, MonadResource m)
                   => (stmt -> IO (Maybe a))  -- action to execute until it return
                                             -- Nothing
                   -> IO stmt               -- statement constructor
                   -> Source m a
statementSource getter stmt = bracketP
                              stmt
                              finish
                              statementSource'
  where
    statementSource' st = do
      row <- liftIO $ getter st
      case row of
        Nothing -> return ()
        Just r -> do
          yield r
          statementSource' st

-- | Execute action many times with given thread of values, return the count
-- of executions
statementSinkCount :: (Statement stmt, MonadResource m, Num count)
                      => (stmt -> a -> IO ()) -- action to execute each time
                      -> IO stmt            -- statement constructor
                      -> Sink a m count
statementSinkCount putter stmt = bracketP
                                 stmt
                                 finish
                                 $ statementSinkCount' 0
  where
    statementSinkCount' !ac st = do
      next <- await
      case next of
        Nothing -> return ac
        Just n -> do
          liftIO $ do
            reset st
            putter st n
          statementSinkCount' (ac+1) st

-- | Same as `statementSinkCount` but without counting, just return ()
statementSink :: (Statement stmt, MonadResource m)
                 => (stmt -> a -> IO ())
                 -> IO stmt
                 -> Sink a m ()
statementSink putter stmt = bracketP
                            stmt
                            finish
                            statementSink'
  where
    statementSink' st = do
      next <- await
      case next of
        Nothing -> return ()
        Just n -> do
          liftIO $ do
            reset st
            putter st n
          statementSink' st