packages feed

esqueleto 1.2.2.1 → 1.2.3

raw patch · 4 files changed

+106/−14 lines, 4 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Database.Esqueleto: (<#) :: (a -> b) -> SqlExpr (Value a) -> SqlExpr (Insertion b)
+ Database.Esqueleto: (<&>) :: SqlExpr (Insertion (a -> b)) -> SqlExpr (Value a) -> SqlExpr (Insertion b)
+ Database.Esqueleto: insertSelect :: (MonadLogger m, MonadResourceBase m, SqlSelect (SqlExpr (Insertion a)) r, PersistEntity a) => SqlQuery (SqlExpr (Insertion a)) -> SqlPersistT m ()
+ Database.Esqueleto: insertSelectDistinct :: (MonadLogger m, MonadResourceBase m, SqlSelect (SqlExpr (Insertion a)) r, PersistEntity a) => SqlQuery (SqlExpr (Insertion a)) -> SqlPersistT m ()
+ Database.Esqueleto.Internal.Sql: (<#) :: (a -> b) -> SqlExpr (Value a) -> SqlExpr (Insertion b)
+ Database.Esqueleto.Internal.Sql: (<&>) :: SqlExpr (Insertion (a -> b)) -> SqlExpr (Value a) -> SqlExpr (Insertion b)
+ Database.Esqueleto.Internal.Sql: insertSelect :: (MonadLogger m, MonadResourceBase m, SqlSelect (SqlExpr (Insertion a)) r, PersistEntity a) => SqlQuery (SqlExpr (Insertion a)) -> SqlPersistT m ()
+ Database.Esqueleto.Internal.Sql: insertSelectDistinct :: (MonadLogger m, MonadResourceBase m, SqlSelect (SqlExpr (Insertion a)) r, PersistEntity a) => SqlQuery (SqlExpr (Insertion a)) -> SqlPersistT m ()
+ Database.Esqueleto.Internal.Sql: instance PersistEntity a => SqlSelect (SqlExpr (Insertion a)) (Insertion a)
- Database.Esqueleto.Internal.Sql: toRawSql :: SqlSelect a r => Mode -> Connection -> SqlQuery a -> (Builder, [PersistValue])
+ Database.Esqueleto.Internal.Sql: toRawSql :: SqlSelect a r => Mode -> QueryType a -> Connection -> SqlQuery a -> (Builder, [PersistValue])

Files

esqueleto.cabal view
@@ -1,5 +1,5 @@ name:                esqueleto-version:             1.2.2.1+version:             1.2.3 synopsis:            Bare bones, type-safe EDSL for SQL queries on persistent backends. homepage:            https://github.com/meteficha/esqueleto license:             BSD3@@ -33,9 +33,9 @@   compile-time errors---although it is possible to write   type-checked @esqueleto@ queries that fail at runtime.   .-  Currently only @SELECT@s are supported.  Not all SQL features-  are available, but most of them can be easily added (especially-  functions), so please open an issue or send a pull request if+  Currently, @SELECT@s, @UPDATE@s, @INSERT@s and @DELETE@s are supported.+  Not all SQL features are available, but most of them can be easily added+  (especially functions), so please open an issue or send a pull request if   you need anything that is not covered by @esqueleto@ on   <https://github.com/meteficha/esqueleto/>.   .
src/Database/Esqueleto.hs view
@@ -71,6 +71,10 @@   , deleteCount   , update   , updateCount+  , insertSelect+  , insertSelectDistinct+  , (<#)+  , (<&>)      -- * Helpers   , valkey@@ -318,6 +322,26 @@ --      from $ \\p -> do --      where_ (p ^. PersonAge <. just (val 14)) -- @+--+-- The results of queries can also be used for insertions.+-- In @SQL@, we might write the following, inserting a new blog+-- post for every user:+--+-- @+-- INSERT INTO BlogPost+-- SELECT ('Group Blog Post', id)+-- FROM Person+-- @+--+-- In @esqueleto@, we may write the same query above as:+--+-- @+-- insertSelect $ from $ \p->+--  return $ BlogPost \<# \"Group Blog Post\" \<&\> (p ^. PersonId)+-- @+--+-- Individual insertions can be performed through Persistent's+-- 'insert' function, reexported for convenience.   ----------------------------------------------------------------------@@ -325,9 +349,7 @@  -- $reexports ----- We re-export many symbols from @persistent@ for convenince,--- since @esqueleto@ currently does not provide a way of doing--- @INSERT@s:+-- We re-export many symbols from @persistent@ for convenince: -- --  * \"Store functions\" from "Database.Persist". --
src/Database/Esqueleto/Internal/Sql.hs view
@@ -35,6 +35,10 @@   , Mode(..)   , SqlSelect   , veryUnsafeCoerceSqlExprValue+  , insertSelectDistinct+  , insertSelect+  , (<#)+  , (<&>)   ) where  import Control.Applicative (Applicative(..), (<$>), (<$))@@ -224,9 +228,11 @@  ---------------------------------------------------------------------- +type Insertion = Proxy  -- | An expression on the SQL backend. data SqlExpr a where+  EInsert  :: Proxy a -> (Connection -> (TLB.Builder, [PersistValue])) -> SqlExpr (Insertion a)   EEntity  :: Ident -> SqlExpr (Entity val)   EMaybe   :: SqlExpr a -> SqlExpr (Maybe a)   ERaw     :: NeedParens -> (Connection -> (TLB.Builder, [PersistValue])) -> SqlExpr (Value a)@@ -371,7 +377,7 @@   where name = ERaw Never $ \conn -> (fieldName conn field, mempty)  sub :: PersistField a => Mode -> SqlQuery (SqlExpr (Value a)) -> SqlExpr (Value a)-sub mode query = ERaw Parens $ \conn -> toRawSql mode conn query+sub mode query = ERaw Parens $ \conn -> toRawSql mode pureQuery conn query  fromDBName :: Connection -> DBName -> TLB.Builder fromDBName conn = TLB.fromText . connEscapeName conn@@ -379,7 +385,7 @@ existsHelper :: SqlQuery () -> SqlExpr (Value a) existsHelper =   ERaw Parens .-  flip (toRawSql SELECT) .+  flip (toRawSql SELECT pureQuery) .   (>> return (val True :: SqlExpr (Value Bool)))  ifNotEmptyList :: SqlExpr (ValueList a) -> Bool -> SqlExpr (Value Bool) -> SqlExpr (Value Bool)@@ -488,7 +494,7 @@       run conn =         uncurry rawQuery $         first builderToText $-        toRawSql mode conn query+        toRawSql mode pureQuery conn query        massage = do         mrow <- C.await@@ -600,7 +606,7 @@   conn <- SqlPersistT R.ask   uncurry rawExecuteCount $     first builderToText $-    toRawSql mode conn query+    toRawSql mode pureQuery conn query   -- | Execute an @esqueleto@ @DELETE@ query inside @persistent@'s@@ -684,14 +690,15 @@ -- @esqueleto@, instead of manually using this function (which is -- possible but tedious), you may just turn on query logging of -- @persistent@.-toRawSql :: SqlSelect a r => Mode -> Connection -> SqlQuery a -> (TLB.Builder, [PersistValue])-toRawSql mode conn query =+toRawSql :: SqlSelect a r => Mode -> QueryType a -> Connection -> SqlQuery a -> (TLB.Builder, [PersistValue])+toRawSql mode qt conn query =   let (ret, SideData fromClauses setClauses whereClauses groupByClause havingClause orderByClauses limitClause) =         flip S.evalState initialIdentState $         W.runWriterT $         unQ query   in mconcat-      [ makeSelect  conn mode ret+      [ makeInsert  qt ret+      , makeSelect  conn mode ret       , makeFrom    conn mode fromClauses       , makeSet     conn setClauses       , makeWhere   conn whereClauses@@ -704,7 +711,22 @@ -- | (Internal) Mode of query being converted by 'toRawSql'. data Mode = SELECT | SELECT_DISTINCT | DELETE | UPDATE +newtype QueryType a = QueryType { unQueryType :: a -> TLB.Builder } +pureQuery :: QueryType a+pureQuery = QueryType (const mempty)++insertQuery :: PersistEntity a => QueryType (SqlExpr (Insertion a))+insertQuery = QueryType $ \(EInsert p _)->+    let def = entityDef p+        unName = TLB.fromText . unDBName+        fields = uncommas $ map (unName . fieldDB) (entityFields def)+        table = unName . entityDB . entityDef $ p+    in "INSERT INTO " <> table <> parens fields <> "\n"++makeInsert :: QueryType a -> a -> (TLB.Builder, [PersistValue])+makeInsert q a = (unQueryType q a, [])+ uncommas :: [TLB.Builder] -> TLB.Builder uncommas = mconcat . intersperse ", " . filter (/= mempty) @@ -840,6 +862,14 @@   sqlSelectProcessRow :: [PersistValue] -> Either T.Text r  +-- | You may return an insertion of some PersistEntity+instance PersistEntity a => SqlSelect (SqlExpr (Insertion a)) (Insertion a) where+  sqlSelectCols esc (EInsert _ f) = let (b, vals) = f esc+                                    in (b, vals)+  sqlSelectColCount = const 0+  sqlSelectProcessRow = const (Right Proxy)++ -- | Not useful for 'select', but used for 'update' and 'delete'. instance SqlSelect () () where   sqlSelectCols _ _ = ("1", [])@@ -1386,3 +1416,31 @@  to16 :: ((a,b),(c,d),(e,f),(g,h),(i,j),(k,l),(m,n),(o,p)) -> (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) to16 ((a,b),(c,d),(e,f),(g,h),(i,j),(k,l),(m,n),(o,p)) = (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)++-- | Apply a 'PersistField' constructor to @SqlExpr Value@ arguments+(<#) :: (a -> b) -> SqlExpr (Value a) -> SqlExpr (Insertion b)+(<#) _ (ERaw _ f) = EInsert Proxy f++-- | Apply extra @SqlExpr Value@ arguments to a 'PersistField' constructor+(<&>) :: SqlExpr (Insertion (a -> b)) -> SqlExpr (Value a) -> SqlExpr (Insertion b)+(EInsert _ f) <&> (ERaw _ g) = EInsert Proxy $ \x-> +  let (fb, fv) = f x+      (gb, gv) = g x+  in (fb <> ", " <> gb, fv ++ gv)++-- | Insert a 'PersistField' for every selected value+insertSelect :: (MonadLogger m, MonadResourceBase m, SqlSelect (SqlExpr (Insertion a)) r, PersistEntity a) =>+  SqlQuery (SqlExpr (Insertion a)) -> SqlPersistT m ()+insertSelect = insertGeneralSelect SELECT++-- | Insert a 'PersistField' for every unique selected value+insertSelectDistinct :: (MonadLogger m, MonadResourceBase m, SqlSelect (SqlExpr (Insertion a)) r, PersistEntity a) =>+  SqlQuery (SqlExpr (Insertion a)) -> SqlPersistT m ()+insertSelectDistinct = insertGeneralSelect SELECT_DISTINCT+++insertGeneralSelect :: (MonadLogger m, MonadResourceBase m, SqlSelect (SqlExpr (Insertion a)) r, PersistEntity a) =>+  Mode -> SqlQuery (SqlExpr (Insertion a)) -> SqlPersistT m ()+insertGeneralSelect mode query = do+  conn <- SqlPersistT R.ask+  uncurry rawExecute $ first builderToText $ toRawSql mode insertQuery conn query
test/Test.hs view
@@ -9,6 +9,7 @@            , Rank2Types            , TemplateHaskell            , TypeFamilies+           , ScopedTypeVariables  #-} module Main (main) where @@ -622,6 +623,17 @@                           where_ (bp ^. BlogPostAuthorId ==. p ^. PersonId)                  return p           liftIO $ ret `shouldBe` [ Entity p2k p2 ]++    describe "inserts by select" $ do+      it "IN works for insertSelect" $+        run $ do+          _ <- insert p1+          _ <- insert p2+          _ <- insert p3+          insertSelect $ from $ \p -> do+            return $ BlogPost <# val "FakePost" <&> (p ^. PersonId)+          ret <- select $ from (\(b::(SqlExpr (Entity BlogPost))) -> return countRows)+          liftIO $ ret `shouldBe` [Value (3::Int)]   ----------------------------------------------------------------------