diff --git a/src/Database/Esqueleto.hs b/src/Database/Esqueleto.hs index c781dc7..35b4c6a 100644 --- a/src/Database/Esqueleto.hs +++ b/src/Database/Esqueleto.hs @@ -40,10 +40,11 @@ module Database.Esqueleto -- * @esqueleto@'s Language Esqueleto( where_, on, groupBy, orderBy, asc, desc, limit, offset, having , sub_select, sub_selectDistinct, (^.), (?.) - , val, isNothing, just, nothing, countRows, count, not_ + , val, isNothing, just, nothing, joinV, countRows, count, not_ , (==.), (>=.), (>.), (<=.), (<.), (!=.), (&&.), (||.) , (+.), (-.), (/.), (*.) - , random_, sum_, round_, ceiling_, floor_, avg_, min_, max_ + , random_, round_, ceiling_, floor_ + , min_, max_,_sum_, avg_, , like, (%), concat_, (++.) , subList_select, subList_selectDistinct, valList , in_, notIn, exists, notExists diff --git a/src/Database/Esqueleto/Internal/Language.hs b/src/Database/Esqueleto/Internal/Language.hs index f0d0c1a..ebc220d 100644 --- a/src/Database/Esqueleto/Internal/Language.hs +++ b/src/Database/Esqueleto/Internal/Language.hs @@ -208,6 +208,10 @@ class (Functor query, Applicative query, Monad query) => -- | @NULL@ value. nothing :: expr (Value (Maybe typ)) + -- | Join nested 'Maybe's in a 'Value' into one. This is useful when + -- calling aggregate functions on nullable fields. + joinV :: expr (Value (Maybe (Maybe typ))) -> expr (Value (Maybe typ)) + -- | @COUNT(*)@ value. countRows :: Num a => expr (Value a) @@ -236,10 +240,11 @@ class (Functor query, Applicative query, Monad query) => round_ :: (PersistField a, PersistField b) => expr (Value a) -> expr (Value b) ceiling_ :: (PersistField a, PersistField b) => expr (Value a) -> expr (Value b) floor_ :: (PersistField a, PersistField b) => expr (Value a) -> expr (Value b) - sum_ :: (PersistField a, PersistField b) => expr (Value a) -> expr (Value b) - min_ :: (PersistField a, PersistField b) => expr (Value a) -> expr (Value b) - max_ :: (PersistField a, PersistField b) => expr (Value a) -> expr (Value b) - avg_ :: (PersistField a, PersistField b) => expr (Value a) -> expr (Value b) + + sum_ :: (PersistField a) => expr (Value a) -> expr (Value (Maybe a)) + min_ :: (PersistField a) => expr (Value a) -> expr (Value (Maybe a)) + max_ :: (PersistField a) => expr (Value a) -> expr (Value (Maybe a)) + avg_ :: (PersistField a, PersistField b) => expr (Value a) -> expr (Value (Maybe b)) -- | @LIKE@ operator. like :: (PersistField s, IsString s) => expr (Value s) -> expr (Value s) -> expr (Value Bool) diff --git a/src/Database/Esqueleto/Internal/Sql.hs b/src/Database/Esqueleto/Internal/Sql.hs index 79f104b..910a8c7 100644 --- a/src/Database/Esqueleto/Internal/Sql.hs +++ b/src/Database/Esqueleto/Internal/Sql.hs @@ -313,6 +313,7 @@ instance Esqueleto SqlQuery SqlExpr SqlBackend where isNothing (ERaw p f) = ERaw Parens $ first ((<> " IS NULL") . parensM p) . f just (ERaw p f) = ERaw p f nothing = unsafeSqlValue "NULL" + joinV (ERaw p f) = ERaw p f countRows = unsafeSqlValue "COUNT(*)" count (ERaw _ f) = ERaw Never $ \conn -> let (b, vals) = f conn in ("COUNT" <> parens b, vals) @@ -1432,7 +1433,7 @@ to16 ((a,b),(c,d),(e,f),(g,h),(i,j),(k,l),(m,n),(o,p)) = (a,b,c,d,e,f,g,h,i,j,k, -- | Apply extra @SqlExpr Value@ arguments to a 'PersistField' constructor (<&>) :: SqlExpr (Insertion (a -> b)) -> SqlExpr (Value a) -> SqlExpr (Insertion b) -(EInsert _ f) <&> (ERaw _ g) = EInsert Proxy $ \x-> +(EInsert _ f) <&> (ERaw _ g) = EInsert Proxy $ \x-> let (fb, fv) = f x (gb, gv) = g x in (fb <> ", " <> gb, fv ++ gv) diff --git a/test/Test.hs b/test/Test.hs index 897d86a..3d92243 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -274,10 +274,10 @@ main = do _ <- insert' p4 ret <- select $ from $ \p-> - return $ sum_ (p ^. PersonAge) - liftIO $ ret `shouldBe` [ Value (36 + 17 + 17 :: Int) ] + return $ joinV $ sum_ (p ^. PersonAge) + liftIO $ ret `shouldBe` [ Value $ Just (36 + 17 + 17 :: Int) ] - it "works with sum_" $ + it "works with avg_" $ run $ do _ <- insert' p1 _ <- insert' p2 @@ -285,8 +285,8 @@ main = do _ <- insert' p4 ret <- select $ from $ \p-> - return $ avg_ (p ^. PersonAge) - liftIO $ ret `shouldBe` [ Value ((36 + 17 + 17) / 3 :: Double) ] + return $ joinV $ avg_ (p ^. PersonAge) + liftIO $ ret `shouldBe` [ Value $ Just ((36 + 17 + 17) / 3 :: Double) ] it "works with min_" $ run $ do @@ -296,8 +296,8 @@ main = do _ <- insert' p4 ret <- select $ from $ \p-> - return $ min_ (p ^. PersonAge) - liftIO $ ret `shouldBe` [ Value (17 :: Int) ] + return $ joinV $ min_ (p ^. PersonAge) + liftIO $ ret `shouldBe` [ Value $ Just (17 :: Int) ] it "works with max_" $ run $ do @@ -307,8 +307,8 @@ main = do _ <- insert' p4 ret <- select $ from $ \p-> - return $ max_ (p ^. PersonAge) - liftIO $ ret `shouldBe` [ Value (36 :: Int) ] + return $ joinV $ max_ (p ^. PersonAge) + liftIO $ ret `shouldBe` [ Value $ Just (36 :: Int) ] it "works with random_" $ run $ do