esqueleto-streaming-0.1.0.0: src/Database/Esqueleto/PostgreSQL/Streaming.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Database.Esqueleto.PostgreSQL.Streaming
( selectCursor
) where
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (ReaderT(..), ask)
import Control.Monad.Trans.Resource (MonadResource)
import Data.Conduit (ConduitT)
import Data.Conduit.Lift (runReaderC)
import Database.Esqueleto.Internal.Internal
import Database.Persist.Postgresql
import Database.Persist.Postgresql.Streaming.Internal (rawSelectStream)
-- | Execute an @esqueleto@ @SELECT@ query against a PostgreSQL backend and
-- return a stream of the results in constant memory, using the PostgreSQL
-- cursor feature.
--
-- NB: this function is likely to perform worse than 'select' on small
-- result sets.
--
-- NB: for large result sets, this function is likely to perform very poorly
-- unless you manually configure the @cursor_tuple_fraction@ setting to be
-- close to @1@. This setting decides how much PostgreSQL will prioritise
-- returning the first rows quickly vs. returning all rows efficiently. See
--
-- https://postgresqlco.nf/doc/en/param/cursor_tuple_fraction/
--
-- for more.
selectCursor
::
( SqlSelect a r
, MonadIO m
, MonadResource m
, BackendCompatible (RawPostgresql SqlBackend) backend
)
=> SqlQuery a
-> ConduitT () r (ReaderT backend m) ()
selectCursor query = do
backend@(RawPostgresql conn _) <- lift $ projectBackend <$> ask
let (queryTextBuilder, vals) = toRawSql SELECT (conn, initialIdentState) query
queryText = builderToText queryTextBuilder
runReaderC backend
(rawSelectStream sqlSelectProcessRow queryText vals)