packages feed

groundhog 0.4.0.2 → 0.4.0.3

raw patch · 3 files changed

+47/−21 lines, 3 files

Files

Database/Groundhog/Generic.hs view
@@ -19,6 +19,7 @@   , HasConn   , runDb   , runDbConn+  , runDbConnNoTransaction   , withSavepoint   -- * Helper functions for defining *PersistValue instances   , primToPersistValue@@ -303,6 +304,17 @@ -- | Runs action within connection. It can handle a simple connection, a pool of them, etc. runDbConn :: (MonadBaseControl IO m, MonadIO m, ConnectionManager cm conn) => DbPersist conn (NoLoggingT m) a -> cm -> m a runDbConn f cm = runNoLoggingT (withConn (runDbPersist f) cm)++-- | It is similar to `runDbConn` but runs action without transaction. It can be useful if you use Groundhog within IO monad or in other cases when you cannot put `PersistBackend` instance into your monad stack.+--+-- @+-- flip withConn cm $ \\conn -> liftIO $ do+--   -- transaction is already opened by withConn at this point+--   someIOAction+--   getValuesFromIO $ \\value -> runDbConnNoTransaction (insert_ value) conn+-- @+runDbConnNoTransaction :: (MonadBaseControl IO m, MonadIO m, ConnectionManager cm conn) => DbPersist conn (NoLoggingT m) a -> cm -> m a+runDbConnNoTransaction f cm = runNoLoggingT (withConnNoTransaction (runDbPersist f) cm)  -- | It helps to run 'withConnSavepoint' within a monad. withSavepoint :: (HasConn m cm conn, SingleConnectionManager cm conn, Savepoint conn) => String -> m a -> m a
Database/Groundhog/Generic/PersistBackendHelpers.hs view
@@ -25,7 +25,7 @@ import Database.Groundhog.Generic.Sql  import Control.Monad (liftM, forM, (>=>))-import Data.Maybe (fromJust)+import Data.Maybe (catMaybes, fromJust) import Data.Monoid  {-# INLINABLE get #-}@@ -104,19 +104,18 @@ getBy :: forall m v u . (PersistBackend m, PersistEntity v, IsUniqueKey (Key v (Unique u)))       => (Utf8 -> Utf8) -- ^ escape       -> (forall a . Utf8 -> [DbType] -> [PersistValue] -> (RowPopper m -> m a) -> m a) -- ^ function to run query-      -> Utf8  -- ^ condition to compare with constant       -> Key v (Unique u)       -> m (Maybe v)-getBy escape queryFunc equalsTo (k :: Key v (Unique u)) = do+getBy escape queryFunc (k :: Key v (Unique u)) = do   uniques <- toPersistValues k   let e = entityDef (undefined :: v)       u = (undefined :: Key v (Unique u) -> u (UniqueMarker v)) k       uFields = renderChain escape (fieldChain u) []-      cond = intercalateS " AND " $ map (<> equalsTo) uFields+      RenderS cond vals = intercalateS " AND " $ mkUniqueCond uFields uniques       constr = head $ constructors e       fields = renderFields escape (constrParams constr)       query = "SELECT " <> fields <> " FROM " <> tableName escape e constr <> " WHERE " <> cond-  x <- queryFunc query (getConstructorTypes constr) (uniques []) id+  x <- queryFunc query (getConstructorTypes constr) (vals []) id   case x of     Just xs -> liftM (Just . fst) $ fromEntityPersistValues $ PersistInt64 0:xs     Nothing -> return Nothing@@ -225,20 +224,25 @@ insertByAll :: forall m v . (PersistBackend m, PersistEntity v)             => (Utf8 -> Utf8) -- ^ escape             -> (forall a . Utf8 -> [DbType] -> [PersistValue] -> (RowPopper m -> m a) -> m a) -- ^ function to run query-            -> Utf8 -- ^ condition to compare with constant+            -> Bool -- ^ allow multiple duplication of uniques with nulls             -> v -> m (Either (AutoKey v) (AutoKey v))-insertByAll escape queryFunc equalsTo v = do+insertByAll escape queryFunc manyNulls v = do   let e = entityDef v       proxy = undefined :: Proxy (PhantomDb m)       (constructorNum, uniques) = getUniques proxy v       constr = constructors e !! constructorNum       uniqueDefs = constrUniques constr-  if null uniques++      query = "SELECT " <> maybe "1" id (constrId escape constr) <> " FROM " <> tableName escape e constr <> " WHERE " <> cond+      conds = catMaybes $ zipWith (\u (_, uVals) -> checkNulls uVals $ intercalateS " AND " $ mkUniqueCond (f u) uVals) uniqueDefs uniques where+        f = foldr (flatten escape) [] . uniqueFields+      -- skip condition if any value is NULL. It allows to insert many values with duplicate unique key+      checkNulls uVals x = if manyNulls && any (== PersistNull) (uVals []) then Nothing else Just x+      RenderS cond vals = intercalateS " OR " conds+  if null conds     then liftM Right $ Core.insert v     else do-      let cond = intercalateS " OR " $ map (intercalateS " AND " . foldr (flatten $ \x -> escape x <> equalsTo) [] . uniqueFields) uniqueDefs-          query = "SELECT " <> maybe "1" id (constrId escape constr) <> " FROM " <> tableName escape e constr <> " WHERE " <> cond-      x <- queryFunc query [dbInt64] (foldr ((.) . snd) id uniques []) id+      x <- queryFunc query [dbInt64] (vals []) id       case x of         Nothing -> liftM Right $ Core.insert v         Just xs -> return $ Left $ fst $ fromPurePersistValues proxy xs@@ -270,22 +274,27 @@ insertBy :: forall m v u . (PersistBackend m, PersistEntity v, IsUniqueKey (Key v (Unique u)))          => (Utf8 -> Utf8)          -> (forall a . Utf8 -> [DbType] -> [PersistValue] -> (RowPopper m -> m a) -> m a)-         -> Utf8+         -> Bool          -> u (UniqueMarker v) -> v -> m (Either (AutoKey v) (AutoKey v))-insertBy escape queryFunc equalsTo u v = do+insertBy escape queryFunc manyNulls u v = do   uniques <- toPersistValues $ (extractUnique v `asTypeOf` ((undefined :: u (UniqueMarker v) -> Key v (Unique u)) u))   let e = entityDef v       proxy = undefined :: Proxy (PhantomDb m)-      uFields = (renderChain escape $ fieldChain u) []-      cond = intercalateS " AND " $ map (<> equalsTo) uFields+      uFields = renderChain escape (fieldChain u) []+      RenderS cond vals = intercalateS " AND " $ mkUniqueCond uFields uniques+      -- skip condition if any value is NULL. It allows to insert many values with duplicate unique key+      checkNulls uVals = manyNulls && any (== PersistNull) (uVals [])       -- this is safe because unique keys exist only for entities with one constructor       constr = head $ constructors e       query = "SELECT " <> maybe "1" id (constrId escape constr) <> " FROM " <> tableName escape e constr <> " WHERE " <> cond-  x <- queryFunc query [dbInt64] (uniques []) id-  case x of-    Nothing  -> liftM Right $ Core.insert v-    Just [k] -> return $ Left $ fst $ fromPurePersistValues proxy [k]-    Just xs  -> fail $ "unexpected query result: " ++ show xs+  if checkNulls uniques+    then liftM Right $ Core.insert v+    else do+      x <- queryFunc query [dbInt64] (vals []) id+      case x of+        Nothing  -> liftM Right $ Core.insert v+        Just [k] -> return $ Left $ fst $ fromPurePersistValues proxy [k]+        Just xs  -> fail $ "unexpected query result: " ++ show xs  getConstructorTypes :: ConstructorDef -> [DbType] getConstructorTypes = map snd . constrParams where@@ -303,3 +312,8 @@  dbInt64 :: DbType dbInt64 = DbTypePrimitive DbInt64 False Nothing Nothing++mkUniqueCond :: [Utf8] -> ([PersistValue] -> [PersistValue]) -> [RenderS db r]+mkUniqueCond u vals = zipWith f u (vals []) where+  f a PersistNull = RenderS (a <> " IS NULL") id+  f a x = RenderS (a <> "=?") (x:)
groundhog.cabal view
@@ -1,5 +1,5 @@ name:            groundhog-version:         0.4.0.2+version:         0.4.0.3 license:         BSD3 license-file:    LICENSE author:          Boris Lykah <lykahb@gmail.com>