This commit is contained in:
parsonsmatt 2019-10-29 14:07:53 -06:00
parent 6a8239ac93
commit 0c9b41a87d
2 changed files with 93 additions and 23 deletions

View File

@ -64,6 +64,7 @@ import qualified Data.Text.Lazy.Builder as TLB
import Data.Typeable (Typeable)
import Text.Blaze.Html (Html)
import qualified Debug.Trace as Debug
import Database.Esqueleto.Internal.ExprParser (TableAccess(..), parseOnExpr)
-- | (Internal) Start a 'from' query with an entity. 'from'
@ -1125,7 +1126,7 @@ data JoinKind =
| LeftOuterJoinKind -- ^ @LEFT OUTER JOIN@
| RightOuterJoinKind -- ^ @RIGHT OUTER JOIN@
| FullOuterJoinKind -- ^ @FULL OUTER JOIN@
deriving Eq
deriving (Eq, Show)
-- | (Internal) Functions that operate on types (that should be)
@ -1538,6 +1539,33 @@ data FromClause =
| FromJoin FromClause JoinKind FromClause (Maybe (SqlExpr (Value Bool)))
| OnClause (SqlExpr (Value Bool))
instance Show FromClause where
show fc = case fc of
FromStart i _ ->
"(FromStart " <> show i <> ")"
FromJoin lhs jk rhs mexpr ->
mconcat
[ "(FromJoin "
, show lhs
, " "
, show jk
, " "
, show rhs
, case mexpr of
Nothing -> ""
Just expr -> "(" <> render' expr <> ")"
, ")"
]
OnClause expr ->
"(OnClause " <> render' expr <> ")"
where
dummy = SqlBackend
{ connEscapeName = \(DBName x) -> x
}
render' = T.unpack . renderExpr dummy
-- | A part of a @SET@ clause.
newtype SetClause = SetClause (SqlExpr (Value ()))
@ -1551,6 +1579,7 @@ collectOnClauses
-> [FromClause]
-> Either (SqlExpr (Value Bool)) [FromClause]
collectOnClauses sqlBackend = go Set.empty []
-- . (\fcs -> Debug.trace (mappend "FromClauses: " (show fcs)) fcs)
where
go is [] (f@(FromStart i _) : fs) =
fmap (f:) (go (Set.insert i is) [] fs) -- fast path
@ -1599,39 +1628,53 @@ collectOnClauses sqlBackend = go Set.empty []
tryMatch idents expr fromClause =
case fromClause of
FromJoin l k r onClause ->
matchTable <|> matchR <|> matchC <|> matchL <|> matchPartial -- right to left
matchR <|> matchTable <|> matchL <|> matchPartial <|> matchC -- right to left
where
matchR = fmap (\r' -> FromJoin l k r' onClause)
<$> tryMatch idents expr r
matchL = fmap (\l' -> FromJoin l' k r onClause)
<$> tryMatch idents expr l
matchPartial = do
i1 <- findLeftmostIdent l
i2 <- findRightmostIdent r
-- Debug.traceM $ "matchPartial"
-- Debug.traceM $ "matchPartial: identsInOnClause: " <> show identsInOnClause
-- Debug.traceM $ "matchPartial: seen idents: " <> show idents
ll <- findLeftmostIdent l
-- Debug.traceM $ "matchPartial: ll: " <> show ll
rr <- findLeftmostIdent r
-- Debug.traceM $ "matchPartial: rr: " <> show rr
guard $
Set.isSubsetOf
identsInOnClause
(Set.fromList [i1, i2])
(Set.fromList [ll, rr])
-- Debug.traceM "matchPartial: passed subset check"
guard $ k /= CrossJoinKind
-- Debug.traceM "matchPartial: passed cross join kind check"
guard $ Maybe.isNothing onClause
-- Debug.traceM "matchPartial: passed isNothing check!"
pure (Set.fromList [] <> idents, FromJoin l k r (Just expr))
matchC =
case onClause of
Nothing
| "?" `T.isInfixOf` renderedExpr ->
-- Debug.trace ("matchC success" <> show identsInOnClause) $
return (idents, FromJoin l k r (Just expr))
| Set.null identsInOnClause ->
-- Debug.trace ("matchC success" <> show identsInOnClause) $
return (idents, FromJoin l k r (Just expr))
| otherwise ->
Nothing
Just _ ->
Nothing
matchTable = do
-- Debug.traceM $ "matchTable: " <> show identsInOnClause
i1 <- findLeftmostIdent r
i2 <- findRightmostIdent l
guard $ Set.fromList [i1, i2] `Set.isSubsetOf` identsInOnClause
guard $ k /= CrossJoinKind
guard $ Maybe.isNothing onClause
-- Debug.traceM $ "matchTable: succeed " <> show identsInOnClause
pure (Set.fromList [i1, i2] <> idents, FromJoin l k r (Just expr))
_ ->

