esqueleto-3.6.0.1: test/Common/Test/CTE.hs
{-# language TypeApplications #-}
module Common.Test.CTE where
import Common.Test.Models
import Common.Test.Import
import Database.Persist.TH
testCTE :: SpecDb
testCTE = describe "CTE" $ do
itDb "aliases new columns after a union whose left branch is a CTE reference" $ do
-- Mirror image of the test below: the left branch allocates fewer
-- idents than the right one.
let q :: SqlQuery (SqlExpr (Value Int), SqlExpr (Value Int), SqlExpr (Value Int))
q = do
bCte <- with $ do
b <- from $ table @B
pure (b ^. BK, b ^. BV)
(k, v, extra) <- from $ do
(k, v) <- from $
from bCte
`union_`
(do
b <- from $ table @B
pure (b ^. BK, b ^. BV))
pure (k, v, val (42 :: Int))
pure (k, v, extra)
insert_ $ B { bK = 1, bV = 3 }
ret <- select q
asserting $ do
ret `shouldMatchList`
[ (Value 1, Value 3, Value 42)
]
itDb "aliases a repeated reference in a subquery select list" $ do
-- Each occurrence needs its own output alias, or outer references
-- to the duplicated column name are ambiguous.
let q :: SqlQuery (SqlExpr (Value Int), SqlExpr (Value Int))
q = do
(a, b) <- from $ do
(x, _) <- from $ do
b <- from $ table @B
pure (b ^. BK, b ^. BV)
pure (x, x)
pure (a, b)
insert_ $ B { bK = 1, bV = 3 }
ret <- select q
asserting $ do
ret `shouldMatchList`
[ (Value 1, Value 1)
]
itDb "layered CTEs over subqueries with repeated references" $ do
let recentTransactions :: SqlQuery (SqlExpr (Value Int), SqlExpr (Value (Maybe Int)), SqlExpr (Value Int))
recentTransactions = do
b <- from $ table @B
limit 10
pure (b ^. BK, just (b ^. BV), b ^. BK)
getPrev tr = with $ do
(k, mr, _) <- tr
pure (k, mr, just (k +. val 1), just (k +. val 2))
filterRuns tr = do
prev <- getPrev tr
(k, mr, p1, _) <- from prev
where_ $ isNothing_ p1 ||. p1 !=. just (val 0)
pure (k, mr, k)
inferTime tr = do
let removed = from $ filterRuns tr
prev <- getPrev removed
(k, mr, _, p2) <- from prev
let newT = coalesceDefault [p2] k
pure (k, mr, newT)
q :: SqlQuery (SqlExpr (Value Int), SqlExpr (Value (Maybe Int)), SqlExpr (Value Int))
q = do
recent <- with $ inferTime (from recentTransactions)
sent <- with $ do
(k, mr, t) <- from recent
where_ $ k >=. val 0
pure (k, just (coalesceDefault [mr] (val 0)), t)
recRecs <- with $ distinct $ do
(_, mr, _) <- from sent
pure mr
recRecTx <- with $ do
(mr :& b) <- from $ recRecs
`innerJoin` table @B
`on` do
\(mr :& b) -> mr ==. just (b ^. BV)
pure (b ^. BK, mr, b ^. BV)
deduped <- with $ do
r@(k, _, _) <- from recRecTx
where_ $ not_ $ exists $ do
(k2, _, _) <- from sent
where_ $ k2 ==. k
pure r
(k, mr, t) <- from $ from sent `union_` from deduped
pure (k, mr, t)
insert_ $ B { bK = 1, bV = 3 }
ret <- select q
asserting $ do
ret `shouldMatchList`
[ (Value 1, Value (Just 3), Value 3)
]
itDb "can refer to the same CTE twice" $ do
let q :: SqlQuery (SqlExpr (Value Int), SqlExpr (Value Int))
q = do
bCte <- with $ do
b <- from $ table @B
pure b
a :& b1 :& b2 <- from $
table @A
`innerJoin` bCte
`on` do
\(a :& b) ->
a ^. AK ==. b ^. BK
`innerJoin` bCte
`on` do
\(a :& _ :& b2) ->
a ^. AK ==. b2 ^. BK
pure (a ^. AK, a ^. AV +. b1 ^. BV +. b2 ^. BV)
insert_ $ A { aK = 1, aV = 2 }
insert_ $ B { bK = 1, bV = 3 }
ret <- select q
asserting $ do
ret `shouldMatchList`
[ (Value 1, Value (2 + 3 + 3))
]
itDb "aliases new columns after a union with a CTE reference" $ do
-- The right branch allocates fewer idents than the left one; the
-- enclosing query must not reuse the left branch's idents for later
-- aliases (a variant of issue #299).
let q :: SqlQuery (SqlExpr (Value Int), SqlExpr (Value Int), SqlExpr (Value Int))
q = do
bCte <- with $ do
b <- from $ table @B
pure (b ^. BK, b ^. BV)
(k, v, extra) <- from $ do
(k, v) <- from $
(do
b <- from $ table @B
pure (b ^. BK, b ^. BV))
`union_` from bCte
pure (k, v, val (42 :: Int))
pure (k, v, extra)
insert_ $ B { bK = 1, bV = 3 }
ret <- select q
asserting $ do
ret `shouldMatchList`
[ (Value 1, Value 3, Value 42)
]