module Database.Relational.Query.Monad.Trans.Join (
QueryJoin, join',
extractProduct
) where
import Prelude hiding (product)
import Control.Monad.Trans.Class (MonadTrans (lift))
import Control.Monad.Trans.Writer (WriterT, runWriterT, tell)
import Control.Monad.Trans.State (modify, StateT, runStateT)
import Control.Applicative (Applicative, (<$>))
import Control.Arrow (second, (***))
import Data.Maybe (fromMaybe)
import Data.Monoid (Last (Last, getLast))
import Database.Relational.Query.Context (Flat)
import Database.Relational.Query.Monad.Trans.JoinState
(JoinContext, primeJoinContext, updateProduct, joinProduct)
import Database.Relational.Query.Internal.Product (NodeAttr, restrictProduct, growProduct)
import Database.Relational.Query.Projection (Projection)
import qualified Database.Relational.Query.Projection as Projection
import Database.Relational.Query.Expr (Expr, fromJust)
import Database.Relational.Query.Component (Duplication (All))
import Database.Relational.Query.Sub (SubQuery, Qualified, JoinProduct)
import Database.Relational.Query.Projectable (expr)
import Database.Relational.Query.Monad.Class (MonadQuery (..))
newtype QueryJoin m a =
QueryJoin (StateT JoinContext (WriterT (Last Duplication) m) a)
deriving (Monad, Functor, Applicative)
join' :: Monad m => m a -> QueryJoin m a
join' = QueryJoin . lift . lift
updateContext :: Monad m => (JoinContext -> JoinContext) -> QueryJoin m ()
updateContext = QueryJoin . modify
updateJoinRestriction :: Monad m => Expr Flat (Maybe Bool) -> QueryJoin m ()
updateJoinRestriction e = updateContext (updateProduct d) where
d Nothing = error "on: Product is empty! Restrict target product is not found!"
d (Just pt) = restrictProduct pt (fromJust e)
instance (Monad q, Functor q) => MonadQuery (QueryJoin q) where
setDuplication = QueryJoin . lift . tell . Last . Just
restrictJoin = updateJoinRestriction . expr
unsafeSubQuery = unsafeSubQueryWithAttr
unsafeSubQueryWithAttr :: Monad q
=> NodeAttr
-> Qualified SubQuery
-> QueryJoin q (Projection Flat r)
unsafeSubQueryWithAttr attr qsub = do
updateContext (updateProduct (`growProduct` (attr, qsub)))
return $ Projection.unsafeFromQualifiedSubQuery qsub
extractProduct :: Functor m => QueryJoin m a -> m ((a, JoinProduct), Duplication)
extractProduct (QueryJoin s) = (second joinProduct *** (fromMaybe All . getLast))
<$> runWriterT (runStateT s primeJoinContext)