{-# LANGUAGE OverloadedStrings #-}
module Database.PostgreSQL.Simple.Cursor
(
Cursor
, declareCursor
, closeCursor
, foldForward
, foldForwardWithParser
) where
import Data.ByteString.Builder (intDec)
import Control.Applicative ((<$>))
import Control.Exception as E
import Control.Monad (unless, void)
import Data.Monoid (mconcat)
import Database.PostgreSQL.Simple.Compat ((<>), toByteString)
import Database.PostgreSQL.Simple.FromRow (FromRow(..))
import Database.PostgreSQL.Simple.Types (Query(..))
import Database.PostgreSQL.Simple.Internal as Base hiding (result, row)
import Database.PostgreSQL.Simple.Internal.PQResultUtils
import Database.PostgreSQL.Simple.Transaction
import qualified Database.PostgreSQL.LibPQ as PQ
data Cursor = Cursor !Query !Connection
declareCursor :: Connection -> Query -> IO Cursor
declareCursor conn q = do
name <- newTempName conn
void $ execute_ conn $ mconcat ["DECLARE ", name, " NO SCROLL CURSOR FOR ", q]
return $ Cursor name conn
closeCursor :: Cursor -> IO ()
closeCursor (Cursor name conn) =
(void $ execute_ conn ("CLOSE " <> name)) `E.catch` \ex ->
unless (isFailedTransactionError ex) $ throwIO ex
foldForwardWithParser :: Cursor -> RowParser r -> Int -> (a -> r -> IO a) -> a -> IO (Either a a)
foldForwardWithParser (Cursor name conn) parser chunkSize f a0 = do
let q = "FETCH FORWARD "
<> (toByteString $ intDec chunkSize)
<> " FROM "
<> fromQuery name
result <- exec conn q
status <- PQ.resultStatus result
case status of
PQ.TuplesOk -> do
nrows <- PQ.ntuples result
ncols <- PQ.nfields result
if nrows > 0
then do
let inner a row = do
x <- getRowWith parser row ncols conn result
f a x
Right <$> foldM' inner a0 0 (nrows - 1)
else
return $ Left a0
_ -> throwResultError "foldForwardWithParser" result status
foldForward :: FromRow r => Cursor -> Int -> (a -> r -> IO a) -> a -> IO (Either a a)
foldForward cursor = foldForwardWithParser cursor fromRow
foldM' :: (Ord n, Num n) => (a -> n -> IO a) -> a -> n -> n -> IO a
foldM' f a lo hi = loop a lo
where
loop x !n
| n > hi = return x
| otherwise = do
x' <- f x n
loop x' (n+1)
{-# INLINE foldM' #-}