diff --git a/src/Database/Esqueleto.hs b/src/Database/Esqueleto.hs index 495e1a6..4ad2da5 100644 --- a/src/Database/Esqueleto.hs +++ b/src/Database/Esqueleto.hs @@ -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_, (==.), (>=.), (>.), (<=.), (<.), (!=.), (&&.), (||.) , (+.), (-.), (/.), (*.) diff --git a/src/Database/Esqueleto/Internal/Language.hs b/src/Database/Esqueleto/Internal/Language.hs index 4226170..f3ed0d0 100644 --- a/src/Database/Esqueleto/Internal/Language.hs +++ b/src/Database/Esqueleto/Internal/Language.hs @@ -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)) diff --git a/src/Database/Esqueleto/Internal/Sql.hs b/src/Database/Esqueleto/Internal/Sql.hs index 02861ab..5600fdd 100644 --- a/src/Database/Esqueleto/Internal/Sql.hs +++ b/src/Database/Esqueleto/Internal/Sql.hs @@ -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]) diff --git a/test/Test.hs b/test/Test.hs index 347470f..14122a9 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -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