Add new experimental aggregates using SqlAggregate wrapper around SqlExpr.

This commit is contained in:
belevy 2021-02-14 16:56:58 -06:00
commit 75f9c8d3b8
6 changed files with 293 additions and 134 deletions

View File

@ -30,6 +30,7 @@ library
exposed-modules:
Database.Esqueleto
Database.Esqueleto.Experimental
Database.Esqueleto.Experimental.Aggregates
Database.Esqueleto.Internal.Language
Database.Esqueleto.Internal.Sql
Database.Esqueleto.Internal.Internal

View File

@ -53,6 +53,8 @@ module Database.Esqueleto.Experimental
, with
, withRecursive
, agg
-- ** Internals
, From(..)
, ToMaybe(..)
@ -219,9 +221,13 @@ module Database.Esqueleto.Experimental
, module Database.Esqueleto.Internal.PersistentImport
) where
import Database.Esqueleto.Internal.Internal hiding (From, from, on)
import Data.Coerce
import Database.Esqueleto.Internal.Internal hiding
(From, from, groupBy, on, sum_, (?.), (^.))
import qualified Database.Esqueleto.Internal.Internal as I ((?.), (^.))
import Database.Esqueleto.Internal.PersistentImport
import Database.Esqueleto.Experimental.Aggregates
import Database.Esqueleto.Experimental.From
import Database.Esqueleto.Experimental.From.CommonTableExpression
import Database.Esqueleto.Experimental.From.Join
@ -561,3 +567,4 @@ import Database.Esqueleto.Experimental.ToMaybe
-- )
-- @
--
--

View File

@ -0,0 +1,139 @@
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Database.Esqueleto.Experimental.Aggregates
where
import Control.Monad.IO.Class
import qualified Control.Monad.Trans.Writer as W
import Data.Coerce (Coercible, coerce)
import Database.Esqueleto.Internal.Internal
( GroupByClause(..)
, SideData(..)
, SqlExpr(..)
, SqlQuery(..)
, SqlSelect(..)
, ToSomeValues(..)
, noMeta
, select
, unsafeSqlFunction
)
import qualified Database.Esqueleto.Internal.Internal as I
import Database.Esqueleto.Internal.PersistentImport
( Entity
, EntityField
, Key
, PersistEntity
, PersistField
, SqlReadT
, persistIdField
)
class SqlExprEntity expr where
(^.) :: (PersistEntity val, PersistField typ)
=> expr (Entity val)
-> EntityField val typ
-> expr typ
(?.) :: (PersistEntity val, PersistField typ)
=> expr (Maybe (Entity val))
-> EntityField val typ
-> expr (Maybe typ)
-- | Project a field of an entity.
instance SqlExprEntity SqlExpr where
(^.) = (I.^.)
(?.) = (I.?.)
newtype SqlAggregate a = SqlAggregate { agg :: SqlExpr a }
deriving via SqlExpr instance SqlExprEntity SqlAggregate
test :: (PersistEntity ent, PersistField a, PersistField b, PersistField c)
=> SqlExpr (Maybe (Entity ent))
-> EntityField ent a
-> SqlExpr b
-> SqlExpr c
-> SqlQuery (SqlExpr (Maybe a), SqlExpr b, SqlExpr (Maybe Int), SqlExpr Int)
test ent field y other = do
groupBy (ent, y) $ \(ent', y') ->
pure (ent' ?. field, y', sum_ other, countRows_)
countRows_ :: (PersistField n, Integral n) => SqlAggregate n
countRows_ = SqlAggregate $ ERaw noMeta $ \_ _ -> ("COUNT(*)", [])
-- Tuple magic, only SqlExprs are on the leaves.
-- The Coercible instance from the SqlExpr a -> SqlExpr b allows 0 cost casting
class Coercible a r => Aggregateable a r | a -> r, r -> a where
toAggregate :: a -> r
toAggregate = coerce
fromAggregate :: r -> a
fromAggregate = coerce
instance Aggregateable () () where
instance Aggregateable (SqlExpr a) (SqlAggregate a) where
instance (Aggregateable a ra, Aggregateable b rb) => Aggregateable (a,b) (ra, rb) where
instance
( Aggregateable a ra
, Aggregateable b rb
, Aggregateable c rc
) => Aggregateable (a,b,c) (ra,rb,rc) where
instance
( Aggregateable a ra
, Aggregateable b rb
, Aggregateable c rc
, Aggregateable d rd
) => Aggregateable (a,b,c,d) (ra,rb,rc,rd) where
instance
( Aggregateable a ra
, Aggregateable b rb
, Aggregateable c rc
, Aggregateable d rd
, Aggregateable e re
) => Aggregateable (a,b,c,d,e) (ra,rb,rc,rd,re) where
instance
( Aggregateable a ra
, Aggregateable b rb
, Aggregateable c rc
, Aggregateable d rd
, Aggregateable e re
, Aggregateable f rf
) => Aggregateable (a,b,c,d,e,f) (ra,rb,rc,rd,re,rf) where
instance
( Aggregateable a ra
, Aggregateable b rb
, Aggregateable c rc
, Aggregateable d rd
, Aggregateable e re
, Aggregateable f rf
, Aggregateable g rg
) => Aggregateable (a,b,c,d,e,f,g) (ra,rb,rc,rd,re,rf,rg) where
instance
( Aggregateable a ra
, Aggregateable b rb
, Aggregateable c rc
, Aggregateable d rd
, Aggregateable e re
, Aggregateable f rf
, Aggregateable g rg
, Aggregateable h rh
) => Aggregateable (a,b,c,d,e,f,g,h) (ra,rb,rc,rd,re,rf,rg,rh) where
sum_ :: (PersistField a, PersistField n, Integral n) => SqlExpr a -> SqlAggregate (Maybe n)
sum_ = coerce . unsafeSqlFunction "SUM"
groupBy :: ( ToSomeValues a
, Aggregateable a a'
, Aggregateable b b'
) => a -> (a' -> SqlQuery b') -> SqlQuery b
groupBy a f = do
Q $ W.tell $ mempty{sdGroupByClause = GroupBy $ toSomeValues a }
fmap fromAggregate $ f $ toAggregate a

