module Database.Seakale.Store.Join
( JoinLeftProperty(..)
, JoinRightProperty(..)
, EntityID(..)
, LeftJoin(..)
, RightJoin(..)
, InnerJoin(..)
, FullJoin(..)
, JoinRelation
, selectJoin
, selectJoin_
, countJoin
, leftJoin
, leftJoin_
, rightJoin
, rightJoin_
, innerJoin
, innerJoin_
, fullJoin
, fullJoin_
) where
import GHC.Generics
import Data.Monoid
import Data.Typeable
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy as BSL
import Database.Seakale.FromRow
import Database.Seakale.Store
import Database.Seakale.Types
import qualified Database.Seakale.Store.Internal as I
data JoinLeftProperty (j :: * -> * -> *) f b backend (n :: Nat) c
= JLeft (f backend n c)
data JoinRightProperty (j :: * -> * -> *) f a backend (n :: Nat) c
= JRight (f backend n c)
instance Property backend a f
=> Property backend (j a b) (JoinLeftProperty j f b) where
toColumns backend (JLeft prop) =
fmap (\col -> Column $ unColumn col . (<> "l")) (toColumns backend prop)
instance Property backend b f
=> Property backend (j a b) (JoinRightProperty j f a) where
toColumns backend (JRight prop) =
fmap (\col -> Column $ unColumn col . (<> "r")) (toColumns backend prop)
data LeftJoin a b = LeftJoin a (Maybe b) deriving (Show, Eq, Generic)
instance ( Storable backend k l a, Storable backend i j b
, (k :+ i) ~ g, (l :+ j) ~ h, Typeable g, Typeable h )
=> Storable backend g h (LeftJoin a b) where
data EntityID (LeftJoin a b) = LeftJoinID (EntityID a) (Maybe (EntityID b))
relation = leftJoin_ mempty
instance (FromRow backend k a, FromRow backend l (Maybe b), (k :+ l) ~ i)
=> FromRow backend i (LeftJoin a b) where
fromRow = LeftJoin `pmap` fromRow `papply` fromRow
instance ( FromRow backend k (EntityID a)
, FromRow backend l (Maybe (EntityID b)) , (k :+ l) ~ i )
=> FromRow backend i (EntityID (LeftJoin a b)) where
fromRow = LeftJoinID `pmap` fromRow `papply` fromRow
deriving instance (Show (EntityID a), Show (EntityID b))
=> Show (EntityID (LeftJoin a b))
deriving instance (Eq (EntityID a), Eq (EntityID b))
=> Eq (EntityID (LeftJoin a b))
data RightJoin a b = RightJoin (Maybe a) b deriving (Show, Eq)
instance ( Storable backend k l a, Storable backend i j b
, (k :+ i) ~ g, (l :+ j) ~ h, Typeable g, Typeable h )
=> Storable backend g h (RightJoin a b) where
data EntityID (RightJoin a b) = RightJoinID (Maybe (EntityID a)) (EntityID b)
relation = rightJoin_ mempty
instance (FromRow backend k (Maybe a), FromRow backend l b, (k :+ l) ~ i)
=> FromRow backend i (RightJoin a b) where
fromRow = RightJoin `pmap` fromRow `papply` fromRow
instance ( FromRow backend k (Maybe (EntityID a))
, FromRow backend l (EntityID b) , (k :+ l) ~ i )
=> FromRow backend i (EntityID (RightJoin a b)) where
fromRow = RightJoinID `pmap` fromRow `papply` fromRow
deriving instance (Show (EntityID a), Show (EntityID b))
=> Show (EntityID (RightJoin a b))
deriving instance (Eq (EntityID a), Eq (EntityID b))
=> Eq (EntityID (RightJoin a b))
data InnerJoin a b = InnerJoin a b deriving (Show, Eq)
instance ( Storable backend k l a, Storable backend i j b
, (k :+ i) ~ g, (l :+ j) ~ h, Typeable g, Typeable h )
=> Storable backend g h (InnerJoin a b) where
data EntityID (InnerJoin a b) = InnerJoinID (EntityID a) (EntityID b)
relation = innerJoin_ mempty
instance (FromRow backend k a, FromRow backend l b, (k :+ l) ~ i)
=> FromRow backend i (InnerJoin a b) where
fromRow = InnerJoin `pmap` fromRow `papply` fromRow
instance ( FromRow backend k (EntityID a), FromRow backend l (EntityID b)
, (k :+ l) ~ i )
=> FromRow backend i (EntityID (InnerJoin a b)) where
fromRow = InnerJoinID `pmap` fromRow `papply` fromRow
deriving instance (Show (EntityID a), Show (EntityID b))
=> Show (EntityID (InnerJoin a b))
deriving instance (Eq (EntityID a), Eq (EntityID b))
=> Eq (EntityID (InnerJoin a b))
data FullJoin a b = FullJoin (Maybe a) (Maybe b) deriving (Show, Eq)
instance ( Storable backend k l a, Storable backend i j b
, (k :+ i) ~ g, (l :+ j) ~ h, Typeable g, Typeable h )
=> Storable backend g h (FullJoin a b) where
data EntityID (FullJoin a b)
= FullJoinID (Maybe (EntityID a)) (Maybe (EntityID b))
relation = fullJoin_ mempty
instance ( FromRow backend k (Maybe a), FromRow backend l (Maybe b)
, (k :+ l) ~ i )
=> FromRow backend i (FullJoin a b) where
fromRow = FullJoin `pmap` fromRow `papply` fromRow
instance ( FromRow backend k (Maybe (EntityID a))
, FromRow backend l (Maybe (EntityID b)) , (k :+ l) ~ i )
=> FromRow backend i (EntityID (FullJoin a b)) where
fromRow = FullJoinID `pmap` fromRow `papply` fromRow
deriving instance (Show (EntityID a), Show (EntityID b))
=> Show (EntityID (FullJoin a b))
deriving instance (Eq (EntityID a), Eq (EntityID b))
=> Eq (EntityID (FullJoin a b))
type JoinRelation backend k l a = backend -> Relation backend k l a
selectJoin :: ( MonadSelect backend m, Storable backend k l (f a b)
, FromRow backend (k :+ l) (Entity (f a b)) )
=> JoinRelation backend k l (f a b)
-> Condition backend (f a b) -> SelectClauses backend (f a b)
-> m [Entity (f a b)]
selectJoin jrel cond clauses = do
backend <- getBackend
I.select (jrel backend) cond clauses
selectJoin_ :: ( MonadSelect backend m, Storable backend k l (f a b)
, FromRow backend (k :+ l) (Entity (f a b)) )
=> JoinRelation backend k l (f a b)
-> Condition backend (f a b) -> m [Entity (f a b)]
selectJoin_ jrel cond = selectJoin jrel cond mempty
countJoin :: (MonadSelect backend m, Storable backend k l (f a b))
=> JoinRelation backend k l (f a b)
-> Condition backend (f a b) -> m Integer
countJoin jrel cond = do
backend <- getBackend
I.count (jrel backend) cond
leftJoin :: JoinRelation backend k l a -> JoinRelation backend i j b
-> Condition backend (LeftJoin a b)
-> JoinRelation backend (k :+ i) (l :+ j) (LeftJoin a b)
leftJoin = mkJoin "LEFT JOIN"
leftJoin_ :: (Storable backend k l a, Storable backend i j b)
=> Condition backend (LeftJoin a b)
-> JoinRelation backend (k :+ i) (l :+ j) (LeftJoin a b)
leftJoin_ = leftJoin relation relation
rightJoin :: JoinRelation backend k l a -> JoinRelation backend i j b
-> Condition backend (RightJoin a b)
-> JoinRelation backend (k :+ i) (l :+ j) (RightJoin a b)
rightJoin = mkJoin "RIGHT JOIN"
rightJoin_ :: (Storable backend k l a, Storable backend i j b)
=> Condition backend (RightJoin a b)
-> JoinRelation backend (k :+ i) (l :+ j) (RightJoin a b)
rightJoin_ = rightJoin relation relation
innerJoin :: JoinRelation backend k l a -> JoinRelation backend i j b
-> Condition backend (InnerJoin a b)
-> JoinRelation backend (k :+ i) (l :+ j) (InnerJoin a b)
innerJoin = mkJoin "INNER JOIN"
innerJoin_ :: (Storable backend k l a, Storable backend i j b)
=> Condition backend (InnerJoin a b)
-> JoinRelation backend (k :+ i) (l :+ j) (InnerJoin a b)
innerJoin_ = innerJoin relation relation
fullJoin :: JoinRelation backend k l a -> JoinRelation backend i j b
-> Condition backend (FullJoin a b)
-> JoinRelation backend (k :+ i) (l :+ j) (FullJoin a b)
fullJoin = mkJoin "FULL JOIN"
fullJoin_ :: (Storable backend k l a, Storable backend i j b)
=> Condition backend (FullJoin a b)
-> JoinRelation backend (k :+ i) (l :+ j) (FullJoin a b)
fullJoin_ = fullJoin relation relation
mkJoin :: BS.ByteString
-> JoinRelation backend k l a -> JoinRelation backend i j b
-> Condition backend (f a b)
-> JoinRelation backend (k :+ i) (l :+ j) (f a b)
mkJoin joinStmt jrelA jrelB cond backend =
let relA = jrelA backend
relB = jrelB backend
in combineRelations joinStmt backend relA relB cond
combineRelationNames :: BS.ByteString -> backend -> RelationName -> RelationName
-> Condition backend a -> RelationName
combineRelationNames joinStmt backend l r cond = RelationName $ \prefix ->
unRelationName l (prefix <> "l")
<> " " <> joinStmt <> " "
<> unRelationName r (prefix <> "r")
<> BSL.toStrict (I.buildOnClause cond prefix backend)
combineRelationColumns :: Vector k Column -> Vector l Column
-> Vector (k :+ l) Column
combineRelationColumns l r =
fmap (\col -> Column $ unColumn col . (<> "l")) l
`vappend`
fmap (\col -> Column $ unColumn col . (<> "r")) r
combineRelations :: BS.ByteString -> backend -> Relation backend k l a
-> Relation backend i j b -> Condition backend c
-> Relation backend (k :+ i) (l :+ j) c
combineRelations joinStmt backend relA relB cond = Relation
{ relationName =
combineRelationNames joinStmt backend
(relationName relA) (relationName relB) cond
, relationIDColumns =
combineRelationColumns (relationIDColumns relA) (relationIDColumns relB)
, relationColumns =
combineRelationColumns (relationColumns relA) (relationColumns relB)
}