From 83dc4a433c2256314906ba484f87d7d0581e3195 Mon Sep 17 00:00:00 2001 From: Danny B Date: Wed, 7 Aug 2013 15:27:29 +1100 Subject: [PATCH 1/3] Make max_, min_ and sum_ less general. --- src/Database/Esqueleto/Internal/Language.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Database/Esqueleto/Internal/Language.hs b/src/Database/Esqueleto/Internal/Language.hs index f0d0c1a..72e33fa 100644 --- a/src/Database/Esqueleto/Internal/Language.hs +++ b/src/Database/Esqueleto/Internal/Language.hs @@ -236,9 +236,9 @@ 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) + sum_ :: (PersistField a) => expr (Value a) -> expr (Value a) + min_ :: (PersistField a) => expr (Value a) -> expr (Value a) + max_ :: (PersistField a) => expr (Value a) -> expr (Value a) avg_ :: (PersistField a, PersistField b) => expr (Value a) -> expr (Value b) -- | @LIKE@ operator. From 068ec9b1aeafa547a121cbe8c1f11835665fa649 Mon Sep 17 00:00:00 2001 From: Danny B Date: Thu, 8 Aug 2013 15:51:31 +1100 Subject: [PATCH 2/3] Proper NULL handling in max_, min_ and sum_. - return expr (Value (Maybe a)) - add joinV to join nested Maybes in an expr --- src/Database/Esqueleto.hs | 2 +- src/Database/Esqueleto/Internal/Language.hs | 10 +++++++--- src/Database/Esqueleto/Internal/Sql.hs | 3 ++- test/Test.hs | 14 +++++++------- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Database/Esqueleto.hs b/src/Database/Esqueleto.hs index c781dc7..f1764a8 100644 --- a/src/Database/Esqueleto.hs +++ b/src/Database/Esqueleto.hs @@ -40,7 +40,7 @@ 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_ diff --git a/src/Database/Esqueleto/Internal/Language.hs b/src/Database/Esqueleto/Internal/Language.hs index 72e33fa..ea686b0 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,9 +240,9 @@ 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) => expr (Value a) -> expr (Value a) - min_ :: (PersistField a) => expr (Value a) -> expr (Value a) - max_ :: (PersistField a) => expr (Value a) -> expr (Value a) + 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 b) -- | @LIKE@ operator. 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..ecc3b1a 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 @@ -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 From e77e056f5b8ecfb5a257764268d092d23319a039 Mon Sep 17 00:00:00 2001 From: Danny B Date: Thu, 8 Aug 2013 16:46:01 +1100 Subject: [PATCH 3/3] Handle NULL result in avg_ too. --- src/Database/Esqueleto.hs | 3 ++- src/Database/Esqueleto/Internal/Language.hs | 3 ++- test/Test.hs | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Database/Esqueleto.hs b/src/Database/Esqueleto.hs index f1764a8..35b4c6a 100644 --- a/src/Database/Esqueleto.hs +++ b/src/Database/Esqueleto.hs @@ -43,7 +43,8 @@ module Database.Esqueleto , 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 ea686b0..ebc220d 100644 --- a/src/Database/Esqueleto/Internal/Language.hs +++ b/src/Database/Esqueleto/Internal/Language.hs @@ -240,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) => 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 b) + 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/test/Test.hs b/test/Test.hs index ecc3b1a..3d92243 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -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