Connect qualify monad for nested query monad case.

This commit is contained in:
Kei Hibino 2013-05-30 16:08:37 +09:00
parent 2f60660345
commit 61eb273c28
8 changed files with 168 additions and 92 deletions

View File

@ -52,6 +52,7 @@ library
Database.Relational.Query.Internal.Product Database.Relational.Query.Internal.Product
Database.Relational.Query.Internal.Context Database.Relational.Query.Internal.Context
Database.Relational.Query.Internal.AggregatingContext Database.Relational.Query.Internal.AggregatingContext
Database.Relational.Query.Monad.Qualify
Database.Relational.Query.Monad.Unsafe Database.Relational.Query.Monad.Unsafe
Database.Relational.Schema.DB2Syscat.Tabconst Database.Relational.Schema.DB2Syscat.Tabconst

View File

@ -1,6 +1,10 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Database.Relational.Query.Internal.Context ( module Database.Relational.Query.Internal.Context (
AliasIdContext,
primeAliasIdContext,
Context, Context,
primeContext, primeContext,
@ -43,16 +47,20 @@ import qualified Language.SQL.Keyword as SQL
-- Base contexts -- Base contexts
newtype AliasIdContext = AliasIdContext { currentAliasId :: AliasId }
primeAliasIdContext :: AliasIdContext
primeAliasIdContext = AliasIdContext primAlias
data Context = Context data Context = Context
{ currentAliasId :: AliasId { product :: Maybe QueryProductNode
, product :: Maybe QueryProductNode
, restriction :: Maybe (Expr Bool) , restriction :: Maybe (Expr Bool)
} }
primeContext :: Context primeContext :: Context
primeContext = Context primAlias Nothing Nothing primeContext = Context Nothing Nothing
nextAlias :: Context -> (AliasId, Context) nextAlias :: AliasIdContext -> (AliasId, AliasIdContext)
nextAlias s = (cur, s { currentAliasId = newAliasId cur }) where nextAlias s = (cur, s { currentAliasId = newAliasId cur }) where
cur = currentAliasId s cur = currentAliasId s

View File

@ -28,6 +28,7 @@ import Database.Relational.Query.Sub (SubQuery, subQuery)
import Database.Relational.Query.Internal.AggregatingContext (AggregatingContext, primeAggregatingContext) import Database.Relational.Query.Internal.AggregatingContext (AggregatingContext, primeAggregatingContext)
import qualified Database.Relational.Query.Internal.AggregatingContext as Context import qualified Database.Relational.Query.Internal.AggregatingContext as Context
import Database.Relational.Query.Monad.Qualify (Qualify)
import Database.Relational.Query.Monad.Unsafe (UnsafeMonadQuery(unsafeSubQuery)) import Database.Relational.Query.Monad.Unsafe (UnsafeMonadQuery(unsafeSubQuery))
import Database.Relational.Query.Monad.Class (MonadQuery, MonadAggregate) import Database.Relational.Query.Monad.Class (MonadQuery, MonadAggregate)
import qualified Database.Relational.Query.Monad.Class as MonadQuery import qualified Database.Relational.Query.Monad.Class as MonadQuery
@ -103,14 +104,16 @@ appendGroupBys q = second appendGroupBys' `liftM` runAggregatingPrime q
type QueryAggregate = Orderings Aggregation (Aggregatings QueryCore) type QueryAggregate = Orderings Aggregation (Aggregatings QueryCore)
type AggregatedQuery r = OrderedQuery Aggregation (Aggregatings QueryCore) r type AggregatedQuery r = OrderedQuery Aggregation (Aggregatings QueryCore) r
expandSQL :: AggregatedQuery r -> ((String, Projection r), (String -> String, String -> String)) expandSQL :: QueryAggregate (Aggregation r) -> Qualify ((String, Projection r), (String -> String, String -> String))
expandSQL q = Core.expandSQL $ assoc <$> appendGroupBys (Ordering.appendOrderBys q) where expandSQL q = Core.expandSQL $ assoc <$> appendGroupBys (Ordering.appendOrderBys q) where
assoc ((a, b), c) = (Aggregation.projection a, (b, c)) assoc ((a, b), c) = (Aggregation.projection a, (b, c))
toSQL :: AggregatedQuery r -> String toSQL :: QueryAggregate (Aggregation r) -> Qualify String
toSQL q = appOrd $ appGrp sql where toSQL q = do
((sql, _pj), (appOrd, appGrp)) = expandSQL q ((sql, _pj), (appOrd, appGrp)) <- expandSQL q
return . appOrd $ appGrp sql
toSubQuery :: AggregatedQuery r -> SubQuery toSubQuery :: QueryAggregate (Aggregation r) -> Qualify SubQuery
toSubQuery q = subQuery (appOrd $ appGrp sql) (Projection.width pj) where toSubQuery q = do
((sql, pj), (appOrd, appGrp)) = expandSQL q ((sql, pj), (appOrd, appGrp)) <- expandSQL q
return $ subQuery (appOrd $ appGrp sql) (Projection.width pj)

View File

@ -1,32 +1,32 @@
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
module Database.Relational.Query.Monad.Core ( module Database.Relational.Query.Monad.Core (
QueryJoin, join',
QueryCore, QueryCore,
expr, expr,
unsafeSubQueryWithAttr, unsafeSubQueryWithAttr,
unsafeQueryMergeWithAttr, -- unsafeQueryMergeWithAttr,
expandSQL expandSQL
) where ) where
import Prelude hiding (product) import Prelude hiding (product)
import Control.Monad (liftM, ap) import Control.Monad (liftM, ap)
import Control.Monad.Trans.State (State, state, runState, modify) import Control.Monad.Trans.Class (MonadTrans (lift))
import Control.Monad.Trans.State (modify, StateT, runStateT)
import Control.Applicative (Applicative (pure, (<*>))) import Control.Applicative (Applicative (pure, (<*>)))
import Database.Relational.Query.Internal.Context import Database.Relational.Query.Internal.Context
(Context, primeContext, nextAlias, updateProduct, composeSQL) (Context, primeContext, updateProduct, composeSQL)
import qualified Database.Relational.Query.Internal.Context as Context import qualified Database.Relational.Query.Internal.Context as Context
import Database.Relational.Query.Internal.AliasId (AliasId, Qualified)
import qualified Database.Relational.Query.Internal.AliasId as AliasId
import Database.Relational.Query.Expr (Expr, fromTriBool) import Database.Relational.Query.Expr (Expr, fromTriBool)
import Database.Relational.Query.Internal.Product import Database.Relational.Query.Internal.Product
(NodeAttr, QueryProductNode, growProduct, restrictProduct) (NodeAttr, growProduct, restrictProduct)
import Database.Relational.Query.Projection (Projection) import Database.Relational.Query.Projection (Projection)
import qualified Database.Relational.Query.Projection as Projection import qualified Database.Relational.Query.Projection as Projection
@ -34,90 +34,90 @@ import Database.Relational.Query.Projectable (Projectable(project))
import Database.Relational.Query.Sub (SubQuery) import Database.Relational.Query.Sub (SubQuery)
import Database.Relational.Query.Monad.Qualify (Qualify, evalQualifyPrime, qualifyQuery)
import Database.Relational.Query.Monad.Class (MonadQuery(on, wheres)) import Database.Relational.Query.Monad.Class (MonadQuery(on, wheres))
import Database.Relational.Query.Monad.Unsafe import Database.Relational.Query.Monad.Unsafe (UnsafeMonadQuery(unsafeSubQuery))
(UnsafeMonadQuery(unsafeSubQuery))
newtype QueryCore a =
QueryCore { queryState :: State Context a }
runQueryCore :: QueryCore a -> Context -> (a, Context)
runQueryCore = runState . queryState
queryCore :: (Context -> (a, Context)) -> QueryCore a
queryCore = QueryCore . state
runQueryPrime :: QueryCore a -> (a, Context)
runQueryPrime q = runQueryCore q primeContext
newAlias :: QueryCore AliasId
newAlias = queryCore nextAlias
updateContext :: (Context -> Context) -> QueryCore ()
updateContext = QueryCore . modify
updateJoinRestriction :: Expr (Maybe Bool) -> QueryCore () newtype QueryJoin m a =
QueryJoin { queryState :: StateT Context m a }
runQueryJoin :: QueryJoin m a -> Context -> m (a, Context)
runQueryJoin = runStateT . queryState
runQueryPrime :: QueryJoin m a -> m (a, Context)
runQueryPrime q = runQueryJoin q primeContext
instance MonadTrans QueryJoin where
lift = QueryJoin . lift
join' :: Monad m => m a -> QueryJoin m a
join' = lift
updateContext :: Monad m => (Context -> Context) -> QueryJoin m ()
updateContext = QueryJoin . modify
updateJoinRestriction :: Monad m => Expr (Maybe Bool) -> QueryJoin m ()
updateJoinRestriction e = updateContext (updateProduct d) where updateJoinRestriction e = updateContext (updateProduct d) where
d Nothing = error "on: product is empty!" d Nothing = error "on: product is empty!"
d (Just pt) = restrictProduct pt (fromTriBool e) d (Just pt) = restrictProduct pt (fromTriBool e)
updateRestriction :: Expr (Maybe Bool) -> QueryCore () updateRestriction :: Monad m => Expr (Maybe Bool) -> QueryJoin m ()
updateRestriction e = updateContext (Context.addRestriction e) updateRestriction e = updateContext (Context.addRestriction e)
takeProduct :: QueryCore (Maybe QueryProductNode) -- takeProduct :: QueryJoin (Maybe QueryProductNode)
takeProduct = queryCore Context.takeProduct -- takeProduct = queryCore Context.takeProduct
restoreLeft :: QueryProductNode -> NodeAttr -> QueryCore () -- restoreLeft :: QueryProductNode -> NodeAttr -> QueryJoin ()
restoreLeft pL naR = updateContext $ Context.restoreLeft pL naR -- restoreLeft pL naR = updateContext $ Context.restoreLeft pL naR
expr :: Projection ft -> Expr ft expr :: Projection ft -> Expr ft
expr = project expr = project
instance Monad QueryCore where instance Monad m => Monad (QueryJoin m) where
return = QueryCore . return return = QueryJoin . return
q0 >>= f = QueryCore $ queryState q0 >>= queryState . f q0 >>= f = QueryJoin $ queryState q0 >>= queryState . f
instance Functor QueryCore where instance Monad m => Functor (QueryJoin m) where
fmap = liftM fmap = liftM
instance Applicative QueryCore where instance Monad m => Applicative (QueryJoin m) where
pure = return pure = return
(<*>) = ap (<*>) = ap
instance MonadQuery QueryCore where instance MonadQuery (QueryJoin Qualify) where
on = updateJoinRestriction on = updateJoinRestriction
wheres = updateRestriction wheres = updateRestriction
qualify :: rel -> QueryCore (Qualified rel) type QueryCore = QueryJoin Qualify
qualify rel =
do n <- newAlias
return $ AliasId.qualify rel n
unsafeSubQueryWithAttr :: NodeAttr -> SubQuery -> QueryCore (Projection t) unsafeSubQueryWithAttr :: NodeAttr -> Qualify SubQuery -> QueryJoin Qualify (Projection t)
unsafeSubQueryWithAttr attr sub = do unsafeSubQueryWithAttr attr qualSub = do
qsub <- qualify sub qsub <- join' (qualSub >>= qualifyQuery)
updateContext (updateProduct (`growProduct` (attr, qsub))) updateContext (updateProduct (`growProduct` (attr, qsub)))
return $ Projection.fromQualifiedSubQuery qsub return $ Projection.fromQualifiedSubQuery qsub
unsafeMergeAnother :: NodeAttr -> QueryCore a -> QueryCore a -- unsafeMergeAnother :: NodeAttr -> QueryJoin a -> QueryJoin a
unsafeMergeAnother naR qR = do -- unsafeMergeAnother naR qR = do
mayPL <- takeProduct -- mayPL <- takeProduct
v <- qR -- v <- qR
maybe (return ()) (\pL -> restoreLeft pL naR) mayPL -- maybe (return ()) (\pL -> restoreLeft pL naR) mayPL
return v -- return v
unsafeQueryMergeWithAttr :: NodeAttr -> QueryCore (Projection r) -> QueryCore (Projection r) -- unsafeQueryMergeWithAttr :: NodeAttr -> QueryJoin (Projection r) -> QueryJoin (Projection r)
unsafeQueryMergeWithAttr = unsafeMergeAnother -- unsafeQueryMergeWithAttr = unsafeMergeAnother
instance UnsafeMonadQuery QueryCore where instance UnsafeMonadQuery (QueryJoin Qualify) where
unsafeSubQuery = unsafeSubQueryWithAttr unsafeSubQuery = unsafeSubQueryWithAttr
-- unsafeMergeAnotherQuery = unsafeQueryMergeWithAttr -- unsafeMergeAnotherQuery = unsafeQueryMergeWithAttr
expandSQL :: QueryCore (Projection r, st) -> ((String, Projection r), st) expandSQL :: Monad m => QueryJoin m (Projection r, t) -> m ((String, Projection r), t)
expandSQL qp = ((composeSQL pj c, pj), st) where expandSQL qp = do
((pj, st), c) = runQueryPrime qp ((pj, st), c) <- runQueryPrime qp
return ((composeSQL pj c, pj), st)
instance Show (QueryCore (Projection r)) where instance Show (QueryJoin Qualify (Projection r)) where
show = fst . fst . expandSQL . fmap (\x -> (,) x ()) show = fst . fst . doExpand
where doExpand = evalQualifyPrime . expandSQL . fmap (\x -> (,) x ())

View File

@ -0,0 +1,55 @@
{-# LANGUAGE MultiParamTypeClasses #-}
module Database.Relational.Query.Monad.Qualify (
Qualify, evalQualifyPrime, newAlias, qualifyQuery
) where
import Control.Monad (liftM, ap)
import Control.Monad.Trans.State
(State, state, runState)
import Control.Applicative (Applicative (pure, (<*>)))
import Database.Relational.Query.Internal.AliasId (AliasId, Qualified)
import qualified Database.Relational.Query.Internal.AliasId as AliasId
import Database.Relational.Query.Internal.Context
(AliasIdContext, primeAliasIdContext, nextAlias)
newtype Qualify a =
Qualify { runQualify' :: State AliasIdContext a }
runQualify :: Qualify a -> AliasIdContext -> (a, AliasIdContext)
runQualify = runState . runQualify'
runQualifyPrime :: Qualify a -> (a, AliasIdContext)
runQualifyPrime q = runQualify q primeAliasIdContext
evalQualifyPrime :: Qualify a -> a
evalQualifyPrime = fst . runQualifyPrime
qualifyState :: (AliasIdContext -> (a, AliasIdContext)) -> Qualify a
qualifyState = Qualify . state
instance Monad Qualify where
return = Qualify . return
q0 >>= f = Qualify $ runQualify' q0 >>= runQualify' . f
instance Functor Qualify where
fmap = liftM
instance Applicative Qualify where
pure = return
(<*>) = ap
newAlias :: Qualify AliasId
newAlias = qualifyState nextAlias
qualifyQuery :: query -> Qualify (Qualified query)
qualifyQuery query =
do n <- newAlias
return $ AliasId.qualify query n
-- qualifyQuery :: Qualify query -> Qualify (Qualified query)
-- qualifyQuery qualSub = do
-- sub <- qualSub
-- qualifyQuery' sub

View File

@ -11,11 +11,10 @@ module Database.Relational.Query.Monad.Simple (
toSubQuery toSubQuery
) where ) where
-- import Database.Relational.Query.Internal.Product (NodeAttr)
import Database.Relational.Query.Projection (Projection) import Database.Relational.Query.Projection (Projection)
import qualified Database.Relational.Query.Projection as Projection import qualified Database.Relational.Query.Projection as Projection
import Database.Relational.Query.Monad.Qualify (Qualify)
import Database.Relational.Query.Monad.Ordering (Orderings, orderings, OrderedQuery) import Database.Relational.Query.Monad.Ordering (Orderings, orderings, OrderedQuery)
import qualified Database.Relational.Query.Monad.Ordering as Ordering import qualified Database.Relational.Query.Monad.Ordering as Ordering
import Database.Relational.Query.Monad.Core (QueryCore) import Database.Relational.Query.Monad.Core (QueryCore)
@ -33,13 +32,15 @@ simple = orderings
-- unsafeMergeAnotherOrderBys :: NodeAttr -> QuerySimple (Projection r) -> QuerySimple (Projection r) -- unsafeMergeAnotherOrderBys :: NodeAttr -> QuerySimple (Projection r) -> QuerySimple (Projection r)
-- unsafeMergeAnotherOrderBys = Ordering.unsafeMergeAnotherOrderBys -- unsafeMergeAnotherOrderBys = Ordering.unsafeMergeAnotherOrderBys
expandSQL :: SimpleQuery r -> ((String, Projection r), String -> String) expandSQL :: SimpleQuery r -> Qualify ((String, Projection r), String -> String)
expandSQL = Core.expandSQL . Ordering.appendOrderBys expandSQL = Core.expandSQL . Ordering.appendOrderBys
toSQL :: SimpleQuery r -> String toSQL :: SimpleQuery r -> Qualify String
toSQL q = append sql where toSQL q = do
((sql, _), append) = expandSQL q ((sql, _), append) <- expandSQL q
return $ append sql where
toSubQuery :: SimpleQuery r -> SubQuery toSubQuery :: SimpleQuery r -> Qualify SubQuery
toSubQuery q = subQuery (append sql) (Projection.width pj) where toSubQuery q = do
((sql, pj), append) = expandSQL q ((sql, pj), append) <- expandSQL q
return $ subQuery (append sql) (Projection.width pj)

View File

@ -1,4 +1,3 @@
module Database.Relational.Query.Monad.Unsafe ( module Database.Relational.Query.Monad.Unsafe (
UnsafeMonadQuery (..) UnsafeMonadQuery (..)
) where ) where
@ -6,7 +5,8 @@ module Database.Relational.Query.Monad.Unsafe (
import Database.Relational.Query.Internal.Product (NodeAttr) import Database.Relational.Query.Internal.Product (NodeAttr)
import Database.Relational.Query.Projection (Projection) import Database.Relational.Query.Projection (Projection)
import Database.Relational.Query.Sub (SubQuery) import Database.Relational.Query.Sub (SubQuery)
import Database.Relational.Query.Monad.Qualify (Qualify)
class (Functor m, Monad m) => UnsafeMonadQuery m where class (Functor m, Monad m) => UnsafeMonadQuery m where
unsafeSubQuery :: NodeAttr -> SubQuery -> m (Projection t) unsafeSubQuery :: NodeAttr -> Qualify SubQuery -> m (Projection r)
-- unsafeMergeAnotherQuery :: NodeAttr -> m (Projection r) -> m (Projection r) -- unsafeMergeAnotherQuery :: NodeAttr -> m (Projection r) -> m (Projection r)

View File

@ -14,11 +14,12 @@ module Database.Relational.Query.Relation (
sqlFromRelation, sqlFromRelation,
subQueryFromRelation, -- subQueryFromRelation,
nested, width nested, width
) where ) where
import Database.Relational.Query.Monad.Qualify (Qualify, evalQualifyPrime)
import Database.Relational.Query.Monad.Class (MonadQuery (on)) import Database.Relational.Query.Monad.Class (MonadQuery (on))
import qualified Database.Relational.Query.Monad.Unsafe as UnsafeMonadQuery import qualified Database.Relational.Query.Monad.Unsafe as UnsafeMonadQuery
import Database.Relational.Query.Monad.Simple (QuerySimple, SimpleQuery) import Database.Relational.Query.Monad.Simple (QuerySimple, SimpleQuery)
@ -57,15 +58,19 @@ from :: Table r -> Relation r
from = table from = table
subQueryFromRelation :: PrimeRelation p r -> SubQuery subQueryQualifyFromRelation :: PrimeRelation p r -> Qualify SubQuery
subQueryFromRelation = d where subQueryQualifyFromRelation = d where
d (SubQuery sub) = sub d (SubQuery sub) = return $ sub
d (SimpleRel qp) = Simple.toSubQuery qp d (SimpleRel qp) = Simple.toSubQuery qp
d (AggregateRel qp) = Aggregate.toSubQuery qp d (AggregateRel qp) = Aggregate.toSubQuery qp
queryWithAttr :: MonadQuery m => NodeAttr -> PrimeRelation p r -> m (PlaceHolders p, Projection r) subQueryFromRelation :: PrimeRelation p r -> SubQuery
subQueryFromRelation = evalQualifyPrime . subQueryQualifyFromRelation
queryWithAttr :: MonadQuery m
=> NodeAttr -> PrimeRelation p r -> m (PlaceHolders p, Projection r)
queryWithAttr attr = addPlaceHolders . q where queryWithAttr attr = addPlaceHolders . q where
q = UnsafeMonadQuery.unsafeSubQuery attr . subQueryFromRelation q = UnsafeMonadQuery.unsafeSubQuery attr . subQueryQualifyFromRelation
-- d (PrimeRelation q) = UnsafeMonadQuery.unsafeMergeAnotherQuery attr q -- d (PrimeRelation q) = UnsafeMonadQuery.unsafeMergeAnotherQuery attr q
query' :: MonadQuery m => PrimeRelation p r -> m (PlaceHolders p, Projection r) query' :: MonadQuery m => PrimeRelation p r -> m (PlaceHolders p, Projection r)
@ -182,12 +187,15 @@ on' = ($)
infixl 8 `inner'`, `left'`, `right'`, `full'`, `inner`, `left`, `right`, `full`, `on'` infixl 8 `inner'`, `left'`, `right'`, `full'`, `inner`, `left`, `right`, `full`, `on'`
sqlFromRelation :: PrimeRelation p r -> String sqlQualifyFromRelation :: PrimeRelation p r -> Qualify String
sqlFromRelation = d where sqlQualifyFromRelation = d where
d (SubQuery sub) = SubQuery.toSQL sub d (SubQuery sub) = return $ SubQuery.toSQL sub
d (SimpleRel qp) = Simple.toSQL qp d (SimpleRel qp) = Simple.toSQL qp
d (AggregateRel qp) = Aggregate.toSQL qp d (AggregateRel qp) = Aggregate.toSQL qp
sqlFromRelation :: PrimeRelation p r -> String
sqlFromRelation = evalQualifyPrime . sqlQualifyFromRelation
instance Show (PrimeRelation p r) where instance Show (PrimeRelation p r) where
show = sqlFromRelation show = sqlFromRelation