Add between (#127)

* Update between so it works with SQL values

* Add support for composite keys in between clause

* Remove unused values from ERaw in construct

* Update unsafeSqlBinOp to handle composite keys and between to use >=., <=. and &&.

* Support composite keys in unsafeSqlBinOp correctly

* Updated changelog

* Update version number of between to 3.1.0
This commit is contained in:
Esteban Ibarra 2019-08-08 12:23:10 -05:00 committed by Matt Parsons
parent a452946f58
commit 5d8f5b53e6
5 changed files with 67 additions and 5 deletions

View File

@ -6,6 +6,8 @@ Unreleased
- @Vlix
- [#128](https://github.com/bitemyapp/esqueleto/pull/128): Added `Database.Esqueleto.PostgreSQL.JSON` module with JSON operators and `JSONB` data type.
- @ibarrae
- [#127](https://github.com/bitemyapp/esqueleto/pull/127): Added `between` and support for composite keys in `unsafeSqlBinOp`.
3.0.0
=======

View File

@ -44,7 +44,7 @@ module Database.Esqueleto
, val, isNothing, just, nothing, joinV, withNonNull
, countRows, count, countDistinct
, not_, (==.), (>=.), (>.), (<=.), (<.), (!=.), (&&.), (||.)
, (+.), (-.), (/.), (*.)
, between, (+.), (-.), (/.), (*.)
, random_, round_, ceiling_, floor_
, min_, max_, sum_, avg_, castNum, castNumM
, coalesce, coalesceDefault

View File

@ -455,6 +455,11 @@ not_ (ECompositeKey _) = throw (CompositeKeyErr NotError)
(*.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a)
(*.) = unsafeSqlBinOp " * "
-- | @BETWEEN@.
--
-- @since: 3.1.0
between :: PersistField a => SqlExpr (Value a) -> (SqlExpr (Value a), SqlExpr (Value a)) -> SqlExpr (Value Bool)
a `between` (b, c) = a >=. b &&. a <=. c
random_ :: (PersistField a, Num a) => SqlExpr (Value a)
random_ = unsafeSqlValue "RANDOM()"
@ -1242,7 +1247,6 @@ data CompositeKeyError =
| CombineInsertionError
| FoldHelpError
| SqlCaseError
| SqlBinOpError
| SqlCastAsError
| MakeOnClauseError
| MakeExcError
@ -1641,7 +1645,16 @@ unsafeSqlBinOp op (ERaw p1 f1) (ERaw p2 f2) = ERaw Parens f
(b2, vals2) = f2 info
in ( parensM p1 b1 <> op <> parensM p2 b2
, vals1 <> vals2 )
unsafeSqlBinOp _ _ _ = throw (CompositeKeyErr SqlBinOpError)
unsafeSqlBinOp op a b = unsafeSqlBinOp op (construct a) (construct b)
where construct :: SqlExpr (Value a) -> SqlExpr (Value a)
construct (ERaw p f) = ERaw Parens $ \info ->
let (b1, vals) = f info
build ("?", [PersistList vals']) =
(uncommas $ replicate (length vals') "?", vals')
build expr = expr
in build (parensM p b1, vals)
construct (ECompositeKey f) =
ERaw Parens $ \info -> (uncommas $ f info, mempty)
{-# INLINE unsafeSqlBinOp #-}
@ -2785,4 +2798,3 @@ insertSelect = void . insertSelectCount
insertSelectCount :: (MonadIO m, PersistEntity a) =>
SqlQuery (SqlExpr (Insertion a)) -> SqlWriteT m Int64
insertSelectCount = rawEsqueleto INSERT_INTO . fmap EInsertFinal

View File

@ -47,7 +47,7 @@ module Database.Esqueleto.Internal.Language
, val, isNothing, just, nothing, joinV, withNonNull
, countRows, count, countDistinct
, not_, (==.), (>=.), (>.), (<=.), (<.), (!=.), (&&.), (||.)
, (+.), (-.), (/.), (*.)
, between, (+.), (-.), (/.), (*.)
, random_, round_, ceiling_, floor_
, min_, max_, sum_, avg_, castNum, castNumM
, coalesce, coalesceDefault

View File

@ -630,6 +630,54 @@ testSelectWhere run = do
return p
liftIO $ ret `shouldBe` [ p3e ]
describe "when using between" $ do
it "works for a simple example with [uses just . val]" $
run $ do
p1e <- insert' p1
_ <- insert' p2
_ <- insert' p3
ret <- select $
from $ \p -> do
where_ ((p ^. PersonAge) `between` (just $ val 20, just $ val 40))
return p
liftIO $ ret `shouldBe` [ p1e ]
it "works for a proyected fields value" $
run $ do
_ <- insert' p1 >> insert' p2 >> insert' p3
ret <-
select $
from $ \p -> do
where_ $
just (p ^. PersonFavNum)
`between`
(p ^. PersonAge, p ^. PersonWeight)
liftIO $ ret `shouldBe` []
describe "when projecting composite keys" $ do
it "works when using composite keys with val" $
run $ do
insert_ $ Point 1 2 ""
ret <-
select $
from $ \p -> do
where_ $
p ^. PointId
`between`
( val $ PointKey 1 2
, val $ PointKey 5 6 )
liftIO $ ret `shouldBe` [()]
it "works when using ECompositeKey constructor" $
run $ do
insert_ $ Point 1 2 ""
ret <-
select $
from $ \p -> do
where_ $
p ^. PointId
`between`
( EI.ECompositeKey $ const ["3", "4"]
, EI.ECompositeKey $ const ["5", "6"] )
liftIO $ ret `shouldBe` []
it "works with avg_" $
run $ do
_ <- insert' p1