From 161914ddbc1f221dfa99a2f502e16fc239d585a7 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Wed, 2 Aug 2017 13:48:21 +0100 Subject: [PATCH 1/9] Added now_ function. Works in the tests (NOTE: It doesn not work in SQLite). Need to see if there is an answer for constraining the in to only know about time --- esqueleto.cabal | 3 +- src/Database/Esqueleto.hs | 2 +- src/Database/Esqueleto/Internal/Language.hs | 1 + src/Database/Esqueleto/Internal/Sql.hs | 1 + test/Test.hs | 38 ++++++++++++--------- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/esqueleto.cabal b/esqueleto.cabal index 22a4ed0..628eb3f 100644 --- a/esqueleto.cabal +++ b/esqueleto.cabal @@ -48,7 +48,7 @@ source-repository head Flag postgresql Description: test postgresql. default is to test sqlite. - Default: False + Default: True Flag mysql Description: test MySQL/MariaDB. default is to test sqlite. @@ -101,6 +101,7 @@ test-suite test , persistent-template >= 2.1 , monad-control , monad-logger >= 0.3 + , time >= 1.6.0.1 -- This library , esqueleto diff --git a/src/Database/Esqueleto.hs b/src/Database/Esqueleto.hs index 495e1a6..b3f61be 100644 --- a/src/Database/Esqueleto.hs +++ b/src/Database/Esqueleto.hs @@ -45,7 +45,7 @@ module Database.Esqueleto , countRows, count, countDistinct , not_, (==.), (>=.), (>.), (<=.), (<.), (!=.), (&&.), (||.) , (+.), (-.), (/.), (*.) - , random_, round_, ceiling_, floor_ + , random_, now_, round_, ceiling_, floor_ , min_, max_, sum_, avg_, castNum, castNumM , coalesce, coalesceDefault , lower_, like, ilike, (%), concat_, (++.), castString diff --git a/src/Database/Esqueleto/Internal/Language.hs b/src/Database/Esqueleto/Internal/Language.hs index b3188da..261246c 100644 --- a/src/Database/Esqueleto/Internal/Language.hs +++ b/src/Database/Esqueleto/Internal/Language.hs @@ -352,6 +352,7 @@ class (Functor query, Applicative query, Monad query) => random_ :: (PersistField a, Num a) => expr (Value a) + now_ :: (PersistField a) => expr (Value a) round_ :: (PersistField a, Num a, PersistField b, Num b) => expr (Value a) -> expr (Value b) ceiling_ :: (PersistField a, Num a, PersistField b, Num b) => expr (Value a) -> expr (Value b) floor_ :: (PersistField a, Num a, PersistField b, Num b) => expr (Value a) -> expr (Value b) diff --git a/src/Database/Esqueleto/Internal/Sql.hs b/src/Database/Esqueleto/Internal/Sql.hs index 02861ab..b5bd9a3 100644 --- a/src/Database/Esqueleto/Internal/Sql.hs +++ b/src/Database/Esqueleto/Internal/Sql.hs @@ -504,6 +504,7 @@ instance Esqueleto SqlQuery SqlExpr SqlBackend where (*.) = unsafeSqlBinOp " * " random_ = unsafeSqlValue "RANDOM()" + now_ = unsafeSqlValue "NOW()" round_ = unsafeSqlFunction "ROUND" ceiling_ = unsafeSqlFunction "CEILING" floor_ = unsafeSqlFunction "FLOOR" diff --git a/test/Test.hs b/test/Test.hs index 2eadc53..5e32d1b 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -51,6 +51,7 @@ import qualified Data.List as L import qualified Data.Set as S import qualified Data.Text.Lazy.Builder as TLB import qualified Database.Esqueleto.Internal.Sql as EI +import Data.Time.Clock (UTCTime) -- Test schema @@ -644,6 +645,11 @@ main = do #endif return () + it "works with now_" $ + run $ do + _ <- select $ return (now_ :: SqlExpr (Value UTCTime)) + return () + it "works with round_" $ run $ do ret <- select $ return $ round_ (val (16.2 :: Double)) @@ -1126,22 +1132,22 @@ main = do , (Entity p1k p1, Value 3) , (Entity p3k p3, Value 7) ] - it "GROUP BY works with COUNT and InnerJoin" $ - run $ do - l1k <- insert l1 - l2k <- insert l2 - l3k <- insert l3 - mapM_ (\k -> insert $ Deed k l1k) (map show [1..3]) - - mapM_ (\k -> insert $ Deed k l3k) (map show [4..10]) - - (ret :: [(Value (Key Lord), Value Int)]) <- select $ from $ - \ ( lord `InnerJoin` deed ) -> do - on $ lord ^. LordId ==. deed ^. DeedOwnerId - groupBy (lord ^. LordId) - return (lord ^. LordId, count $ deed ^. DeedId) - liftIO $ ret `shouldBe` [ (Value l3k, Value 7) - , (Value l1k, Value 3) ] + -- it "GROUP BY works with COUNT and InnerJoin" $ + -- run $ do + -- l1k <- insert l1 + -- l2k <- insert l2 + -- l3k <- insert l3 + -- mapM_ (\k -> insert $ Deed k l1k) (map show [1..3]) + -- + -- mapM_ (\k -> insert $ Deed k l3k) (map show [4..10]) + -- + -- (ret :: [(Value (Key Lord), Value Int)]) <- select $ from $ + -- \ ( lord `InnerJoin` deed ) -> do + -- on $ lord ^. LordId ==. deed ^. DeedOwnerId + -- groupBy (lord ^. LordId) + -- return (lord ^. LordId, count $ deed ^. DeedId) + -- liftIO $ ret `shouldBe` [ (Value l3k, Value 7) + -- , (Value l1k, Value 3) ] it "GROUP BY works with HAVING" $ run $ do From a4ebae23453b4958fcc9f90909b0de3ff43e780b Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Wed, 2 Aug 2017 16:34:46 +0100 Subject: [PATCH 2/9] Use type constraint to constrain a to UTCTime --- esqueleto.cabal | 1 + src/Database/Esqueleto/Internal/Language.hs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/esqueleto.cabal b/esqueleto.cabal index 628eb3f..a477bfb 100644 --- a/esqueleto.cabal +++ b/esqueleto.cabal @@ -74,6 +74,7 @@ library , monad-logger , conduit >= 1.1 , resourcet >= 1.1 + , time >= 1.6.0.1 , blaze-html hs-source-dirs: src/ if impl(ghc >= 8.0) diff --git a/src/Database/Esqueleto/Internal/Language.hs b/src/Database/Esqueleto/Internal/Language.hs index 261246c..6536f3e 100644 --- a/src/Database/Esqueleto/Internal/Language.hs +++ b/src/Database/Esqueleto/Internal/Language.hs @@ -53,6 +53,7 @@ import Text.Blaze.Html (Html) import qualified Data.ByteString as B import qualified Data.Text as T import qualified Data.Text.Lazy as TL +import Data.Time.Clock (UTCTime) -- | Finally tagless representation of @esqueleto@'s EDSL. @@ -352,7 +353,7 @@ class (Functor query, Applicative query, Monad query) => random_ :: (PersistField a, Num a) => expr (Value a) - now_ :: (PersistField a) => expr (Value a) + now_ :: (PersistField a, a ~ UTCTime) => expr (Value a) round_ :: (PersistField a, Num a, PersistField b, Num b) => expr (Value a) -> expr (Value b) ceiling_ :: (PersistField a, Num a, PersistField b, Num b) => expr (Value a) -> expr (Value b) floor_ :: (PersistField a, Num a, PersistField b, Num b) => expr (Value a) -> expr (Value b) From 013dc19b15e1584b41e1e9000364219f2ba20ec2 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Thu, 3 Aug 2017 20:47:43 +0100 Subject: [PATCH 3/9] Accidentally committed True default for postgres --- esqueleto.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esqueleto.cabal b/esqueleto.cabal index a477bfb..15aa13f 100644 --- a/esqueleto.cabal +++ b/esqueleto.cabal @@ -48,7 +48,7 @@ source-repository head Flag postgresql Description: test postgresql. default is to test sqlite. - Default: True + Default: False Flag mysql Description: test MySQL/MariaDB. default is to test sqlite. From 8eab68a8d30aaab9338b8ed29a5186ed1e90949c Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Thu, 3 Aug 2017 20:48:42 +0100 Subject: [PATCH 4/9] Removed now from typeclass and moved it to PostgreSQL. Testing for DB clock against machine clock --- src/Database/Esqueleto.hs | 2 +- src/Database/Esqueleto/Internal/Language.hs | 2 -- src/Database/Esqueleto/Internal/Sql.hs | 1 - src/Database/Esqueleto/PostgreSQL.hs | 6 +++++- test/Test.hs | 18 ++++++++++++++---- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Database/Esqueleto.hs b/src/Database/Esqueleto.hs index b3f61be..495e1a6 100644 --- a/src/Database/Esqueleto.hs +++ b/src/Database/Esqueleto.hs @@ -45,7 +45,7 @@ module Database.Esqueleto , countRows, count, countDistinct , not_, (==.), (>=.), (>.), (<=.), (<.), (!=.), (&&.), (||.) , (+.), (-.), (/.), (*.) - , random_, now_, round_, ceiling_, floor_ + , random_, round_, ceiling_, floor_ , min_, max_, sum_, avg_, castNum, castNumM , coalesce, coalesceDefault , lower_, like, ilike, (%), concat_, (++.), castString diff --git a/src/Database/Esqueleto/Internal/Language.hs b/src/Database/Esqueleto/Internal/Language.hs index 6536f3e..b3188da 100644 --- a/src/Database/Esqueleto/Internal/Language.hs +++ b/src/Database/Esqueleto/Internal/Language.hs @@ -53,7 +53,6 @@ import Text.Blaze.Html (Html) import qualified Data.ByteString as B import qualified Data.Text as T import qualified Data.Text.Lazy as TL -import Data.Time.Clock (UTCTime) -- | Finally tagless representation of @esqueleto@'s EDSL. @@ -353,7 +352,6 @@ class (Functor query, Applicative query, Monad query) => random_ :: (PersistField a, Num a) => expr (Value a) - now_ :: (PersistField a, a ~ UTCTime) => expr (Value a) round_ :: (PersistField a, Num a, PersistField b, Num b) => expr (Value a) -> expr (Value b) ceiling_ :: (PersistField a, Num a, PersistField b, Num b) => expr (Value a) -> expr (Value b) floor_ :: (PersistField a, Num a, PersistField b, Num b) => expr (Value a) -> expr (Value b) diff --git a/src/Database/Esqueleto/Internal/Sql.hs b/src/Database/Esqueleto/Internal/Sql.hs index b5bd9a3..02861ab 100644 --- a/src/Database/Esqueleto/Internal/Sql.hs +++ b/src/Database/Esqueleto/Internal/Sql.hs @@ -504,7 +504,6 @@ instance Esqueleto SqlQuery SqlExpr SqlBackend where (*.) = unsafeSqlBinOp " * " random_ = unsafeSqlValue "RANDOM()" - now_ = unsafeSqlValue "NOW()" round_ = unsafeSqlFunction "ROUND" ceiling_ = unsafeSqlFunction "CEILING" floor_ = unsafeSqlFunction "FLOOR" diff --git a/src/Database/Esqueleto/PostgreSQL.hs b/src/Database/Esqueleto/PostgreSQL.hs index 0a31cc2..2e9f8d8 100644 --- a/src/Database/Esqueleto/PostgreSQL.hs +++ b/src/Database/Esqueleto/PostgreSQL.hs @@ -7,11 +7,12 @@ module Database.Esqueleto.PostgreSQL ( arrayAgg , stringAgg , chr + , now_ ) where import Database.Esqueleto.Internal.Language import Database.Esqueleto.Internal.Sql - +import Data.Time.Clock (UTCTime) -- | (@array_agg@) Concatenate input values, including @NULL@s, -- into an array. @@ -38,3 +39,6 @@ stringAgg expr delim = unsafeSqlFunction "string_agg" (expr, delim) -- /Since: 2.2.11/ chr :: SqlString s => SqlExpr (Value Int) -> SqlExpr (Value s) chr = unsafeSqlFunction "chr" + +now_ :: SqlExpr (Value UTCTime) +now_ = unsafeSqlValue "NOW()" diff --git a/test/Test.hs b/test/Test.hs index 5e32d1b..a0c2e93 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -51,7 +51,7 @@ import qualified Data.List as L import qualified Data.Set as S import qualified Data.Text.Lazy.Builder as TLB import qualified Database.Esqueleto.Internal.Sql as EI -import Data.Time.Clock (UTCTime) +import Data.Time.Clock (getCurrentTime, diffUTCTime, NominalDiffTime) -- Test schema @@ -645,10 +645,20 @@ main = do #endif return () - it "works with now_" $ + it "works with now" $ run $ do - _ <- select $ return (now_ :: SqlExpr (Value UTCTime)) - return () + nowDb <- select $ return EP.now_ + nowUtc <- liftIO getCurrentTime + let halfSecond = realToFrac 0.5 :: NominalDiffTime + + -- | Check the result is not null + liftIO $ nowDb `shouldSatisfy` (not . null) + + -- | Unpack the now value + let (Value now: _) = nowDb + + -- | Get the time diff and check it's less than half a second + liftIO $ diffUTCTime nowUtc now `shouldSatisfy` (< halfSecond) it "works with round_" $ run $ do From 274b6b2e7cb7110ff43f8a415e26ea61855b4965 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Thu, 3 Aug 2017 20:50:00 +0100 Subject: [PATCH 5/9] Uncomment test that was failing --- test/Test.hs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/Test.hs b/test/Test.hs index a0c2e93..b4e7386 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -1142,22 +1142,22 @@ main = do , (Entity p1k p1, Value 3) , (Entity p3k p3, Value 7) ] - -- it "GROUP BY works with COUNT and InnerJoin" $ - -- run $ do - -- l1k <- insert l1 - -- l2k <- insert l2 - -- l3k <- insert l3 - -- mapM_ (\k -> insert $ Deed k l1k) (map show [1..3]) - -- - -- mapM_ (\k -> insert $ Deed k l3k) (map show [4..10]) - -- - -- (ret :: [(Value (Key Lord), Value Int)]) <- select $ from $ - -- \ ( lord `InnerJoin` deed ) -> do - -- on $ lord ^. LordId ==. deed ^. DeedOwnerId - -- groupBy (lord ^. LordId) - -- return (lord ^. LordId, count $ deed ^. DeedId) - -- liftIO $ ret `shouldBe` [ (Value l3k, Value 7) - -- , (Value l1k, Value 3) ] + it "GROUP BY works with COUNT and InnerJoin" $ + run $ do + l1k <- insert l1 + l2k <- insert l2 + l3k <- insert l3 + mapM_ (\k -> insert $ Deed k l1k) (map show [1..3]) + + mapM_ (\k -> insert $ Deed k l3k) (map show [4..10]) + + (ret :: [(Value (Key Lord), Value Int)]) <- select $ from $ + \ ( lord `InnerJoin` deed ) -> do + on $ lord ^. LordId ==. deed ^. DeedOwnerId + groupBy (lord ^. LordId) + return (lord ^. LordId, count $ deed ^. DeedId) + liftIO $ ret `shouldBe` [ (Value l3k, Value 7) + , (Value l1k, Value 3) ] it "GROUP BY works with HAVING" $ run $ do From 03c95905819925c2b53f8425ffd049378ccd4404 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Fri, 4 Aug 2017 14:10:17 +0100 Subject: [PATCH 6/9] Added lower bound for package --- esqueleto.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esqueleto.cabal b/esqueleto.cabal index 15aa13f..8f5d320 100644 --- a/esqueleto.cabal +++ b/esqueleto.cabal @@ -74,7 +74,7 @@ library , monad-logger , conduit >= 1.1 , resourcet >= 1.1 - , time >= 1.6.0.1 + , time >= 1.5.0.1 && <= 1.6.0.1 , blaze-html hs-source-dirs: src/ if impl(ghc >= 8.0) @@ -102,7 +102,7 @@ test-suite test , persistent-template >= 2.1 , monad-control , monad-logger >= 0.3 - , time >= 1.6.0.1 + , time >= 1.5.0.1 && <= 1.6.0.1 -- This library , esqueleto From 406c1ef46cbde9a2a79bef27eed7a5961d1d3220 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Fri, 4 Aug 2017 14:10:24 +0100 Subject: [PATCH 7/9] Added cpp if to check for postgres flag Added deletion of lord and deed entries to allow for a test to pass --- test/Test.hs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/Test.hs b/test/Test.hs index b4e7386..0e09d45 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -645,6 +645,7 @@ main = do #endif return () +#if defined(WITH_POSTGRESQL) it "works with now" $ run $ do nowDb <- select $ return EP.now_ @@ -659,6 +660,7 @@ main = do -- | Get the time diff and check it's less than half a second liftIO $ diffUTCTime nowUtc now `shouldSatisfy` (< halfSecond) +#endif it "works with round_" $ run $ do @@ -1500,6 +1502,9 @@ cleanDB = do delete $ from $ \(_ :: SqlExpr (Entity Follow)) -> return () delete $ from $ \(_ :: SqlExpr (Entity Person)) -> return () + delete $ from $ \(_ :: SqlExpr (Entity Deed)) -> return () + delete $ from $ \(_ :: SqlExpr (Entity Lord)) -> return () + delete $ from $ \(_ :: SqlExpr (Entity CcList)) -> return () delete $ from $ \(_ :: SqlExpr (Entity ArticleTag)) -> return () From 2f5715470dc7b74dc256e1808acff53b4575cc84 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Fri, 4 Aug 2017 15:16:31 +0100 Subject: [PATCH 8/9] The order of the returned values weren't deterministic so switched to shouldMatchList --- test/Test.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/Test.hs b/test/Test.hs index 0e09d45..5616f71 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -1158,9 +1158,8 @@ main = do on $ lord ^. LordId ==. deed ^. DeedOwnerId groupBy (lord ^. LordId) return (lord ^. LordId, count $ deed ^. DeedId) - liftIO $ ret `shouldBe` [ (Value l3k, Value 7) - , (Value l1k, Value 3) ] - + liftIO $ ret `shouldMatchList` [ (Value l3k, Value 7) + , (Value l1k, Value 3) ] it "GROUP BY works with HAVING" $ run $ do p1k <- insert p1 From 672f6e88842714d3a0e33ff828f1aeae9fd91e88 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Fri, 4 Aug 2017 15:17:12 +0100 Subject: [PATCH 9/9] Bumped up time package upper bound to 1.8.0.2 --- esqueleto.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esqueleto.cabal b/esqueleto.cabal index 8f5d320..debe59f 100644 --- a/esqueleto.cabal +++ b/esqueleto.cabal @@ -74,7 +74,7 @@ library , monad-logger , conduit >= 1.1 , resourcet >= 1.1 - , time >= 1.5.0.1 && <= 1.6.0.1 + , time >= 1.5.0.1 && <= 1.8.0.2 , blaze-html hs-source-dirs: src/ if impl(ghc >= 8.0) @@ -102,7 +102,7 @@ test-suite test , persistent-template >= 2.1 , monad-control , monad-logger >= 0.3 - , time >= 1.5.0.1 && <= 1.6.0.1 + , time >= 1.5.0.1 && <= 1.8.0.2 -- This library , esqueleto