View File

@ -10,22 +10,21 @@
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
module Database.Esqueleto.Experimental.From.Join
where
import Data.Bifunctor (first)
import Control.Arrow (first)
import Data.Kind (Constraint)
import Data.Proxy
import qualified Data.Text.Lazy.Builder as TLB
import Database.Esqueleto.Experimental.From
import Database.Esqueleto.Experimental.From.SqlSetOperation
import Database.Esqueleto.Experimental.ToAlias
import Database.Esqueleto.Experimental.ToAliasReference
import Database.Esqueleto.Experimental.ToMaybe
import Database.Esqueleto.Internal.Internal hiding
(From(..), from, fromJoin, on)
import Database.Esqueleto.Internal.PersistentImport
(Entity(..), EntityField, PersistEntity, PersistField)
import GHC.TypeLits
-- | A left-precedence pair. Pronounced \"and\". Used to represent expressions
@ -40,16 +39,24 @@ import GHC.TypeLits
-- See the examples at the beginning of this module to see how this
-- operator is used in 'JOIN' operations.
data (:&) a b = a :& b
deriving (Show, Eq)
infixl 2 :&
instance (ToMaybe a, ToMaybe b) => ToMaybe (a :& b) where
type ToMaybeT (a :& b) = (ToMaybeT a :& ToMaybeT b)
toMaybe (a :& b) = (toMaybe a :& toMaybe b)
class ValidOnClause a
instance {-# OVERLAPPABLE #-} ToFrom a a' => ValidOnClause a
instance ValidOnClause (a -> SqlQuery b)
fromInductiveTupleP :: Proxy (a :& b) -> Proxy (a, b)
fromInductiveTupleP = const Proxy
toInductiveTuple :: (a, b) -> (a :& b)
toInductiveTuple (a, b) = a :& b
instance (SqlSelect a a', SqlSelect b b') => SqlSelect (a :& b) (a' :& b') where
sqlSelectCols esc (a :& b) = sqlSelectCols esc (a, b)
sqlSelectColCount = sqlSelectColCount . fromInductiveTupleP
sqlSelectProcessRow p = fmap toInductiveTuple . sqlSelectProcessRow (fromInductiveTupleP p)
class ValidOnClause a where
-- | An @ON@ clause that describes how two tables are related. This should be
-- used as an infix operator after a 'JOIN'. For example,
--
@ -60,10 +67,13 @@ instance ValidOnClause (a -> SqlQuery b)
-- \`on\` (\\(p :& bP) ->
-- p ^. PersonId ==. bP ^. BlogPostAuthorId)
-- @
on :: ValidOnClause a => a -> (b -> SqlExpr Bool) -> (a, b -> SqlExpr Bool)
on :: a -> (b -> SqlExpr Bool) -> (a, b -> SqlExpr Bool)
on = (,)
infix 9 `on`
instance {-# OVERLAPPABLE #-} ToFrom a a' => ValidOnClause a
instance ValidOnClause (a -> SqlQuery b)
type family ErrorOnLateral a :: Constraint where
ErrorOnLateral (a -> SqlQuery b) = TypeError ('Text "LATERAL can only be used for INNER, LEFT, and CROSS join kinds.")
ErrorOnLateral _ = ()

View File

@ -1,6 +1,7 @@
{-# LANGUAGE CPP #-}
{-# language DerivingStrategies, GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
@ -24,6 +25,7 @@
-- tracker so we can safely support it.
module Database.Esqueleto.Internal.Internal where
import Data.Kind (Constraint)
import Control.Applicative ((<|>))
import Data.Coerce (Coercible, coerce)
import Control.Arrow (first, (***))
@ -534,12 +536,12 @@ subSelectForeign expr foreignKey k =
subSelectUnsafe :: (SqlSelect (SqlExpr a) a, PersistField a) => SqlQuery (SqlExpr a) -> SqlExpr a
subSelectUnsafe = sub SELECT
-- | Project a field of an entity.
(^.) :: forall typ val . (PersistEntity val, PersistField typ)
(^.) :: (PersistEntity val, PersistField typ)
=> SqlExpr (Entity val)
-> EntityField val typ
-> SqlExpr typ
ERaw m f ^. field
(ERaw m f) ^. field
| isIdField field = idFieldValue
| Just alias <- sqlExprMetaAlias m =
ERaw noMeta $ \_ info ->
@ -563,7 +565,10 @@ ERaw m f ^. field
\p info -> (parensM p $ uncommas $ dot info <$> idFields, [])
ed = entityDef $ getEntityVal (Proxy :: Proxy (SqlExpr (Entity val)))
getProxy :: EntityField ent val -> Proxy (SqlExpr (Entity ent))
getProxy = const Proxy
ed = entityDef $ getEntityVal $ getProxy field
dot info fieldDef =
sourceIdent info <> "." <> fieldIdent
@ -1136,6 +1141,13 @@ data SomeValue where
class ToSomeValues a where
toSomeValues :: a -> [SomeValue]
instance {-# INCOHERENT #-} PersistField a => ToSomeValues (SqlExpr a) where
toSomeValues a = [SomeValue a]
instance PersistEntity a => ToSomeValues (SqlExpr (Entity a)) where
toSomeValues a = [SomeValue $ a ^. persistIdField]
instance PersistEntity a => ToSomeValues (SqlExpr (Maybe (Entity a))) where
toSomeValues a = [SomeValue $ a ?. persistIdField]
instance
( ToSomeValues a
, ToSomeValues b
@ -2077,8 +2089,6 @@ parensM Parens = parens
data OrderByType = ASC | DESC
instance ToSomeValues (SqlExpr a) where
toSomeValues a = [SomeValue a]
fieldName
:: (PersistEntity val, PersistField typ)
@ -3042,13 +3052,8 @@ instance (SqlSelect a ra, SqlSelect b rb) => SqlSelect (a, b) (ra, rb) where
]
sqlSelectColCount = uncurry (+) . (sqlSelectColCount *** sqlSelectColCount) . fromTupleP
sqlSelectProcessRow p =
let x = getType processRow
getType :: SqlSelect a r => (z -> Either y (r,x)) -> Proxy a
getType = const Proxy
colCountFst = sqlSelectColCount x
(fstP, sndP) = fromTupleP p
let (fstP, sndP) = fromTupleP p
colCountFst = sqlSelectColCount fstP
processRow row =
let (rowFst, rowSnd) = splitAt colCountFst row
in (,) <$> sqlSelectProcessRow fstP rowFst

View File

@ -62,8 +62,7 @@ module Common.Test
, Key(..)
) where
import Control.Monad (forM_, replicateM,
replicateM_, void)
import Control.Monad (forM_, replicateM, replicateM_, void)
import Control.Monad.Catch (MonadCatch)
import Control.Monad.Reader (ask)
import Data.Either
@ -72,22 +71,20 @@ import Data.Time
import Control.Monad.Fail (MonadFail)
#endif
import Control.Monad.IO.Class (MonadIO(liftIO))
import Control.Monad.Logger (MonadLogger (..),
NoLoggingT,
runNoLoggingT)
import Control.Monad.Logger (MonadLogger(..), NoLoggingT, runNoLoggingT)
import Control.Monad.Trans.Reader (ReaderT)
import qualified Data.Attoparsec.Text as AP
import Data.Char (toLower, toUpper)
import Data.Monoid ((<>))
import Database.Esqueleto
import Database.Esqueleto.Experimental hiding (from, on)
import qualified Database.Esqueleto.Experimental as Experimental
import Database.Esqueleto.Experimental hiding
(countRows_, from, groupBy, on, sum_, (?.), (^.))
import qualified Database.Esqueleto.Experimental as EX
import Database.Persist.TH
import Test.Hspec
import UnliftIO
import Data.Conduit (ConduitT, runConduit,
(.|))
import Data.Conduit (ConduitT, runConduit, (.|))
import qualified Data.Conduit.List as CL
import qualified Data.List as L
import qualified Data.Set as S
@ -458,10 +455,10 @@ testSubSelect run = do
eres <- try $ run $ do
setup
bad <- select $
from $ \n -> do
from $ \(n :: SqlExpr (Entity Numbers)) -> do
pure $ (,) (n ^. NumbersInt) $
subSelectUnsafe $
from $ \n' -> do
from $ \(n' :: SqlExpr (Entity Numbers)) -> do
pure (just (n' ^. NumbersDouble))
good <- select $
from $ \n -> do
@ -484,10 +481,10 @@ testSubSelect run = do
eres <- try $ run $ do
setup
select $
from $ \n -> do
from $ \(n :: SqlExpr (Entity Numbers)) -> do
pure $ (,) (n ^. NumbersInt) $
subSelectUnsafe $
from $ \n' -> do
from $ \(n' :: SqlExpr (Entity Numbers)) -> do
where_ $ val False
pure (n' ^. NumbersDouble)
case eres of
@ -501,14 +498,14 @@ testSelectSource run = do
describe "selectSource" $ do
it "works for a simple example" $ run $ do
let query = selectSource $
Experimental.from $ Table @Person
EX.from $ Table @Person
p1e <- insert' p1
ret <- runConduit $ query .| CL.consume
liftIO $ ret `shouldBe` [ p1e ]
it "can run a query many times" $ run $ do
let query = selectSource $
Experimental.from $ Table @Person
EX.from $ Table @Person
p1e <- insert' p1
ret0 <- runConduit $ query .| CL.consume
ret1 <- runConduit $ query .| CL.consume
@ -537,7 +534,7 @@ testSelectFrom run = do
describe "select/from" $ do
it "works for a simple example" $ run $ do
p1e <- insert' p1
ret <- select $ Experimental.from $ Table @Person
ret <- select $ EX.from $ Table @Person
liftIO $ ret `shouldBe` [ p1e ]
it "works for a simple self-join (one entity)" $ run $ do
@ -545,7 +542,7 @@ testSelectFrom run = do
ret <-
select $ do
person1 :& person2 <-
Experimental.from $ Table @Person
EX.from $ Table @Person
`crossJoin` Table @Person
return (person1, person2)
liftIO $ ret `shouldBe` [ (p1e, p1e) ]
@ -556,7 +553,7 @@ testSelectFrom run = do
ret <-
select $ do
person1 :& person2 <-
Experimental.from $ Table @Person
EX.from $ Table @Person
`crossJoin` Table @Person
return (person1, person2)
liftIO $
@ -672,7 +669,7 @@ testSelectFrom run = do
number = 101
Right thePk = keyFromValues [toPersistValue number]
fcPk <- insert fc
[Entity _ ret] <- select $ Experimental.from $ Table @Frontcover
[Entity _ ret] <- select $ EX.from $ Table @Frontcover
liftIO $ do
ret `shouldBe` fc
fcPk `shouldBe` thePk
@ -879,9 +876,9 @@ testSelectSubQuery run = describe "select subquery" $ do
it "works" $ run $ do
_ <- insert' p1
let q = do
p <- Experimental.from $ Table @Person
p <- EX.from $ Table @Person
return ( p ^. PersonName, p ^. PersonAge)
ret <- select $ Experimental.from q
ret <- select $ EX.from q
liftIO $ ret `shouldBe` [ (Value $ personName p1, Value $ personAge p1) ]
it "supports sub-selecting Maybe entities" $ run $ do
@ -890,11 +887,11 @@ testSelectSubQuery run = describe "select subquery" $ do
l1Deeds <- mapM (\k -> insert' $ Deed k (entityKey l1e)) (map show [1..3 :: Int])
let l1WithDeeds = do d <- l1Deeds
pure (l1e, Just d)
let q = Experimental.from $ do
let q = EX.from $ do
(lords :& deeds) <-
Experimental.from $ Table @Lord
EX.from $ Table @Lord
`LeftOuterJoin` Table @Deed
`Experimental.on` (\(l :& d) -> just (l ^. LordId) ==. d ?. DeedOwnerId)
`EX.on` (\(l :& d) -> just (l ^. LordId) ==. d ?. DeedOwnerId)
pure (lords, deeds)
ret <- select q
@ -905,8 +902,8 @@ testSelectSubQuery run = describe "select subquery" $ do
_ <- insert' p3
let q = do
(name, age) <-
Experimental.from $ SubQuery $ do
p <- Experimental.from $ Table @Person
EX.from $ SubQuery $ do
p <- EX.from $ Table @Person
return ( p ^. PersonName, p ^. PersonAge)
orderBy [ asc age ]
pure name
@ -920,13 +917,13 @@ testSelectSubQuery run = describe "select subquery" $ do
mapM_ (\k -> insert $ Deed k l3k) (map show [4..10 :: Int])
let q = do
(lord :& deed) <- Experimental.from $ Table @Lord
(lord :& deed) <- EX.from $ Table @Lord
`InnerJoin` Table @Deed
`Experimental.on` (\(lord :& deed) ->
`EX.on` (\(lord :& deed) ->
lord ^. LordId ==. deed ^. DeedOwnerId)
return (lord ^. LordId, deed ^. DeedId)
q' = do
(lordId, deedId) <- Experimental.from $ SubQuery q
(lordId, deedId) <- EX.from $ SubQuery q
groupBy (lordId)
return (lordId, count deedId)
(ret :: [(Value (Key Lord), Value Int)]) <- select q'
@ -941,15 +938,15 @@ testSelectSubQuery run = describe "select subquery" $ do
mapM_ (\k -> insert $ Deed k l3k) (map show [4..10 :: Int])
let q = do
(lord :& deed) <- Experimental.from $ Table @Lord
(lord :& deed) <- EX.from $ Table @Lord
`InnerJoin` Table @Deed
`Experimental.on` (\(lord :& deed) ->
`EX.on` (\(lord :& deed) ->
lord ^. LordId ==. deed ^. DeedOwnerId)
groupBy (lord ^. LordId)
groupBy (lord)
return (lord ^. LordId, count (deed ^. DeedId))
(ret :: [(Value Int)]) <- select $ do
(lordId, deedCount) <- Experimental.from $ SubQuery q
(lordId, deedCount) <- EX.from $ SubQuery q
where_ $ deedCount >. val (3 :: Int)
return (count lordId)
@ -962,9 +959,9 @@ testSelectSubQuery run = describe "select subquery" $ do
mapM_ (\k -> insert $ Deed k l3k) (map show [4..10 :: Int])
let q = do
(lord :& deed) <- Experimental.from $ Table @Lord
`InnerJoin` (Experimental.from $ Table @Deed)
`Experimental.on` (\(lord :& deed) ->
(lord :& deed) <- EX.from $ Table @Lord
`InnerJoin` (EX.from $ Table @Deed)
`EX.on` (\(lord :& deed) ->
lord ^. LordId ==. deed ^. DeedOwnerId)
groupBy (lord ^. LordId)
return (lord ^. LordId, count (deed ^. DeedId))
@ -976,11 +973,11 @@ testSelectSubQuery run = describe "select subquery" $ do
l1k <- insert l1
l3k <- insert l3
let q = do
(lord :& (_, dogCounts)) <- Experimental.from $ Table @Lord
(lord :& (_, dogCounts)) <- EX.from $ Table @Lord
`LeftOuterJoin` do
lord <- Experimental.from $ Table @Lord
lord <- EX.from $ Table @Lord
pure (lord ^. LordId, lord ^. LordDogs)
`Experimental.on` (\(lord :& (lordId, _)) ->
`EX.on` (\(lord :& (lordId, _)) ->
just (lord ^. LordId) ==. lordId)
groupBy (lord ^. LordId, dogCounts)
return (lord ^. LordId, dogCounts)
@ -990,19 +987,19 @@ testSelectSubQuery run = describe "select subquery" $ do
it "unions" $ run $ do
_ <- insert p1
_ <- insert p2
let q = Experimental.from $
let q = EX.from $
(do
p <- Experimental.from $ Table @Person
p <- EX.from $ Table @Person
where_ $ not_ $ isNothing $ p ^. PersonAge
return (p ^. PersonName))
`union_`
(do
p <- Experimental.from $ Table @Person
p <- EX.from $ Table @Person
where_ $ isNothing $ p ^. PersonAge
return (p ^. PersonName))
`union_`
(do
p <- Experimental.from $ Table @Person
p <- EX.from $ Table @Person
where_ $ isNothing $ p ^. PersonAge
return (p ^. PersonName))
names <- select q
@ -2350,7 +2347,7 @@ testExperimentalFrom run = do
_ <- insert' p2
p3e <- insert' p3
peopleWithAges <- select $ do
people <- Experimental.from $ Table @Person
people <- EX.from $ Table @Person
where_ $ not_ $ isNothing $ people ^. PersonAge
return people
liftIO $ peopleWithAges `shouldMatchList` [p1e, p3e]
@ -2363,9 +2360,9 @@ testExperimentalFrom run = do
d2e <- insert' $ Deed "2" (entityKey l1e)
lordDeeds <- select $ do
(lords :& deeds) <-
Experimental.from $ Table @Lord
EX.from $ Table @Lord
`InnerJoin` Table @Deed
`Experimental.on` (\(l :& d) -> l ^. LordId ==. d ^. DeedOwnerId)
`EX.on` (\(l :& d) -> l ^. LordId ==. d ^. DeedOwnerId)
pure (lords, deeds)
liftIO $ lordDeeds `shouldMatchList` [ (l1e, d1e)
, (l1e, d2e)
@ -2379,9 +2376,9 @@ testExperimentalFrom run = do
d2e <- insert' $ Deed "2" (entityKey l1e)
lordDeeds <- select $ do
(lords :& deeds) <-
Experimental.from $ Table @Lord
EX.from $ Table @Lord
`LeftOuterJoin` Table @Deed
`Experimental.on` (\(l :& d) -> just (l ^. LordId) ==. d ?. DeedOwnerId)
`EX.on` (\(l :& d) -> just (l ^. LordId) ==. d ?. DeedOwnerId)
pure (lords, deeds)
liftIO $ lordDeeds `shouldMatchList` [ (l1e, Just d1e)
@ -2393,8 +2390,8 @@ testExperimentalFrom run = do
insert_ l1
insert_ l2
insert_ l3
delete $ void $ Experimental.from $ Table @Lord
lords <- select $ Experimental.from $ Table @Lord
delete $ void $ EX.from $ Table @Lord
lords <- select $ EX.from $ Table @Lord
liftIO $ lords `shouldMatchList` []
it "supports implicit cross joins" $ do
@ -2402,11 +2399,11 @@ testExperimentalFrom run = do
l1e <- insert' l1
l2e <- insert' l2
ret <- select $ do
lords1 <- Experimental.from $ Table @Lord
lords2 <- Experimental.from $ Table @Lord
lords1 <- EX.from $ Table @Lord
lords2 <- EX.from $ Table @Lord
pure (lords1, lords2)
ret2 <- select $ do
(lords1 :& lords2) <- Experimental.from $ Table @Lord `CrossJoin` Table @Lord
(lords1 :& lords2) <- EX.from $ Table @Lord `CrossJoin` Table @Lord
pure (lords1,lords2)
liftIO $ ret `shouldMatchList` ret2
liftIO $ ret `shouldMatchList` [ (l1e, l1e)
@ -2420,12 +2417,12 @@ testExperimentalFrom run = do
run $ void $ do
let q = do
(persons :& profiles :& posts) <-
Experimental.from $ Table @Person
EX.from $ Table @Person
`InnerJoin` Table @Profile
`Experimental.on` (\(people :& profiles) ->
`EX.on` (\(people :& profiles) ->
people ^. PersonId ==. profiles ^. ProfilePerson)
`LeftOuterJoin` Table @BlogPost
`Experimental.on` (\(people :& _ :& posts) ->
`EX.on` (\(people :& _ :& posts) ->
just (people ^. PersonId) ==. posts ?. BlogPostAuthorId)
pure (persons, posts, profiles)
--error . show =<< renderQuerySelect q
@ -2437,7 +2434,7 @@ testExperimentalFrom run = do
insert_ p3
-- Pretend this isnt all posts
upperNames <- select $ do
author <- Experimental.from $ SelectQuery $ Experimental.from $ Table @Person
author <- EX.from $ SelectQuery $ EX.from $ Table @Person
pure $ upper_ $ author ^. PersonName
liftIO $ upperNames `shouldMatchList` [ Value "JOHN"