sqlite-simple 0.4.6.1 → 0.4.7.0
raw patch · 6 files changed
+66/−6 lines, 6 files
Files
- Database/SQLite/Simple.hs +26/−2
- Database/SQLite/Simple/ToRow.hs +2/−2
- changelog +3/−0
- sqlite-simple.cabal +2/−2
- test/Errors.hs +32/−0
- test/Main.hs +1/−0
Database/SQLite/Simple.hs view
@@ -75,6 +75,8 @@ , execute_ , executeNamed , field+ -- * Transactions+ , withTransaction -- * Low-level statement API for stream access and prepared statements , openStatement , closeStatement@@ -329,6 +331,12 @@ -- | A version of 'query' where the query parameters (placeholders) -- are named.+--+-- Example:+--+-- @+-- r \<- 'queryNamed' c \"SELECT * FROM posts WHERE id=:id AND date>=:date\" [\":id\" ':=' postId, \":date\" ':=' afterDate]+-- @ queryNamed :: (FromRow r) => Connection -> Query -> [NamedParam] -> IO [r] queryNamed conn templ params = withStatementNamedParams conn templ params $ \stmt -> doFoldToList stmt@@ -452,6 +460,22 @@ lastInsertRowId :: Connection -> IO Int64 lastInsertRowId (Connection c) = BaseD.lastInsertRowId c +-- | Run an IO action inside a SQL transaction started with @BEGIN+-- TRANSACTION@. If the action throws any kind of an exception, the+-- transaction will be rolled back with @ROLLBACK TRANSACTION@.+-- Otherwise the results are committed with @COMMIT TRANSACTION@.+withTransaction :: Connection -> IO a -> IO a+withTransaction conn action =+ mask $ \restore -> do+ begin+ r <- restore action `onException` rollback+ commit+ return r+ where+ begin = execute_ conn "BEGIN TRANSACTION"+ commit = execute_ conn "COMMIT TRANSACTION"+ rollback = execute_ conn "ROLLBACK TRANSACTION"+ fmtError :: Show v => String -> Query -> [v] -> a fmtError msg q xs = throw FormatError {@@ -587,14 +611,14 @@ -- Example: -- -- @--- r \<- 'queryNamed' c \"SELECT id,text FROM posts WHERE id = :id AND date >= :date\" [\":id\" ':=' postId, \":date\" := afterDate]+-- r \<- 'queryNamed' c \"SELECT id,text FROM posts WHERE id = :id AND date >= :date\" [\":id\" ':=' postId, \":date\" ':=' afterDate] -- @ -- -- Note that you can mix different value types in the same list. -- E.g., the following is perfectly legal: -- -- @--- [\":id\" := (3 :: Int), \":str\" := (\"foo\" :: String)]+-- [\":id\" ':=' (3 :: Int), \":str\" ':=' (\"foo\" :: String)] -- @ -- -- The parameter name (or key) in the 'NamedParam' must match exactly
Database/SQLite/Simple/ToRow.hs view
@@ -26,11 +26,11 @@ import Database.SQLite3 (SQLData(..)) --- | A collection type that can be turned into a list of SQLData+-- | A collection type that can be turned into a list of 'SQLData' -- elements. class ToRow a where toRow :: a -> [SQLData]- -- ^ ToField a collection of values.+ -- ^ 'ToField' a collection of values. instance ToRow () where toRow _ = []
changelog view
@@ -1,3 +1,6 @@+0.4.7.0+ * Add `withTransaction' for running IO actions inside SQL+ transactions with automated rollback if any exceptions are thrown. 0.4.6.1 * Fix unit test build break with older bytestring versions
sqlite-simple.cabal view
@@ -1,10 +1,10 @@ Name: sqlite-simple-Version: 0.4.6.1+Version: 0.4.7.0 Synopsis: Mid-Level SQLite client library Description: Mid-level SQLite client library, based on postgresql-simple. .- Main documentation (with examples): <docs/Database-SQLite-Simple.html Database.SQLite.Simple>+ Main documentation (with examples): <sqlite-simple/docs/Database-SQLite-Simple.html Database.SQLite.Simple> . You can view the project page at <http://github.com/nurpax/sqlite-simple> for more information.
test/Errors.hs view
@@ -6,6 +6,7 @@ , testErrorsInvalidNamedParams , testErrorsWithStatement , testErrorsColumnName+ , testErrorsTransaction ) where import Prelude hiding (catch)@@ -99,3 +100,34 @@ assertOOBCaught $ withStatement conn "SELECT id FROM invcolumn" $ \stmt -> columnName stmt (ColumnIndex (-1)) >> assertFailure "Error not detected"++testErrorsTransaction :: TestEnv -> Test+testErrorsTransaction TestEnv{..} = TestCase $ do+ execute_ conn "CREATE TABLE trans (id INTEGER PRIMARY KEY, t TEXT)"+ withTransaction conn $ do+ executeNamed conn "INSERT INTO trans (t) VALUES (:txt)" [":txt" := ("foo" :: String)]+ e <- rowExists+ True @=? e+ execute_ conn "DELETE FROM trans"+ e <- rowExists+ False @=? e+ assertFormatErrorCaught+ (withTransaction conn $ do+ -- this execute should be automatically rolled back on error+ executeNamed conn+ "INSERT INTO trans (t) VALUES (:txt)" [":txt" := ("foo" :: String)]+ -- intentional mistake here to hit an error & cause rollback of txn+ executeNamed conn+ "INSERT INTO trans (t) VALUES (:txt)" [":missing" := ("foo" :: String)])+ e <- rowExists+ False @=? e+ where+ rowExists = do+ rows <- query_ conn "SELECT t FROM trans" :: IO [Only String]+ case rows of+ [Only txt] -> do+ "foo" @=? txt+ return True+ [] ->+ return False+ _ -> error "should have only one row"
test/Main.hs view
@@ -46,6 +46,7 @@ , TestLabel "Errors" . testErrorsInvalidNamedParams , TestLabel "Errors" . testErrorsWithStatement , TestLabel "Errors" . testErrorsColumnName+ , TestLabel "Errors" . testErrorsTransaction , TestLabel "Utf8" . testUtf8Simplest , TestLabel "Utf8" . testBlobs , TestLabel "Instances" . testUserFromField