diff --git a/src/Database/Esqueleto.hs b/src/Database/Esqueleto.hs index fc8b021..5900c5d 100644 --- a/src/Database/Esqueleto.hs +++ b/src/Database/Esqueleto.hs @@ -20,7 +20,7 @@ module Database.Esqueleto -- * @esqueleto@'s Language Esqueleto( where_, on, groupBy, orderBy, asc, desc, limit, offset , sub_select, sub_selectDistinct, (^.), (?.) - , val, isNothing, just, nothing, countRows, not_ + , val, isNothing, just, nothing, countRows, count, not_ , (==.), (>=.), (>.), (<=.), (<.), (!=.), (&&.), (||.) , (+.), (-.), (/.), (*.) , like, (%), concat_, (++.) diff --git a/src/Database/Esqueleto/Internal/Language.hs b/src/Database/Esqueleto/Internal/Language.hs index 7e54fd7..c6305b3 100644 --- a/src/Database/Esqueleto/Internal/Language.hs +++ b/src/Database/Esqueleto/Internal/Language.hs @@ -207,6 +207,9 @@ class (Functor query, Applicative query, Monad query) => -- | @COUNT(*)@ value. countRows :: Num a => expr (Value a) + -- | @COUNT@. + count :: (Num a) => expr (Value typ) -> expr (Value a) + not_ :: expr (Value Bool) -> expr (Value Bool) (==.) :: PersistField typ => expr (Value typ) -> expr (Value typ) -> expr (Value Bool) diff --git a/src/Database/Esqueleto/Internal/Sql.hs b/src/Database/Esqueleto/Internal/Sql.hs index 28ea389..8dd64f4 100644 --- a/src/Database/Esqueleto/Internal/Sql.hs +++ b/src/Database/Esqueleto/Internal/Sql.hs @@ -306,6 +306,8 @@ instance Esqueleto SqlQuery SqlExpr SqlBackend where just (ERaw p f) = ERaw p f nothing = unsafeSqlValue "NULL" countRows = unsafeSqlValue "COUNT(*)" + count (ERaw _ f) = ERaw Never $ \conn -> let (b, vals) = f conn + in ("COUNT" <> parens b, vals) not_ (ERaw p f) = ERaw Never $ \conn -> let (b, vals) = f conn in ("NOT " <> parensM p b, vals) diff --git a/test/Test.hs b/test/Test.hs index 532b434..bfde6c8 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -495,6 +495,24 @@ main = do , Entity p3k p3 { personAge = Just 7 } , Entity p2k p2 { personAge = Just 0 } ] + it "GROUP BY works with COUNT" $ + run $ do + p1k <- insert p1 + p2k <- insert p2 + p3k <- insert p3 + replicateM_ 3 (insert $ BlogPost "" p1k) + replicateM_ 7 (insert $ BlogPost "" p3k) + ret <- select $ + from $ \(p `LeftOuterJoin` b) -> do + on (p ^. PersonId ==. b ^. BlogPostAuthorId) + groupBy (p ^. PersonId) + let cnt = count (b ^. BlogPostId) + orderBy [ asc cnt ] + return (p, cnt) + liftIO $ ret `shouldBe` [ (Entity p2k p2, Value (0 :: Int)) + , (Entity p1k p1, Value 3) + , (Entity p3k p3, Value 7) ] + describe "lists of values" $ do it "IN works for valList" $ run $ do