packages feed

lmdb-simple 0.1.0.0 → 0.2.0.0

raw patch · 7 files changed

+190/−35 lines, 7 filesdep ~binarydep ~bytestringdep ~lmdbPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: binary, bytestring, lmdb

API changes (from Hackage documentation)

+ Database.LMDB.Simple.DBRef: data DBRef mode a
+ Database.LMDB.Simple.DBRef: modifyDBRef :: Binary a => DBRef ReadWrite a -> (Maybe a -> (Maybe a, b)) -> IO b
+ Database.LMDB.Simple.DBRef: modifyDBRef_ :: Binary a => DBRef ReadWrite a -> (Maybe a -> Maybe a) -> IO ()
+ Database.LMDB.Simple.DBRef: newDBRef :: Binary k => Environment mode -> Database k a -> k -> IO (DBRef mode a)
+ Database.LMDB.Simple.DBRef: readDBRef :: Binary a => DBRef mode a -> IO (Maybe a)
+ Database.LMDB.Simple.DBRef: writeDBRef :: Binary a => DBRef ReadWrite a -> Maybe a -> IO ()
- Database.LMDB.Simple: class Mode mode
+ Database.LMDB.Simple: class Mode a

Files

lmdb-simple.cabal view
@@ -1,6 +1,6 @@  name:                lmdb-simple-version:             0.1.0.0+version:             0.2.0.0  synopsis:            Simple API for LMDB @@ -35,14 +35,15 @@   hs-source-dirs:      src    exposed-modules:     Database.LMDB.Simple+                       Database.LMDB.Simple.DBRef                        Database.LMDB.Simple.Extra                        Database.LMDB.Simple.View   other-modules:       Database.LMDB.Simple.Internal    build-depends:       base >= 4.7 && < 5-                     , binary >= 0.8-                     , bytestring-                     , lmdb >= 0.2+                     , binary >= 0.8 && < 0.9+                     , bytestring >= 0.10 && < 0.11+                     , lmdb >= 0.2 && < 0.3   ghc-options:         -Wall -Wno-name-shadowing -Wno-unused-do-bind   default-language:    Haskell2010   default-extensions:  Trustworthy@@ -64,6 +65,7 @@   hs-source-dirs:      test   main-is:             hspec.hs   other-modules:       Database.LMDB.SimpleSpec+                       Database.LMDB.Simple.DBRefSpec                        Harness   build-depends:       base                      , hspec
src/Database/LMDB/Simple.hs view
@@ -32,9 +32,16 @@   print =<< readOnlyTransaction env (get db "nine")  -- Nothing @ -Additional functions for querying and modifying LMDB databases are provided in-"Database.LMDB.Simple.Extra". For an option to access LMDB databases from pure-code, see "Database.LMDB.Simple.View".+These additional APIs are available:++  * "Database.LMDB.Simple.Extra" provides additional functions for querying+    and modifying LMDB databases within the 'Transaction' monad++  * "Database.LMDB.Simple.View" provides a read-only snapshot of an LMDB+    database that can be accessed from pure code++  * "Database.LMDB.Simple.DBRef" provides a mutable variable (accessed from+    'IO') that is tied to a particular key that persists in an LMDB database -}  module Database.LMDB.Simple@@ -275,12 +282,12 @@ -- -- An exception will cause the nested transaction to be implicitly aborted. nestTransaction :: Transaction ReadWrite a -> Transaction ReadWrite (Maybe a)-nestTransaction tx@(Txn tf) = Txn $ run (isReadOnlyTransaction tx)-  where run ro ptxn = let env = mdb_txn_env ptxn in maybeAborted $-          bracketOnError (mdb_txn_begin env (Just ptxn) ro) mdb_txn_abort $-          \ctxn -> tf ctxn >>= \result -> mdb_txn_commit ctxn >> return result+nestTransaction (Txn tf) = Txn $ \ptxn ->+  let env = mdb_txn_env ptxn in maybeAborted $+  bracketOnError (mdb_txn_begin env (Just ptxn) False) mdb_txn_abort $+  \ctxn -> tf ctxn >>= \result -> mdb_txn_commit ctxn >> return result -        maybeAborted :: IO a -> IO (Maybe a)+  where maybeAborted :: IO a -> IO (Maybe a)         maybeAborted io = either           (\e -> let _ = e :: AbortedTransaction in Nothing) Just <$> try io 
+ src/Database/LMDB/Simple/DBRef.hs view
@@ -0,0 +1,122 @@++-- | This module provides a mutable variable 'DBRef' that is similar in+-- concept to 'Data.IORef.IORef' except that it is tied to a particular key+-- that persists in an LMDB database.++module Database.LMDB.Simple.DBRef+  ( DBRef+  , newDBRef+  , readDBRef+  , writeDBRef+  , modifyDBRef_+  , modifyDBRef+  ) where++import Control.Monad+  ( (>=>)+  , void+  )++import Control.Monad.IO.Class+  ( MonadIO (liftIO)+  )++import Data.Binary+  ( Binary+  , encode+  )++import Data.ByteString+  ( ByteString+  )++import Data.ByteString.Lazy+  ( toStrict+  )++import Data.ByteString.Unsafe+  ( unsafeUseAsCStringLen+  )++import Database.LMDB.Raw+  ( MDB_dbi'+  , MDB_val (..)+  , mdb_get'+  , mdb_put'+  , mdb_del'+  )++import Database.LMDB.Simple+  ( transaction+  )++import Database.LMDB.Simple.Internal+  ( Environment (..)+  , Transaction (..)+  , Database (..)+  , ReadWrite+  , ReadOnly+  , marshalIn+  , marshalOut+  , defaultWriteFlags+  )++import Foreign+  ( castPtr+  )++-- | A 'DBRef' is a reference to a particular key within an LMDB database. It+-- may be empty ('Nothing') if the key does not currently exist in the+-- database, or it may contain a 'Just' value corresponding to the key.+--+-- A 'DBRef' may be 'ReadWrite' or 'ReadOnly', depending on the environment+-- within which it is created. Note that 'ReadOnly' does not imply that the+-- contained value will not change, since the LMDB database could be modified+-- externally.+data DBRef mode a = Ref (Environment mode) MDB_dbi' ByteString++-- | Create a new 'DBRef' for the given key and database within the given+-- environment.+newDBRef :: Binary k+         => Environment mode -> Database k a -> k -> IO (DBRef mode a)+newDBRef env (Db _ dbi) = return . Ref env dbi . toStrict . encode++withVal :: ByteString -> (MDB_val -> IO a) -> IO a+withVal bs f = unsafeUseAsCStringLen bs $ \(ptr, len) ->+  f $ MDB_val (fromIntegral len) (castPtr ptr)++-- | Read the current value of a 'DBRef'.+readDBRef :: Binary a => DBRef mode a -> IO (Maybe a)+readDBRef ref@(Ref env dbi key) = transaction env (tx ref)++  where tx :: Binary a => DBRef mode a -> Transaction ReadOnly (Maybe a)+        tx _ = Txn $ \txn -> withVal key $ mdb_get' txn dbi >=>+          maybe (return Nothing) (liftIO . fmap Just . marshalIn)++-- | Write a new value into a 'DBRef'.+writeDBRef :: Binary a => DBRef ReadWrite a -> Maybe a -> IO ()+writeDBRef (Ref env dbi key) = transaction env . maybe delKey putKey++  where delKey :: Transaction ReadWrite ()+        delKey = Txn $ \txn -> withVal key $ \kval ->+          void $ mdb_del' txn dbi kval Nothing++        putKey :: Binary a => a -> Transaction ReadWrite ()+        putKey value = Txn $ \txn -> withVal key $ \kval ->+          marshalOut value $ \vval ->  -- FIXME: use mdb_reserve'+          void $ mdb_put' defaultWriteFlags txn dbi kval vval++-- | Atomically mutate the contents of a 'DBRef'.+modifyDBRef_ :: Binary a => DBRef ReadWrite a -> (Maybe a -> Maybe a) -> IO ()+modifyDBRef_ ref f = modifyDBRef ref $ \x -> (f x, ())++-- | Atomically mutate the contents of a 'DBRef' and return a value.+modifyDBRef :: Binary a => DBRef ReadWrite a -> (Maybe a -> (Maybe a, b)) -> IO b+modifyDBRef (Ref env dbi key) = transaction env . tx++  where tx :: Binary a => (Maybe a -> (Maybe a, b)) -> Transaction ReadWrite b+        tx f = Txn $ \txn -> withVal key $ \kval -> mdb_get' txn dbi kval >>=+          maybe (return Nothing) (fmap Just . marshalIn) >>= \x ->+          let (x', r) = f x in maybe (mdb_del' txn dbi kval Nothing)+            (flip marshalOut $ mdb_put' defaultWriteFlags txn dbi kval) x' >>+          return r
src/Database/LMDB/Simple/Internal.hs view
@@ -98,14 +98,11 @@ data ReadWrite data ReadOnly -class Mode mode where-  isReadOnlyMode :: mode -> Bool--instance Mode ReadWrite where-  isReadOnlyMode _ = False+class Mode a where+  isReadOnlyMode :: a -> Bool -instance Mode ReadOnly where-  isReadOnlyMode _ = True+instance Mode ReadWrite where isReadOnlyMode _ = False+instance Mode ReadOnly  where isReadOnlyMode _ = True  type family SubMode a b :: Constraint where   SubMode a ReadWrite = a ~ ReadWrite@@ -122,7 +119,7 @@  -- | An LMDB transaction is an atomic unit for reading and/or changing one or -- more LMDB databases within an environment, during which the transaction has--- a consistent view of the databases and is unaffected by any other+-- a consistent view of the database(s) and is unaffected by any other -- transaction. The effects of a transaction can either be committed to the -- LMDB environment atomically, or they can be rolled back with no observable -- effect on the environment if the transaction is aborted.@@ -146,7 +143,7 @@ isReadWriteTransaction = not . isReadOnlyTransaction  instance Functor (Transaction mode) where-  fmap f (Txn tf) = Txn $ \txn -> fmap f (tf txn)+  fmap f (Txn tf) = Txn $ fmap f . tf  instance Applicative (Transaction mode) where   pure x = Txn $ \_ -> pure x@@ -156,7 +153,7 @@   Txn tf >>= f = Txn $ \txn -> tf txn >>= \r -> let Txn tf' = f r in tf' txn  instance MonadIO (Transaction mode) where-  liftIO io = Txn $ const io+  liftIO = Txn . const  -- | A database maps arbitrary keys to values. This API uses the 'Binary' -- class to serialize keys and values for LMDB to store on disk.
src/Database/LMDB/Simple/View.hs view
@@ -1,7 +1,8 @@ -{-| This module provides a read-only 'View' for an LMDB database at a single-point in time. Because the view is unchanging, it can be used within pure-code. Behind the scenes, data is accessed from the underlying LMDB memory map.+{-| This module provides a read-only 'View' that is a snapshot of an LMDB+database at a single point in time. Because the view is unchanging, it can be+used within pure code. Behind the scenes, data is accessed from the underlying+LMDB memory map.  Each 'View' internally keeps open a read-only transaction in the LMDB environment (consuming a slot in the lock table), so their use should be
+ test/Database/LMDB/Simple/DBRefSpec.hs view
@@ -0,0 +1,27 @@++module Database.LMDB.Simple.DBRefSpec+  ( spec+  ) where++import Database.LMDB.Simple.DBRef+import Harness+import Test.Hspec++spec :: Spec+spec = beforeAll (setup >>= \(env, db) -> newDBRef env db 0) $ do++  it "starts empty" $ \ref ->+    readDBRef ref+    `shouldReturn` Nothing++  it "reads what is written" $ \ref ->+    (writeDBRef ref (Just "foo") >> readDBRef ref)+    `shouldReturn` Just "foo"++  it "reads what is modified" $ \ref ->+    (modifyDBRef_ ref (fmap (++ "bar")) >> readDBRef ref)+    `shouldReturn` Just "foobar"++  it "can be made empty again" $ \ref ->+    (writeDBRef ref Nothing >> readDBRef ref)+    `shouldReturn` Nothing
test/Database/LMDB/SimpleSpec.hs view
@@ -10,44 +10,43 @@ import Test.Hspec  spec :: Spec-spec = do-  (env, db) <- runIO setup+spec = beforeAll setup $ do    describe "basic operations" $ do-    it "inserts and counts entries" $+    it "inserts and counts entries" $ \(env, db) ->       transaction env       ( do forM_ [1..100] $ \i -> put db i (Just $ show i)            size db       ) `shouldReturn` 100 -    it "retrieves entries" $+    it "retrieves entries" $ \(env, db) ->       readOnlyTransaction env (forM [1..100] $ \i -> get db i)       `shouldReturn` map (Just . show) [1 :: Int .. 100] -    it "deletes entries" $+    it "deletes entries" $ \(env, db) ->       transaction env (put db 50 Nothing >> (,) <$> get db 50 <*> size db)       `shouldReturn` (Nothing, 99)    describe "transactions" $ do-    it "aborts" $+    it "aborts" $ \(env, db) ->       transaction env (put db 0 (Just "zero") >> abort)       `shouldThrow` (const True :: Selector AbortedTransaction) -    it "rolls back" $+    it "rolls back" $ \(env, db) ->       readOnlyTransaction env (get db 0)       `shouldReturn` Nothing -    it "aborts nested transactions" $+    it "aborts nested transactions" $ \(env, db) ->       transaction env       ( do put db 1 (Just "one")            nestTransaction $ put db 2 (Just "two") >> abort       ) `shouldReturn` (Nothing :: Maybe ()) -    it "rolls back nested transactions" $+    it "rolls back nested transactions" $ \(env, db) ->       readOnlyTransaction env ((,) <$> get db 1 <*> get db 2)       `shouldReturn` (Just "one", Just "2") -    it "commits nested transactions" $+    it "commits nested transactions" $ \(env, db) ->       transaction env       ( do nestTransaction (put db 3 $ Just "three")            get db 3