View File

@ -101,6 +101,9 @@ share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistUpperCase|
Shoop
baz BazId
deriving Show Eq
Asdf
shoop ShoopId
deriving Show Eq
Person
name String
@ -116,6 +119,9 @@ share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistUpperCase|
body String
blog BlogPostId
deriving Eq Show
CommentReply
body String
comment CommentId
Profile
name String
person PersonId
@ -2172,7 +2178,7 @@ testOnClauseOrder run = describe "On Clause Ordering" $ do
describe "works with nested joins" $ do
it "unnested" $ do
run $ void $
select $
selectRethrowingQuery $
from $ \(f `InnerJoin` b `LeftOuterJoin` baz `InnerJoin` shoop) -> do
on $ f ^. FooId ==. b ^. BarQuux
on $ f ^. FooId ==. baz ^. BazBlargh
@ -2180,28 +2186,49 @@ testOnClauseOrder run = describe "On Clause Ordering" $ do
pure ( f ^. FooName)
it "leftmost nesting" $ do
run $ void $
select $
selectRethrowingQuery $
from $ \((f `InnerJoin` b) `LeftOuterJoin` baz `InnerJoin` shoop) -> do
on $ f ^. FooId ==. b ^. BarQuux
on $ f ^. FooId ==. baz ^. BazBlargh
on $ baz ^. BazId ==. shoop ^. ShoopBaz
pure ( f ^. FooName)
it "middle nesting" $ do
run $ void $
select $
from $ \(f `InnerJoin` (b `LeftOuterJoin` baz) `InnerJoin` shoop) -> do
on $ f ^. FooId ==. b ^. BarQuux
on $ f ^. FooId ==. baz ^. BazBlargh
on $ baz ^. BazId ==. shoop ^. ShoopBaz
pure ( f ^. FooName)
it "rightmost nesting" $ do
run $ void $
select $
from $ \(f `InnerJoin` b `LeftOuterJoin` (baz `InnerJoin` shoop)) -> do
on $ f ^. FooId ==. b ^. BarQuux
on $ f ^. FooId ==. baz ^. BazBlargh
on $ baz ^. BazId ==. shoop ^. ShoopBaz
pure ( f ^. FooName)
describe "middle nesting" $ do
it "direct association" $ do
run $ void $
selectRethrowingQuery $
from $ \(p `InnerJoin` (bp `LeftOuterJoin` c) `LeftOuterJoin` cr) -> do
on $ p ^. PersonId ==. bp ^. BlogPostAuthorId
on $ just (bp ^. BlogPostId) ==. c ?. CommentBlog
on $ c ?. CommentId ==. cr ?. CommentReplyComment
pure (p,bp,c,cr)
it "indirect association" $ do
run $ void $
selectRethrowingQuery $
from $ \(f `InnerJoin` b `LeftOuterJoin` (baz `InnerJoin` shoop) `InnerJoin` asdf) -> do
on $ f ^. FooId ==. b ^. BarQuux
on $ f ^. FooId ==. baz ^. BazBlargh
on $ baz ^. BazId ==. shoop ^. ShoopBaz
on $ asdf ^. AsdfShoop ==. shoop ^. ShoopId
pure (f ^. FooName)
describe "rightmost nesting" $ do
it "direct associations" $ do
run $ void $
selectRethrowingQuery $
from $ \(p `InnerJoin` bp `LeftOuterJoin` (c `LeftOuterJoin` cr)) -> do
on $ p ^. PersonId ==. bp ^. BlogPostAuthorId
on $ just (bp ^. BlogPostId) ==. c ?. CommentBlog
on $ c ?. CommentId ==. cr ?. CommentReplyComment
pure (p,bp,c,cr)
it "indirect association" $ do
run $ void $
selectRethrowingQuery $
from $ \(f `InnerJoin` b `LeftOuterJoin` (baz `InnerJoin` shoop)) -> do
on $ f ^. FooId ==. b ^. BarQuux
on $ f ^. FooId ==. baz ^. BazBlargh
on $ baz ^. BazId ==. shoop ^. ShoopBaz
pure (f ^. FooName)
listsEqualOn :: (Show a1, Eq a1) => [a2] -> [a2] -> (a2 -> a1) -> Expectation