module Database.Persist.Query.Internal
(
PersistQuery (..)
, selectList
, selectKeysList
, SelectOpt (..)
, limitOffsetOrder
, Filter (..)
, PersistUpdate (..)
, Update (..)
, updateFieldDef
, deleteCascadeWhere
, UpdateGetException (..)
, BackendSpecificFilter
) where
import Database.Persist.Store
import Database.Persist.EntityDef
import Control.Monad.IO.Class (liftIO)
import Control.Exception (Exception, throwIO)
import Data.Typeable (Typeable)
import qualified Data.Conduit as C
import qualified Data.Conduit.List as CL
import Control.Monad.Trans.Class (lift)
import Data.Monoid (Monoid)
import Data.Conduit (Pipe)
import Control.Monad.Logger (LoggingT)
import Control.Monad.Trans.Identity ( IdentityT)
import Control.Monad.Trans.List ( ListT )
import Control.Monad.Trans.Maybe ( MaybeT )
import Control.Monad.Trans.Error ( ErrorT, Error )
import Control.Monad.Trans.Reader ( ReaderT )
import Control.Monad.Trans.Cont ( ContT )
import Control.Monad.Trans.State ( StateT )
import Control.Monad.Trans.Writer ( WriterT )
import Control.Monad.Trans.RWS ( RWST )
import Control.Monad.Trans.Resource ( ResourceT)
import qualified Control.Monad.Trans.RWS.Strict as Strict ( RWST )
import qualified Control.Monad.Trans.State.Strict as Strict ( StateT )
import qualified Control.Monad.Trans.Writer.Strict as Strict ( WriterT )
data UpdateGetException = KeyNotFound String
deriving Typeable
instance Show UpdateGetException where
show (KeyNotFound key) = "Key not found during updateGet: " ++ key
instance Exception UpdateGetException
class PersistStore m => PersistQuery m where
update :: (PersistEntity val, PersistEntityBackend val ~ PersistMonadBackend m)
=> Key val -> [Update val] -> m ()
updateGet :: (PersistEntity val, PersistMonadBackend m ~ PersistEntityBackend val)
=> Key val -> [Update val] -> m val
updateGet key ups = do
update key ups
get key >>= maybe (liftIO $ throwIO $ KeyNotFound $ show key) return
updateWhere :: (PersistEntity val, PersistEntityBackend val ~ PersistMonadBackend m)
=> [Filter val] -> [Update val] -> m ()
deleteWhere :: (PersistEntity val, PersistEntityBackend val ~ PersistMonadBackend m)
=> [Filter val] -> m ()
selectSource
:: (PersistEntity val, PersistEntityBackend val ~ PersistMonadBackend m)
=> [Filter val]
-> [SelectOpt val]
-> C.Source m (Entity val)
selectFirst :: (PersistEntity val, PersistEntityBackend val ~ PersistMonadBackend m)
=> [Filter val]
-> [SelectOpt val]
-> m (Maybe (Entity val))
selectFirst filts opts = selectSource filts ((LimitTo 1):opts) C.$$ CL.head
selectKeys :: (PersistEntity val, PersistEntityBackend val ~ PersistMonadBackend m)
=> [Filter val]
-> [SelectOpt val]
-> C.Source m (Key val)
count :: (PersistEntity val, PersistEntityBackend val ~ PersistMonadBackend m)
=> [Filter val] -> m Int
#define DEF(T) { update k = lift . update k; updateGet k = lift . updateGet k; updateWhere f = lift . updateWhere f; deleteWhere = lift . deleteWhere; selectSource f = C.transPipe lift . selectSource f; selectFirst f = lift . selectFirst f; selectKeys f = C.transPipe lift . selectKeys f; count = lift . count }
#define GO(T) instance (PersistQuery m) => PersistQuery (T m) where DEF(T)
#define GOX(X, T) instance (X, PersistQuery m) => PersistQuery (T m) where DEF(T)
GO(LoggingT)
GO(IdentityT)
GO(ListT)
GO(MaybeT)
GOX(Error e, ErrorT e)
GO(ReaderT r)
GO(ContT r)
GO(StateT s)
GO(ResourceT)
GO(Pipe l i o u)
GOX(Monoid w, WriterT w)
GOX(Monoid w, RWST r w s)
GOX(Monoid w, Strict.RWST r w s)
GO(Strict.StateT s)
GOX(Monoid w, Strict.WriterT w)
#undef DEF
#undef GO
#undef GOX
type family BackendSpecificFilter b v
data Filter v = forall typ. PersistField typ => Filter
{ filterField :: EntityField v typ
, filterValue :: Either typ [typ]
, filterFilter :: PersistFilter
}
| FilterAnd [Filter v]
| FilterOr [Filter v]
| BackendFilter (BackendSpecificFilter (PersistEntityBackend v) v)
data SelectOpt v = forall typ. Asc (EntityField v typ)
| forall typ. Desc (EntityField v typ)
| OffsetBy Int
| LimitTo Int
selectList :: (PersistEntity val, PersistQuery m, PersistEntityBackend val ~ PersistMonadBackend m)
=> [Filter val]
-> [SelectOpt val]
-> m [Entity val]
selectList a b = selectSource a b C.$$ CL.consume
selectKeysList :: (PersistEntity val, PersistQuery m, PersistEntityBackend val ~ PersistMonadBackend m)
=> [Filter val]
-> [SelectOpt val]
-> m [Key val]
selectKeysList a b = selectKeys a b C.$$ CL.consume
data PersistUpdate = Assign | Add | Subtract | Multiply | Divide
deriving (Read, Show, Enum, Bounded)
data Update v = forall typ. PersistField typ => Update
{ updateField :: EntityField v typ
, updateValue :: typ
, updateUpdate :: PersistUpdate
}
limitOffsetOrder :: PersistEntity val => [SelectOpt val] -> (Int, Int, [SelectOpt val])
limitOffsetOrder opts =
foldr go (0, 0, []) opts
where
go (LimitTo l) (_, b, c) = (l, b ,c)
go (OffsetBy o) (a, _, c) = (a, o, c)
go x (a, b, c) = (a, b, x : c)
updateFieldDef :: PersistEntity v => Update v -> FieldDef
updateFieldDef (Update f _ _) = persistFieldDef f
deleteCascadeWhere :: (DeleteCascade a m, PersistQuery m)
=> [Filter a] -> m ()
deleteCascadeWhere filts = selectKeys filts [] C.$$ CL.mapM_ deleteCascade