Add withNonNull helper to project nullable values

Guards against null values with a where_ call.
This commit is contained in:
Cole Brown 2017-10-17 12:52:31 -04:00
parent 91ea0b0fca
commit 501cf6b266
4 changed files with 25 additions and 1 deletions

View File

@ -41,7 +41,7 @@ module Database.Esqueleto
Esqueleto( where_, on, groupBy, orderBy, rand, asc, desc, limit, offset
, distinct, distinctOn, don, distinctOnOrderBy, having, locking
, sub_select, sub_selectDistinct, (^.), (?.)
, val, isNothing, just, nothing, joinV
, val, isNothing, just, nothing, joinV, withNonNull
, countRows, count, countDistinct
, not_, (==.), (>=.), (>.), (<=.), (<.), (!=.), (&&.), (||.)
, (+.), (-.), (/.), (*.)

View File

@ -299,6 +299,12 @@ class (Functor query, Applicative query, Monad query) =>
(^.) :: (PersistEntity val, PersistField typ) =>
expr (Entity val) -> EntityField val typ -> expr (Value typ)
-- | Project an expression that may be null, guarding against null cases.
withNonNull :: PersistField typ
=> expr (Value (Maybe typ))
-> (expr (Value typ) -> query a)
-> query a
-- | Project a field of an entity that may be null.
(?.) :: (PersistEntity val, PersistField typ) =>
expr (Maybe (Entity val)) -> EntityField val typ -> expr (Value (Maybe typ))

View File

@ -471,6 +471,14 @@ instance Esqueleto SqlQuery SqlExpr SqlBackend where
ed = entityDef $ getEntityVal (Proxy :: Proxy (SqlExpr (Entity val)))
Just pdef = entityPrimary ed
withNonNull :: PersistField typ
=> SqlExpr (Value (Maybe typ))
-> (SqlExpr (Value typ) -> SqlQuery a)
-> SqlQuery a
withNonNull field f = do
where_ $ not_ $ isNothing field
f $ veryUnsafeCoerceSqlExprValue field
EMaybe r ?. field = just (r ^. field)
val v = ERaw Never $ const ("?", [toPersistValue v])

View File

@ -25,6 +25,7 @@ import Control.Monad.Trans.Control (MonadBaseControl(..))
import Control.Monad.Trans.Reader (ReaderT)
import Data.Char (toLower, toUpper)
import Data.Monoid ((<>))
import qualified Data.Maybe as M
import Database.Esqueleto
#if defined (WITH_POSTGRESQL)
import Database.Persist.Postgresql (withPostgresqlConn)
@ -688,6 +689,15 @@ main = do
return p
liftIO $ ret `shouldBe` [ p1e ]
it "works with withNonNull" $
run $ do
ps <- traverse insert' [p1, p2, p3, p4, p5]
let ages = M.maybeToList =<< map (personAge . entityVal) ps
ret <- select $
from $ \p ->
withNonNull (p ^. PersonAge) return
liftIO $ ret `shouldBe` (map Value ages)
it "works for a many-to-many implicit join" $
run $ do
p1e@(Entity p1k _) <- insert' p1