Working sqlite3 backend for tansu
Getty Ritter
8 years ago
1 | {-# LANGUAGE OverloadedStrings #-} | |
2 | ||
3 | module Database.Tansu.Backend.SQLite3 where | |
4 | ||
5 | import Control.Monad (when, void) | |
6 | import Data.ByteString (ByteString) | |
7 | import Data.Text (Text) | |
8 | import qualified Data.Text as T | |
9 | import Database.SQLite3 | |
10 | import Database.Tansu.Internal | |
11 | import System.Directory (doesFileExist) | |
12 | ||
13 | schema :: Text | |
14 | schema = "CREATE TABLE tansu ( k blob primary key not null \ | |
15 | \ , v blob not null );" | |
16 | ||
17 | sqlset :: Database -> ByteString -> ByteString -> IO (Either TansuError ()) | |
18 | sqlset db k v = do | |
19 | stmt <- prepare db "INSERT INTO tansu (k, v) VALUES (:k, :v);" | |
20 | bindNamed stmt [ (":k", SQLBlob k) | |
21 | , (":v", SQLBlob v) | |
22 | ] | |
23 | void (step stmt) | |
24 | finalize stmt | |
25 | return (return ()) | |
26 | ||
27 | sqlget :: Database -> ByteString -> IO (Either TansuError ByteString) | |
28 | sqlget db k = do | |
29 | stmt <- prepare db "SELECT v FROM tansu WHERE k = :k;" | |
30 | bindNamed stmt [ (":k", SQLBlob k) ] | |
31 | void (step stmt) | |
32 | cs <- columns stmt | |
33 | finalize stmt | |
34 | case cs of | |
35 | [SQLBlob v] -> return (Right v) | |
36 | [SQLNull] -> return (Left (KeyNotFound k)) | |
37 | rs -> return (Left (OtherError (show rs))) | |
38 | ||
39 | sqldel :: Database -> ByteString -> IO (Either TansuError ()) | |
40 | sqldel db k = do | |
41 | stmt <- prepare db "DELETE FROM tansu WHERE k = :k;" | |
42 | bindNamed stmt [ (":k", SQLBlob k) ] | |
43 | void (step stmt) | |
44 | finalize stmt | |
45 | return (Right ()) | |
46 | ||
47 | sqltransact :: Database -> IO a -> IO a | |
48 | sqltransact db mote = do | |
49 | exec db "BEGIN TRANSACTION;" | |
50 | result <- mote | |
51 | exec db "END TRANSACTION;" | |
52 | return result | |
53 | ||
54 | withSQLiteDb :: FilePath -> (TansuDb k v -> IO a) -> IO a | |
55 | withSQLiteDb path mote = do | |
56 | exists <- doesFileExist path | |
57 | conn <- open (T.pack path) | |
58 | when (not exists) $ do | |
59 | exec conn schema | |
60 | result <- mote $ TansuDb { dbGet = sqlget conn | |
61 | , dbSet = sqlset conn | |
62 | , dbDel = sqldel conn | |
63 | , dbRunTransaction = id | |
64 | } | |
65 | close conn | |
66 | return result |
1 | Copyright (c) 2016, Getty Ritter | |
2 | ||
3 | All rights reserved. | |
4 | ||
5 | Redistribution and use in source and binary forms, with or without | |
6 | modification, are permitted provided that the following conditions are met: | |
7 | ||
8 | * Redistributions of source code must retain the above copyright | |
9 | notice, this list of conditions and the following disclaimer. | |
10 | ||
11 | * Redistributions in binary form must reproduce the above | |
12 | copyright notice, this list of conditions and the following | |
13 | disclaimer in the documentation and/or other materials provided | |
14 | with the distribution. | |
15 | ||
16 | * Neither the name of Getty Ritter nor the names of other | |
17 | contributors may be used to endorse or promote products derived | |
18 | from this software without specific prior written permission. | |
19 | ||
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
1 | module Main where | |
2 | ||
3 | import Control.Monad (void) | |
4 | import Database.Tansu | |
5 | import Database.Tansu.Backend.SQLite3 | |
6 | ||
7 | main :: IO () | |
8 | main = void $ withSQLiteDb "sample.db" $ \ db -> do | |
9 | putStrLn "Populating test database" | |
10 | run db $ do | |
11 | "one" =: "un" | |
12 | "two" =: "du" | |
13 | "three" =: "tri" | |
14 | "four" =: "kvar" | |
15 | ||
16 | putStr "looking up key 'three': " | |
17 | rs <- run db $ get "three" | |
18 | case rs of | |
19 | Right val -> putStrLn val | |
20 | Left _ -> putStrLn "...not in the database." | |
21 | ||
22 | putStr "looking up key 'five': " | |
23 | rs <- run db $ get "five" | |
24 | case rs of | |
25 | Right val -> putStrLn val | |
26 | Left _ -> putStrLn "...not in the database." |
1 | name: tansu-sqlite3 | |
2 | version: 0.1.0.0 | |
3 | -- synopsis: | |
4 | -- description: | |
5 | license: BSD3 | |
6 | license-file: LICENSE | |
7 | author: Getty Ritter | |
8 | maintainer: gdritter@galois.com | |
9 | copyright: 2015 Getty Ritter | |
10 | category: Database | |
11 | build-type: Simple | |
12 | cabal-version: >=1.10 | |
13 | ||
14 | library | |
15 | exposed-modules: Database.Tansu.Backend.SQLite3 | |
16 | build-depends: base >=4.8 && <4.9, | |
17 | tansu, | |
18 | bytestring, | |
19 | text, | |
20 | directory, | |
21 | direct-sqlite | |
22 | default-language: Haskell2010 | |
23 | ||
24 | executable sample | |
25 | main-is: Main.hs | |
26 | build-depends: base, tansu, tansu-sqlite3 | |
27 | hs-source-dirs: sample | |
28 | default-language: Haskell2010 |