packages feed

sqlite-easy 0.2.0.0 → 0.2.0.1

raw patch · 5 files changed

+47/−18 lines, 5 filesdep ~resource-pool

Dependency ranges changed: resource-pool

Files

changelog.md view
@@ -1,3 +1,7 @@+### 0.2.0.1++- Fix: finalize prepared statements even when errors are encountered+ ### 0.2.0.0  * Rework the API
sqlite-easy.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4  name: sqlite-easy-version: 0.2.0.0+version: 0.2.0.1 synopsis: A primitive yet easy to use sqlite library. description: A primitive yet easy to use sqlite library built using sqlite-direct, resource-pool and migrant. author: Gil Mizrahi
src/Database/Sqlite/Easy.hs view
@@ -58,12 +58,12 @@ {- $connecting  The easiest way to run some statements on the database is using the-'withDb' function. 'withDb' uses continuation-passing style and expects-a connection string (such as a file with flags or @:memory:@,-see sqlite3 docs: <https://www.sqlite.org/c3ref/open.html>), and-a function that will take the database handle and do things with it.-It will open the connection to the database and call the function,-passing the database handle to it.+'withDb' function. 'withDb' expects a connection string+(such as a file with flags or @:memory:@, see sqlite3 docs:+<https://www.sqlite.org/c3ref/open.html>), and SQLite action(s),+such as queries and statements.+It will open the connection to the database and run the SQLite actions+on that database.  === Example @@ -86,7 +86,7 @@ We can use the 'createSqlitePool' function to create a @Pool 'Database'@ and pass that around until you are ready to use the database. -We can use 'withPool' like we did with 'withDb' but passing a 'Database'+We can use 'withPool' like we did with 'withDb' but passing a @Pool Database@ instead of a 'ConnectionString'.  === Example@@ -210,7 +210,7 @@  {- $suggestions -A suggestion for the architecture of your database interactions: use the handler pattern!+A suggestion for the architecture of your database interactions: use the handle pattern!  === 1. Create a type for your API @@ -257,9 +257,9 @@  and so on. -=== 3. Use the handler from your application code+=== 3. Use the handle from your application code -When you start the application, run @mkDB@ and get a handler, and pass it around+When you start the application, run @mkDB@ and get a handle, and pass it around (or use @ReaderT@). When you need to run a database command, call a field from @DB@:  > -- A page for a specific post
src/Database/Sqlite/Easy/Internal.hs view
@@ -61,16 +61,16 @@ run :: SQL -> SQLite [[SQLData]] run (SQL stmt) = do   db <- getDB-  liftIO (prepare db stmt >>= fetchAll)+  liftIO $ bracket (prepare db stmt) finalize fetchAll  -- | Run a SQL statement with certain parameters on a database and fetch the results. runWith :: SQL -> [SQLData] -> SQLite [[SQLData]] runWith (SQL stmt) params = do   db <- getDB   liftIO $ do-    preparedStmt <- prepare db stmt-    bind preparedStmt params-    fetchAll preparedStmt+    bracket (prepare db stmt) finalize $ \preparedStmt -> do+      bind preparedStmt params+      fetchAll preparedStmt  -- | Run a statement and fetch all of the data. fetchAll :: Statement -> IO [[SQLData]]@@ -81,12 +81,19 @@       row <- columns stmt       rows <- fetchAll stmt       pure (row : rows)-    Done -> do-      finalize stmt+    Done ->       pure []  -- * Transaction +-- | The type of actions to run on a SQLite database.+--   In essence, it is almost the same as @Database -> IO a@.+--+--   'SQLite' actions can be created with the 'run' and 'runWith'+--   functions, and can be composed using the type class instances.+--+--   'SQLite' actions can be run with the 'withDb', 'withDatabase',+--   and 'withPool' functions. newtype SQLite a   = SQLite     { unSQLite :: SQLiteStuff -> IO a
test/SqliteEasySpec.hs view
@@ -3,9 +3,10 @@  module SqliteEasySpec (spec) where +import Data.List (isInfixOf) import Test.Hspec import Database.Sqlite.Easy-import UnliftIO.Exception (try)+import UnliftIO.Exception (try, catch, displayException, SomeException(..))  spec :: Spec spec = do@@ -27,6 +28,23 @@       shouldBe         result         [[SQLInteger 1]]++    it "sql injection" $ do+      result <- withDb ":memory:" $ do+        [] <- run "create table students(name text not null)"+        [] <- runWith "insert into students values (?)" [SQLText "Robert'); DROP TABLE students;--"]+        run "select * from students"+      shouldBe+        result+        [[SQLText "Robert'); DROP TABLE students;--"]]++    it "finalize prepared statement on error" $ do+      shouldThrow+        ( withDb ":memory:" $ do+          [] <- run "create table test (id integer primary key autoincrement, val text unique)"+          run "insert into test (val) values ('hello'), ('hello')"+        )+        (\(SomeException e) -> "constraint failed: test.val" `isInfixOf` displayException e)    describe "transactions" $ do     it "transaction is rolled back in case of error" $ do