packages feed

hasql 0.6.0 → 0.7.0

raw patch · 7 files changed

+168/−138 lines, 7 filesdep ~hasql-backendPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: hasql-backend

API changes (from Hackage documentation)

- Hasql: class RowParser c r
- Hasql: countTx :: CxValue c Word64 => Stmt c -> Tx c s Word64
- Hasql: data TxListT s m r
- Hasql: instance (Monad m, Functor m) => Alternative (TxListT s m)
- Hasql: instance (Monad m, Functor m) => Applicative (TxListT s m)
- Hasql: instance Functor m => Functor (TxListT s m)
- Hasql: instance Monad m => Monad (TxListT s m)
- Hasql: instance Monad m => MonadCons (TxListT s m)
- Hasql: instance Monad m => MonadPlus (TxListT s m)
- Hasql: instance Monad m => Monoid (TxListT s m r)
- Hasql: instance MonadTrans (TxListT s)
- Hasql: instance MonadTransUncons (TxListT s)
- Hasql: listTx :: RowParser c r => Stmt c -> Tx c s [r]
- Hasql: maybeTx :: RowParser c r => Stmt c -> Tx c s (Maybe r)
- Hasql: singleTx :: RowParser c r => Stmt c -> Tx c s r
- Hasql: streamTx :: RowParser c r => Stmt c -> Tx c s (TxListT s (Tx c s) r)
- Hasql: unitTx :: Stmt c -> Tx c s ()
- Hasql: vectorTx :: RowParser c r => Stmt c -> Tx c s (Vector r)
+ Hasql: class CxRow c r
+ Hasql: countEx :: CxValue c Word64 => Ex c s Word64
+ Hasql: data TxStreamListT s m r
+ Hasql: instance (Monad m, Functor m) => Alternative (TxStreamListT s m)
+ Hasql: instance (Monad m, Functor m) => Applicative (TxStreamListT s m)
+ Hasql: instance Functor m => Functor (TxStreamListT s m)
+ Hasql: instance Lift PoolSettings
+ Hasql: instance Monad m => Monad (TxStreamListT s m)
+ Hasql: instance Monad m => MonadCons (TxStreamListT s m)
+ Hasql: instance Monad m => MonadPlus (TxStreamListT s m)
+ Hasql: instance Monad m => Monoid (TxStreamListT s m r)
+ Hasql: instance MonadTrans (TxStreamListT s)
+ Hasql: instance MonadTransUncons (TxStreamListT s)
+ Hasql: instance Show PoolSettings
+ Hasql: listEx :: CxRow c r => Ex c s [r]
+ Hasql: maybeEx :: CxRow c r => Ex c s (Maybe r)
+ Hasql: singleEx :: CxRow c r => Ex c s r
+ Hasql: streamEx :: CxRow c r => Int -> Ex c s (TxStream c s r)
+ Hasql: type Ex c s r = Stmt c -> Tx c s r
+ Hasql: type TxStream c s = TxStreamListT s (Tx c s)
+ Hasql: unitEx :: Ex c s ()
+ Hasql: vectorEx :: CxRow c r => Ex c s (Vector r)

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+# 0.7.0 - Refinements and minor updates+* Streaming now is parameterized by the size of a chunk+* Introduced a new type `Ex`+* Changed the suffix of statement execution functions to `Ex`+ # 0.6.0 - Major API overhaul * The connection pool acquisition is now explicit and is no longer handled by the `Session` monad. This should provide for a simpler integration with other libraries. * The `Session` monad is now merely a convenience thing for providing a context to multiple transactions. One can run it as many times as he wants - it won't reestablish any resources any more.
demo/Main.hs view
@@ -36,11 +36,11 @@      -- Execute a group of statements without any locking and ACID guarantees:     H.tx Nothing $ do-      H.unitTx [H.stmt|DROP TABLE IF EXISTS a|]-      H.unitTx [H.stmt|CREATE TABLE a (id SERIAL NOT NULL, balance INT8, PRIMARY KEY (id))|]+      H.unitEx [H.stmt|DROP TABLE IF EXISTS a|]+      H.unitEx [H.stmt|CREATE TABLE a (id SERIAL NOT NULL, balance INT8, PRIMARY KEY (id))|]       -- Insert three rows:       replicateM_ 3 $ do -        H.unitTx [H.stmt|INSERT INTO a (balance) VALUES (0)|]+        H.unitEx [H.stmt|INSERT INTO a (balance) VALUES (0)|]      -- Declare a list of transfer settings, which we'll later use.     -- The tuple structure is: @@ -56,16 +56,16 @@         runMaybeT $ do           -- To distinguish results rows containing just one column,            -- we use 'Identity' as a sort of a single element tuple.-          Identity balance1 <- MaybeT $ H.maybeTx $ [H.stmt|SELECT balance FROM a WHERE id=?|] id1-          Identity balance2 <- MaybeT $ H.maybeTx $ [H.stmt|SELECT balance FROM a WHERE id=?|] id2-          lift $ H.unitTx $ [H.stmt|UPDATE a SET balance=? WHERE id=?|] (balance1 - amount) id1-          lift $ H.unitTx $ [H.stmt|UPDATE a SET balance=? WHERE id=?|] (balance2 + amount) id2+          Identity balance1 <- MaybeT $ H.maybeEx $ [H.stmt|SELECT balance FROM a WHERE id=?|] id1+          Identity balance2 <- MaybeT $ H.maybeEx $ [H.stmt|SELECT balance FROM a WHERE id=?|] id2+          lift $ H.unitEx $ [H.stmt|UPDATE a SET balance=? WHERE id=?|] (balance1 - amount) id1+          lift $ H.unitEx $ [H.stmt|UPDATE a SET balance=? WHERE id=?|] (balance2 + amount) id2      -- Output all the updated rows:     do       -- Unfortunately in this case there's no way to infer the type of the results,       -- so we need to specify it explicitly:-      rows <- H.tx Nothing $ H.vectorTx $ [H.stmt|SELECT * FROM a|]+      rows <- H.tx Nothing $ H.vectorEx $ [H.stmt|SELECT * FROM a|]       forM_ rows $ \(id :: Int, amount :: Int) -> do         liftIO $ putStrLn $ "ID: " ++ show id ++ ", Amount: " ++ show amount 
hasql.cabal view
@@ -1,7 +1,7 @@ name:   hasql version:-  0.6.0+  0.7.0 synopsis:   A minimalistic general high level API for relational databases description:@@ -10,7 +10,7 @@   .   Features:   .-  * Concise and crisp API. Just a few functions and one monad doing all the+  * Concise and crisp API. Just a few functions and two monads doing all the   boilerplate job for you.   .   * A powerful transaction abstraction, which provides @@ -89,14 +89,14 @@   other-modules:     Hasql.Prelude     Hasql.QParser-    Hasql.RowParser+    Hasql.CxRow     Hasql.TH   exposed-modules:     Hasql   build-depends:     --      resource-pool == 0.2.*,-    hasql-backend == 0.3.*,+    hasql-backend == 0.4.*,     --      template-haskell >= 2.8 && < 2.10,     -- 
hspec-postgres/Main.hs view
@@ -18,18 +18,18 @@         flip shouldBe (Right (Nothing :: Maybe (Identity Int))) =<< do           session $ do             H.tx Nothing $ do-              H.unitTx $ [H.stmt|DROP TABLE IF EXISTS a|]-              H.unitTx $ [H.stmt|CREATE TABLE a (x INT8 NOT NULL, PRIMARY KEY (x))|]+              H.unitEx $ [H.stmt|DROP TABLE IF EXISTS a|]+              H.unitEx $ [H.stmt|CREATE TABLE a (x INT8 NOT NULL, PRIMARY KEY (x))|]             H.tx (Just (H.Serializable, Just False)) $ do-              H.unitTx $ [H.stmt|INSERT INTO a (x) VALUES (2)|]+              H.unitEx $ [H.stmt|INSERT INTO a (x) VALUES (2)|]             H.tx Nothing $ do-              H.maybeTx $ [H.stmt|SELECT x FROM a WHERE x = 2|]+              H.maybeEx $ [H.stmt|SELECT x FROM a WHERE x = 2|]      context "UTF-8 templates" $ do        it "encode properly" $ do         flip shouldBe (Right (Just (Identity ("Ёжик" :: Text)))) =<< do-          session $ H.tx Nothing $ H.maybeTx $ [H.stmt| SELECT 'Ёжик' |]+          session $ H.tx Nothing $ H.maybeEx $ [H.stmt| SELECT 'Ёжик' |]      context "Bug" $ do @@ -37,16 +37,16 @@          it "should not be" $ do           session $ H.tx Nothing $ do-            H.unitTx [H.stmt|DROP TABLE IF EXISTS artist|]-            H.unitTx [H.stmt|DROP TABLE IF EXISTS artist_union|]-            H.unitTx $+            H.unitEx [H.stmt|DROP TABLE IF EXISTS artist|]+            H.unitEx [H.stmt|DROP TABLE IF EXISTS artist_union|]+            H.unitEx $               [H.stmt|                 CREATE TABLE "artist_union" (                   "id" BIGSERIAL,                   PRIMARY KEY ("id")                 )               |]-            H.unitTx $+            H.unitEx $               [H.stmt|                 CREATE TABLE "artist" (                   "id" BIGSERIAL,@@ -60,13 +60,13 @@           let              insertArtistUnion :: H.Tx HP.Postgres s Int64             insertArtistUnion =-              fmap (runIdentity . fromJust) $ H.maybeTx $+              fmap (runIdentity . fromJust) $ H.maybeEx $               [H.stmt|                  INSERT INTO artist_union DEFAULT VALUES RETURNING id               |]             insertArtist :: Int64 -> [Text] -> H.Tx HP.Postgres s Int64             insertArtist unionID artistNames = -              fmap (runIdentity . fromJust) $ H.maybeTx $+              fmap (runIdentity . fromJust) $ H.maybeEx $               [H.stmt|                  INSERT INTO artist                   (artist_union_id, @@ -86,22 +86,22 @@           replicateM_ 6 process           block -    context "RowParser" $ do+    context "CxRow" $ do        it "should fail on incorrect arity" $ do         flip shouldSatisfy (\case Left (H.ResultError _) -> True; _ -> False) =<< do           session $ do             H.tx Nothing $ do-              H.unitTx [H.stmt|DROP TABLE IF EXISTS data|]-              H.unitTx [H.stmt|CREATE TABLE data (+              H.unitEx [H.stmt|DROP TABLE IF EXISTS data|]+              H.unitEx [H.stmt|CREATE TABLE data (                                    field1    DECIMAL NOT NULL,                                    field2    BIGINT  NOT NULL,                                    PRIMARY KEY (field1)                                )|]-              H.unitTx [H.stmt|INSERT INTO data (field1, field2) VALUES (0, 0)|]+              H.unitEx [H.stmt|INSERT INTO data (field1, field2) VALUES (0, 0)|]             mrow :: Maybe (Double, Int64, String) <-                H.tx Nothing $  -                H.maybeTx $ [H.stmt|SELECT * FROM data|]+                H.maybeEx $ [H.stmt|SELECT * FROM data|]             return ()  
library/Hasql.hs view
@@ -30,14 +30,15 @@   Bknd.Stmt,   stmt, -  -- ** Statement Execution-  unitTx,-  countTx,-  singleTx,-  maybeTx,-  listTx,-  vectorTx,-  streamTx,+  -- * Statement Execution+  Ex,+  unitEx,+  countEx,+  singleEx,+  maybeEx,+  listEx,+  vectorEx,+  streamEx,    -- * Transaction   Tx,@@ -49,16 +50,17 @@   Bknd.TxWriteMode(..),    -- ** Result Stream-  TxListT,+  TxStream,+  TxStreamListT,    -- * Row Parser-  RowParser.RowParser,+  CxRow.CxRow, ) where  import Hasql.Prelude import qualified Hasql.Backend as Bknd-import qualified Hasql.RowParser as RowParser+import qualified Hasql.CxRow as CxRow import qualified Hasql.QParser as QParser import qualified ListT import qualified Data.Pool as Pool@@ -67,6 +69,7 @@ import qualified Data.Vector.Mutable as MVector import qualified Language.Haskell.TH as TH import qualified Language.Haskell.TH.Quote as TH+import qualified Language.Haskell.TH.Syntax as TH import qualified Hasql.TH as THUtil  @@ -115,7 +118,13 @@ -- Settings of a pool. data PoolSettings =   PoolSettings !Int !Int+  deriving (Show) +instance TH.Lift PoolSettings where+  lift (PoolSettings a b) = +    [|PoolSettings a b|]+    + -- |  -- A smart constructor for pool settings. poolSettings :: @@ -225,7 +234,7 @@ -- Execute a transaction in a session. --  -- This function ensures on the type level, --- that it's impossible to return @'TxListT' s m r@ from it.+-- that it's impossible to return @'TxStreamListT' s m r@ from it. tx :: (Bknd.CxTx c, MonadBaseControl IO m) => Bknd.TxMode -> (forall s. Tx c s r) -> Session c m r tx mode (Tx m) =   Session $ ReaderT $ \(Pool pool) ->@@ -244,16 +253,23 @@ -------------------------  -- |+-- Statement executor.+-- +-- Just an alias to a function, which executes a statement in 'Tx'.+type Ex c s r =+  Bknd.Stmt c -> Tx c s r++-- | -- Execute a statement without processing the result.-unitTx :: Bknd.Stmt c -> Tx c s ()-unitTx = +unitEx :: Ex c s ()+unitEx =    Tx . lift . Bknd.unitTx  -- | -- Execute a statement and count the amount of affected rows. -- Useful for resolving how many rows were updated or deleted.-countTx :: Bknd.CxValue c Word64 => Bknd.Stmt c -> Tx c s Word64-countTx =+countEx :: Bknd.CxValue c Word64 => Ex c s Word64+countEx =   Tx . lift . Bknd.countTx  -- |@@ -264,53 +280,58 @@ --  -- Please note that using this executor for selecting rows is conceptually wrong,  -- since in that case the results are always optional. --- Use 'maybeTx', 'listTx' or 'vectorTx' instead.+-- Use 'maybeEx', 'listEx' or 'vectorEx' instead. --  -- If the result is empty this executor will raise 'ResultError'.-singleTx :: RowParser.RowParser c r => Bknd.Stmt c -> Tx c s r-singleTx =-  join . fmap (maybe (Tx $ left $ ResultError "No rows on 'singleTx'") return) .-  maybeTx+singleEx :: CxRow.CxRow c r => Ex c s r+singleEx =+  join . fmap (maybe (Tx $ left $ ResultError "No rows on 'singleEx'") return) .+  maybeEx  -- | -- Execute a statement, -- which optionally produces a single result row.-maybeTx :: RowParser.RowParser c r => Bknd.Stmt c -> Tx c s (Maybe r)-maybeTx =-  fmap (fmap Vector.unsafeHead . mfilter (not . Vector.null) . Just) . vectorTx+maybeEx :: CxRow.CxRow c r => Ex c s (Maybe r)+maybeEx =+  fmap (fmap Vector.unsafeHead . mfilter (not . Vector.null) . Just) . vectorEx  -- | -- Execute a statement, -- and produce a list of results.-listTx :: RowParser.RowParser c r => Bknd.Stmt c -> Tx c s [r]-listTx =-  fmap toList . vectorTx+listEx :: CxRow.CxRow c r => Ex c s [r]+listEx =+  fmap toList . vectorEx  -- | -- Execute a statement, -- and produce a vector of results.-vectorTx :: RowParser.RowParser c r => Bknd.Stmt c -> Tx c s (Vector r)-vectorTx s =+vectorEx :: CxRow.CxRow c r => Ex c s (Vector r)+vectorEx s =   Tx $ do     r <- lift $ Bknd.vectorTx s-    EitherT $ return $ traverse ((mapLeft ResultError) . RowParser.parseRow) $ r+    EitherT $ return $ traverse ((mapLeft ResultError) . CxRow.parseRow) $ r  -- |--- Execute a @SELECT@ statement with a cursor,+-- Given a batch size, execute a statement with a cursor, -- and produce a result stream. -- --- Cursor allows you to fetch virtually limitless results in a constant memory+-- The cursor allows you to fetch virtually limitless results in a constant memory -- at a cost of a small overhead.+-- +-- The batch size parameter controls how many rows will be fetched +-- during every roundtrip to the database. +-- A minimum value of 256 seems to be sane.+--  -- Note that in most databases cursors require establishing a database transaction, -- so depending on a backend the transaction may result in an error, -- if you run it improperly.-streamTx :: RowParser.RowParser c r => Bknd.Stmt c -> Tx c s (TxListT s (Tx c s) r)-streamTx s =+streamEx :: CxRow.CxRow c r => Int -> Ex c s (TxStream c s r)+streamEx n s =   Tx $ do-    r <- lift $ Bknd.streamTx s-    return $ TxListT $ do+    r <- lift $ Bknd.streamTx n s+    return $ TxStreamListT $ do       row <- hoist (Tx . lift) r-      lift $ Tx $ EitherT $ return $ mapLeft ResultError $ RowParser.parseRow $ row+      lift $ Tx $ EitherT $ return $ mapLeft ResultError $ CxRow.parseRow $ row   -- * Result Stream@@ -318,26 +339,30 @@  -- | -- A stream of results, --- which fetches only those that you reach.--- --- It's a wrapper around 'ListT.ListT', +-- which fetches approximately only those that you reach.+type TxStream c s =+  TxStreamListT s (Tx c s)++-- |+-- A wrapper around 'ListT.ListT',  -- which uses the same trick as the 'ST' monad to associate with the--- context transaction and become impossible to be used outside of it.+-- context monad and become impossible to be returned from it,+-- using the anonymous type parameter @s@. -- This lets the library ensure that it is safe to automatically -- release all the connections associated with this stream. --  -- All the functions of the \"list-t\" library are applicable to this type, -- amongst which are 'ListT.head', 'ListT.toList', 'ListT.fold', 'ListT.traverse_'.-newtype TxListT s m r =-  TxListT (ListT.ListT m r)+newtype TxStreamListT s m r =+  TxStreamListT (ListT.ListT m r)   deriving (Functor, Applicative, Alternative, Monad, MonadTrans, MonadPlus,              Monoid, ListT.MonadCons) -instance ListT.MonadTransUncons (TxListT s) where+instance ListT.MonadTransUncons (TxStreamListT s) where   uncons = -    (liftM . fmap . fmap) (unsafeCoerce :: ListT.ListT m r -> TxListT s m r) .+    (liftM . fmap . fmap) (unsafeCoerce :: ListT.ListT m r -> TxStreamListT s m r) .     ListT.uncons . -    (unsafeCoerce :: TxListT s m r -> ListT.ListT m r)+    (unsafeCoerce :: TxStreamListT s m r -> ListT.ListT m r)   -- * Statements quasi-quotation
+ library/Hasql/CxRow.hs view
@@ -0,0 +1,64 @@+module Hasql.CxRow where++import Hasql.Prelude+import Language.Haskell.TH+import qualified Hasql.Backend as Bknd+import qualified Data.Vector as Vector+import qualified Hasql.TH as THUtil+++-- |+-- This class is only intended to be used with the supplied instances,+-- which should be enough to cover all use cases.+class CxRow c r where+  parseRow :: Bknd.ResultRow c -> Either Text r++instance CxRow c () where+  parseRow row = +    if Vector.null row+      then Right ()+      else Left "Not an empty row"++instance Bknd.CxValue c v => CxRow c (Identity v) where+  parseRow row = do+    Identity <$> Bknd.decodeValue (Vector.unsafeHead row)++-- Generate tuple instaces using Template Haskell:+return $ flip map [2 .. 24] $ \arity ->+  let +    varNames =+      [1 .. arity] >>= \i -> return (mkName ('v' : show i))+    varTypes =+      map VarT varNames+    connectionType =+      VarT (mkName "c")+    constraints =+      map (\t -> ClassP ''Bknd.CxValue [connectionType, t]) varTypes+    head =+      AppT (AppT (ConT ''CxRow) connectionType) (foldl AppT (TupleT arity) varTypes)+    parseRowDec =+      FunD 'parseRow [Clause [VarP rowVarName] (NormalB e) []]+      where+        rowVarName = mkName "row"+        e =+          THUtil.purify $+            [|+              let actualLength = Vector.length $(varE rowVarName)+                  expectedLength = $(litE (IntegerL $ fromIntegral arity))+                  in if actualLength == expectedLength+                    then $(pure $ THUtil.applicativeE (ConE (tupleDataName arity)) lookups)+                    else Left $ fromString $ ($ "") $+                           showString "Inappropriate row length: " . shows actualLength .+                           showString ", expecting: " . shows expectedLength . +                           showString " instead"+            |]+          where+            lookups = do+              i <- [0 .. pred arity]+              return $ THUtil.purify $+                [|+                  Bknd.decodeValue+                    (Vector.unsafeIndex $(varE rowVarName) $(litE (IntegerL $ fromIntegral i)) )+                |]+    in InstanceD constraints head [parseRowDec]+
− library/Hasql/RowParser.hs
@@ -1,64 +0,0 @@-module Hasql.RowParser where--import Hasql.Prelude-import Language.Haskell.TH-import qualified Hasql.Backend as Bknd-import qualified Data.Vector as Vector-import qualified Hasql.TH as THUtil----- |--- This class is only intended to be used with the supplied instances,--- which should be enough to cover all use cases.-class RowParser c r where-  parseRow :: Bknd.ResultRow c -> Either Text r--instance RowParser c () where-  parseRow row = -    if Vector.null row-      then Right ()-      else Left "Not an empty row"--instance Bknd.CxValue c v => RowParser c (Identity v) where-  parseRow row = do-    Identity <$> Bknd.decodeValue (Vector.unsafeHead row)---- Generate tuple instaces using Template Haskell:-return $ flip map [2 .. 24] $ \arity ->-  let -    varNames =-      [1 .. arity] >>= \i -> return (mkName ('v' : show i))-    varTypes =-      map VarT varNames-    connectionType =-      VarT (mkName "c")-    constraints =-      map (\t -> ClassP ''Bknd.CxValue [connectionType, t]) varTypes-    head =-      AppT (AppT (ConT ''RowParser) connectionType) (foldl AppT (TupleT arity) varTypes)-    parseRowDec =-      FunD 'parseRow [Clause [VarP rowVarName] (NormalB e) []]-      where-        rowVarName = mkName "row"-        e =-          THUtil.purify $-            [|-              let actualLength = Vector.length $(varE rowVarName)-                  expectedLength = $(litE (IntegerL $ fromIntegral arity))-                  in if actualLength == expectedLength-                    then $(pure $ THUtil.applicativeE (ConE (tupleDataName arity)) lookups)-                    else Left $ fromString $ ($ "") $-                           showString "Inappropriate row length: " . shows actualLength .-                           showString ", expecting: " . shows expectedLength . -                           showString " instead"-            |]-          where-            lookups = do-              i <- [0 .. pred arity]-              return $ THUtil.purify $-                [|-                  Bknd.decodeValue-                    (Vector.unsafeIndex $(varE rowVarName) $(litE (IntegerL $ fromIntegral i)) )-                |]-    in InstanceD constraints head [parseRowDec]-