    {-# LANGUAGE ExplicitForAll #-}
    module Database.Persist.Class.PersistQuery
        ( selectList
        , PersistQueryRead (..)
        , PersistQueryWrite (..)
        , selectSource
        , selectKeys
        , selectKeysList
        ) where

    import Control.Monad.IO.Class (MonadIO, liftIO)
    import Control.Monad.Reader   (ReaderT, MonadReader)
    import Control.Monad.Trans.Resource (MonadResource, release)
    import Data.Acquire (Acquire, allocateAcquire, with)
    import Data.Conduit (ConduitM, (.|), await, runConduit)
    import qualified Data.Conduit.List as CL

    import Database.Persist.Class.PersistStore
    import Database.Persist.Class.PersistEntity

    -- | Backends supporting conditional read operations.
    class (PersistCore backend, PersistStoreRead backend) => PersistQueryRead backend where
        -- | Get all records matching the given criterion in the specified order.
        -- Returns also the identifiers.
        --
        -- NOTE: This function returns an 'Acquire' and a 'ConduitM', which implies
        -- that it streams from the database. It does not. Please use 'selectList'
        -- to simplify the code. If you want streaming behavior, consider
        -- @persistent-pagination@ which efficiently chunks a query into ranges, or
        -- investigate a backend-specific streaming solution.
        selectSourceRes
               :: (PersistRecordBackend record backend, MonadIO m1, MonadIO m2)
               => [Filter record]
               -> [SelectOpt record]
               -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ()))

        -- | Get just the first record for the criterion.
        selectFirst :: (MonadIO m, PersistRecordBackend record backend)
                    => [Filter record]
                    -> [SelectOpt record]
                    -> ReaderT backend m (Maybe (Entity record))
        selectFirst [Filter record]
    filts [SelectOpt record]
    opts = do
            srcRes <- [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m (Acquire (ConduitM () (Entity record) IO ()))
    forall backend record (m1 :: * -> *) (m2 :: * -> *).
    (PersistQueryRead backend, PersistRecordBackend record backend,
     MonadIO m1, MonadIO m2) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ()))
    forall record (m1 :: * -> *) (m2 :: * -> *).
    (PersistRecordBackend record backend, MonadIO m1, MonadIO m2) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ()))
    selectSourceRes [Filter record]
    filts (Int -> SelectOpt record
    forall record. Int -> SelectOpt record
    LimitTo Int
    1 SelectOpt record -> [SelectOpt record] -> [SelectOpt record]
    forall a. a -> [a] -> [a]
    : [SelectOpt record]
    opts)
            liftIO $ with srcRes (\ConduitM () (Entity record) IO ()
    src -> ConduitT () Void IO (Maybe (Entity record))
    -> IO (Maybe (Entity record))
    forall (m :: * -> *) r. Monad m => ConduitT () Void m r -> m r
    runConduit (ConduitT () Void IO (Maybe (Entity record))
     -> IO (Maybe (Entity record)))
    -> ConduitT () Void IO (Maybe (Entity record))
    -> IO (Maybe (Entity record))
    forall a b. (a -> b) -> a -> b
    $ ConduitM () (Entity record) IO ()
    src ConduitM () (Entity record) IO ()
    -> ConduitT (Entity record) Void IO (Maybe (Entity record))
    -> ConduitT () Void IO (Maybe (Entity record))
    forall (m :: * -> *) a b c r.
    Monad m =>
    ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
    .| ConduitT (Entity record) Void IO (Maybe (Entity record))
    forall (m :: * -> *) i o. Monad m => ConduitT i o m (Maybe i)
    await)

        -- | Get the 'Key's of all records matching the given criterion.
        selectKeysRes
            :: (MonadIO m1, MonadIO m2, PersistRecordBackend record backend)
            => [Filter record]
            -> [SelectOpt record]
            -> ReaderT backend m1 (Acquire (ConduitM () (Key record) m2 ()))

        -- | The total number of records fulfilling the given criterion.
        count :: (MonadIO m, PersistRecordBackend record backend)
              => [Filter record] -> ReaderT backend m Int

        -- | Check if there is at least one record fulfilling the given criterion.
        --
        -- @since 2.11
        exists :: (MonadIO m, PersistRecordBackend record backend)
               => [Filter record] -> ReaderT backend m Bool

    -- | Backends supporting conditional write operations
    class (PersistQueryRead backend, PersistStoreWrite backend) => PersistQueryWrite backend where
        -- | Update individual fields on any record matching the given criterion.
        updateWhere :: (MonadIO m, PersistRecordBackend record backend)
                    => [Filter record] -> [Update record] -> ReaderT backend m ()

        -- | Delete all records matching the given criterion.
        deleteWhere :: (MonadIO m, PersistRecordBackend record backend)
                    => [Filter record] -> ReaderT backend m ()

    -- | Get all records matching the given criterion in the specified order.
    -- Returns also the identifiers.
    --
    -- WARNING: This function returns a 'ConduitM', which suggests that it streams
    -- the results. It does not stream results on most backends. If you need
    -- streaming, see @persistent-pagination@ for a means of chunking results based
    -- on indexed ranges.
    selectSource
           :: forall record backend m. (PersistQueryRead backend, MonadResource m, PersistRecordBackend record backend, MonadReader backend m)
           => [Filter record]
           -> [SelectOpt record]
           -> ConduitM () (Entity record) m ()
    selectSource :: forall record backend (m :: * -> *).
    (PersistQueryRead backend, MonadResource m,
     PersistRecordBackend record backend, MonadReader backend m) =>
    [Filter record]
    -> [SelectOpt record] -> ConduitM () (Entity record) m ()
    selectSource [Filter record]
    filts [SelectOpt record]
    opts = do
        srcRes <- ReaderT backend IO (Acquire (ConduitM () (Entity record) m ()))
    -> ConduitT
         () (Entity record) m (Acquire (ConduitM () (Entity record) m ()))
    forall (m :: * -> *) backend b.
    (MonadIO m, MonadReader backend m) =>
    ReaderT backend IO b -> m b
    liftPersist (ReaderT backend IO (Acquire (ConduitM () (Entity record) m ()))
     -> ConduitT
          () (Entity record) m (Acquire (ConduitM () (Entity record) m ())))
    -> ReaderT backend IO (Acquire (ConduitM () (Entity record) m ()))
    -> ConduitT
         () (Entity record) m (Acquire (ConduitM () (Entity record) m ()))
    forall a b. (a -> b) -> a -> b
    $ [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend IO (Acquire (ConduitM () (Entity record) m ()))
    forall backend record (m1 :: * -> *) (m2 :: * -> *).
    (PersistQueryRead backend, PersistRecordBackend record backend,
     MonadIO m1, MonadIO m2) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ()))
    forall record (m1 :: * -> *) (m2 :: * -> *).
    (PersistRecordBackend record backend, MonadIO m1, MonadIO m2) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ()))
    selectSourceRes [Filter record]
    filts [SelectOpt record]
    opts
        (releaseKey, src) <- allocateAcquire srcRes
        src
        release releaseKey

    -- | Get the 'Key's of all records matching the given criterion.
    --
    -- For an example, see 'selectList'.
    selectKeys :: forall record backend m. (PersistQueryRead backend, MonadResource m, PersistRecordBackend record backend, MonadReader backend m)
               => [Filter record]
               -> [SelectOpt record]
               -> ConduitM () (Key record) m ()
    selectKeys :: forall record backend (m :: * -> *).
    (PersistQueryRead backend, MonadResource m,
     PersistRecordBackend record backend, MonadReader backend m) =>
    [Filter record]
    -> [SelectOpt record] -> ConduitM () (Key record) m ()
    selectKeys [Filter record]
    filts [SelectOpt record]
    opts = do
        srcRes <- ReaderT backend IO (Acquire (ConduitM () (Key record) m ()))
    -> ConduitT
         () (Key record) m (Acquire (ConduitM () (Key record) m ()))
    forall (m :: * -> *) backend b.
    (MonadIO m, MonadReader backend m) =>
    ReaderT backend IO b -> m b
    liftPersist (ReaderT backend IO (Acquire (ConduitM () (Key record) m ()))
     -> ConduitT
          () (Key record) m (Acquire (ConduitM () (Key record) m ())))
    -> ReaderT backend IO (Acquire (ConduitM () (Key record) m ()))
    -> ConduitT
         () (Key record) m (Acquire (ConduitM () (Key record) m ()))
    forall a b. (a -> b) -> a -> b
    $ [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend IO (Acquire (ConduitM () (Key record) m ()))
    forall backend (m1 :: * -> *) (m2 :: * -> *) record.
    (PersistQueryRead backend, MonadIO m1, MonadIO m2,
     PersistRecordBackend record backend) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Key record) m2 ()))
    forall (m1 :: * -> *) (m2 :: * -> *) record.
    (MonadIO m1, MonadIO m2, PersistRecordBackend record backend) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Key record) m2 ()))
    selectKeysRes [Filter record]
    filts [SelectOpt record]
    opts
        (releaseKey, src) <- allocateAcquire srcRes
        src
        release releaseKey

    -- | Returns a @['Entity' record]@ corresponding to the filters and options
    -- provided.
    --
    -- Filters are constructed using the operators defined in "Database.Persist"
    -- (and re-exported from "Database.Persist.Sql"). Let's look at some examples:
    --
    -- @
    -- usersWithAgeOver40 :: 'SqlPersistT' 'IO' ['Entity' User]
    -- usersWithAgeOver40 =
    --     'selectList' [UserAge 'Database.Persist.>=.' 40] []
    -- @
    --
    -- If you provide multiple values in the list, the conditions are @AND@ed
    -- together.
    --
    -- @
    -- usersWithAgeBetween30And50 :: 'SqlPersistT' 'IO' ['Entity' User]
    -- usersWithAgeBetween30And50 =
    --      'selectList'
    --          [ UserAge 'Database.Persist.>=.' 30
    --          , UserAge 'Database.Persist.<=.' 50
    --          ]
    --          []
    -- @
    --
    -- The second list contains the 'SelectOpt' for a record.  We can select the
    -- first ten records with 'LimitTo'
    --
    -- @
    -- firstTenUsers =
    --     'selectList' [] ['LimitTo' 10]
    -- @
    --
    -- And we can select the second ten users with 'OffsetBy'.
    --
    -- @
    -- secondTenUsers =
    --     'selectList' [] ['LimitTo' 10, 'OffsetBy' 10]
    -- @
    --
    -- <https://use-the-index-luke.com/sql/partial-results/fetch-next-page Warning that LIMIT/OFFSET is bad for pagination!>
    --
    -- The type of record can usually be infered from the types of the provided filters
    -- and select options. In the previous two examples, though, you'll notice that the
    -- select options are polymorphic, applying to any record type. In order to help
    -- type inference in such situations, or simply as an enhancement to readability,
    -- you might find type application useful, illustrated below.
    --
    -- @
    -- {-# LANGUAGE TypeApplications #-}
    -- ...
    --
    -- firstTenUsers =
    --     'selectList' @User [] ['LimitTo' 10]
    --
    -- secondTenUsers =
    --     'selectList' @User [] ['LimitTo' 10, 'OffsetBy' 10]
    -- @
    --
    -- With 'Asc' and 'Desc', we can provide the field we want to sort on. We can
    -- provide multiple sort orders - later ones are used to sort records that are
    -- equal on the first field.
    --
    -- @
    -- newestUsers =
    --     selectList [] ['Desc' UserCreatedAt, 'LimitTo' 10]
    --
    -- oldestUsers =
    --     selectList [] ['Asc' UserCreatedAt, 'LimitTo' 10]
    -- @
    selectList
        :: forall record backend m. (MonadIO m, PersistQueryRead backend, PersistRecordBackend record backend)
        => [Filter record]
        -> [SelectOpt record]
        -> ReaderT backend m [Entity record]
    selectList :: forall record backend (m :: * -> *).
    (MonadIO m, PersistQueryRead backend,
     PersistRecordBackend record backend) =>
    [Filter record]
    -> [SelectOpt record] -> ReaderT backend m [Entity record]
    selectList [Filter record]
    filts [SelectOpt record]
    opts = do
        srcRes <- [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m (Acquire (ConduitM () (Entity record) IO ()))
    forall backend record (m1 :: * -> *) (m2 :: * -> *).
    (PersistQueryRead backend, PersistRecordBackend record backend,
     MonadIO m1, MonadIO m2) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ()))
    forall record (m1 :: * -> *) (m2 :: * -> *).
    (PersistRecordBackend record backend, MonadIO m1, MonadIO m2) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ()))
    selectSourceRes [Filter record]
    filts [SelectOpt record]
    opts
        liftIO $ with srcRes (\ConduitM () (Entity record) IO ()
    src -> ConduitT () Void IO [Entity record] -> IO [Entity record]
    forall (m :: * -> *) r. Monad m => ConduitT () Void m r -> m r
    runConduit (ConduitT () Void IO [Entity record] -> IO [Entity record])
    -> ConduitT () Void IO [Entity record] -> IO [Entity record]
    forall a b. (a -> b) -> a -> b
    $ ConduitM () (Entity record) IO ()
    src ConduitM () (Entity record) IO ()
    -> ConduitT (Entity record) Void IO [Entity record]
    -> ConduitT () Void IO [Entity record]
    forall (m :: * -> *) a b c r.
    Monad m =>
    ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
    .| ConduitT (Entity record) Void IO [Entity record]
    forall (m :: * -> *) a o. Monad m => ConduitT a o m [a]
    CL.consume)

    -- | Call 'selectKeys' but return the result as a list.
    selectKeysList :: forall record backend m. (MonadIO m, PersistQueryRead backend, PersistRecordBackend record backend)
                   => [Filter record]
                   -> [SelectOpt record]
                   -> ReaderT backend m [Key record]
    selectKeysList :: forall record backend (m :: * -> *).
    (MonadIO m, PersistQueryRead backend,
     PersistRecordBackend record backend) =>
    [Filter record]
    -> [SelectOpt record] -> ReaderT backend m [Key record]
    selectKeysList [Filter record]
    filts [SelectOpt record]
    opts = do
        srcRes <- [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m (Acquire (ConduitM () (Key record) IO ()))
    forall backend (m1 :: * -> *) (m2 :: * -> *) record.
    (PersistQueryRead backend, MonadIO m1, MonadIO m2,
     PersistRecordBackend record backend) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Key record) m2 ()))
    forall (m1 :: * -> *) (m2 :: * -> *) record.
    (MonadIO m1, MonadIO m2, PersistRecordBackend record backend) =>
    [Filter record]
    -> [SelectOpt record]
    -> ReaderT backend m1 (Acquire (ConduitM () (Key record) m2 ()))
    selectKeysRes [Filter record]
    filts [SelectOpt record]
    opts
        liftIO $ with srcRes (\ConduitM () (Key record) IO ()
    src -> ConduitT () Void IO [Key record] -> IO [Key record]
    forall (m :: * -> *) r. Monad m => ConduitT () Void m r -> m r
    runConduit (ConduitT () Void IO [Key record] -> IO [Key record])
    -> ConduitT () Void IO [Key record] -> IO [Key record]
    forall a b. (a -> b) -> a -> b
    $ ConduitM () (Key record) IO ()
    src ConduitM () (Key record) IO ()
    -> ConduitT (Key record) Void IO [Key record]
    -> ConduitT () Void IO [Key record]
    forall (m :: * -> *) a b c r.
    Monad m =>
    ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
    .| ConduitT (Key record) Void IO [Key record]
    forall (m :: * -> *) a o. Monad m => ConduitT a o m [a]
    CL.consume)