sqlite-simple 0.1.0.1 → 0.1.0.2
raw patch · 7 files changed
+100/−91 lines, 7 filesdep +old-localedep ~direct-sqlite
Dependencies added: old-locale
Dependency ranges changed: direct-sqlite
Files
- Database/SQLite/Simple.hs +12/−86
- Database/SQLite/Simple/FromField.hs +8/−1
- Database/SQLite/Simple/ToField.hs +3/−1
- sqlite-simple.cabal +5/−2
- test/Main.hs +4/−0
- test/Simple.hs +38/−1
- test/TestImports.hs +30/−0
Database/SQLite/Simple.hs view
@@ -39,9 +39,15 @@ -- ** Type conversions -- $types+ Query(..)+ , Connection+ , ToRow(..)+ , FromRow(..)+ , Only(..)+ , (:.)(..) -- * Connections- open+ , open , close -- * Queries that return results , query@@ -50,12 +56,6 @@ , execute , execute_ , field- , Query- , Connection- , ToRow- , FromRow- , Only(..)- , (:.)(..) -- ** Exceptions , FormatError(fmtMessage, fmtQuery, fmtParams) , ResultError(errSQLType, errHaskellType, errMessage)@@ -148,9 +148,6 @@ -- -- * 'FormatError': the query string mismatched with given arguments. ----- * 'QueryError': the result contains no columns (i.e. you should be--- using 'execute' instead of 'query').--- -- * 'ResultError': result conversion failed. query :: (ToRow q, FromRow r) => Connection -> Query -> q -> IO [r]@@ -254,7 +251,7 @@ -- > import Database.SQLite.Simple -- > -- > hello = do--- > conn <- connect defaultConnectInfo+-- > conn <- open "test.db" -- > query conn "select 2 + 2" -- -- A 'Query' value does not represent the actual query that will be@@ -327,85 +324,14 @@ -- Haskell lacks a single-element tuple type, so if you have just one -- value you want substituted into a query, what should you do? ----- The obvious approach would appear to be something like this:------ > instance (Param a) => QueryParam a where--- > ...------ Unfortunately, this wreaks havoc with type inference, so we take a--- different tack. To represent a single value @val@ as a parameter, write--- a singleton list @[val]@, use 'Just' @val@, or use 'Only' @val@.+-- To represent a single value @val@ as a parameter, write a singleton+-- list @[val]@, use 'Just' @val@, or use 'Only' @val@. -- -- Here's an example using a singleton list: -- -- > execute conn "insert into users (first_name) values (?)" -- > ["Nuala"] --- $in------ Suppose you want to write a query using an @IN@ clause:------ > select * from users where first_name in ('Anna', 'Boris', 'Carla')------ In such cases, it's common for both the elements and length of the--- list after the @IN@ keyword to vary from query to query.------ To address this case, use the 'In' type wrapper, and use a single--- \"@?@\" character to represent the list. Omit the parentheses--- around the list; these will be added for you.------ Here's an example:------ > query conn "select * from users where first_name in ?" $--- > In ["Anna", "Boris", "Carla"]------ If your 'In'-wrapped list is empty, the string @\"(null)\"@ will be--- substituted instead, to ensure that your clause remains--- syntactically valid.---- $many------ If you know that you have many rows of data to insert into a table,--- it is much more efficient to perform all the insertions in a single--- multi-row @INSERT@ statement than individually.------ The 'executeMany' function is intended specifically for helping--- with multi-row @INSERT@ and @UPDATE@ statements. Its rules for--- query substitution are different than those for 'execute'.------ What 'executeMany' searches for in your 'Query' template is a--- single substring of the form:------ > values (?,?,?)------ The rules are as follows:------ * The keyword @VALUES@ is matched case insensitively.------ * There must be no other \"@?@\" characters anywhere in your--- template.------ * There must one or more \"@?@\" in the parentheses.------ * Extra white space is fine.------ The last argument to 'executeMany' is a list of parameter--- tuples. These will be substituted into the query where the @(?,?)@--- string appears, in a form suitable for use in a multi-row @INSERT@--- or @UPDATE@.------ Here is an example:------ > executeMany conn--- > "insert into users (first_name,last_name) values (?,?)"--- > [("Boris","Karloff"),("Ed","Wood")]------ The query that will be executed here will look like this--- (reformatted for tidiness):------ > insert into users (first_name,last_name) values--- > ('Boris','Karloff'),('Ed','Wood')- -- $result -- -- The 'query' and 'query_' functions return a list of values in the@@ -414,11 +340,11 @@ -- -- Here is a simple example of how to extract results: ----- > import qualified Data.Text as Text+-- > import qualified Data.Text as T -- > -- > xs <- query_ conn "select name,age from users" -- > forM_ xs $ \(name,age) ->--- > putStrLn $ Text.unpack name ++ " is " ++ show (age :: Int)+-- > putStrLn $ T.unpack name ++ " is " ++ show (age :: Int) -- -- Notice two important details about this code: --
Database/SQLite/Simple/FromField.hs view
@@ -39,9 +39,12 @@ import qualified Data.ByteString.Char8 as B import Data.Int (Int16, Int32, Int64) import Data.Time (UTCTime, Day)+import Data.Time.Format (parseTime) import qualified Data.Text as T import Data.Typeable (Typeable, typeOf) +import System.Locale (defaultTimeLocale)+ import Database.SQLite3 as Base import Database.SQLite.Simple.Types import Database.SQLite.Simple.Internal@@ -138,7 +141,11 @@ fromField f = returnError ConversionFailed f "expecting SQLBlob column type" instance FromField UTCTime where- fromField (Field (SQLText t) _) = Ok . read . T.unpack $ t+ fromField f@(Field (SQLText t) _) =+ case parseTime defaultTimeLocale "%F %X%Q" . T.unpack $ t of+ Just t -> Ok t+ Nothing -> returnError ConversionFailed f "couldn't parse UTCTime field"+ fromField f = returnError ConversionFailed f "expecting SQLText column type" instance FromField Day where
Database/SQLite/Simple/ToField.hs view
@@ -25,8 +25,10 @@ import qualified Data.Text as T import qualified Data.Text.Lazy as LT import Data.Time (Day, UTCTime)+import Data.Time.Format (formatTime) import Data.Word (Word, Word8, Word16, Word32, Word64) import GHC.Float+import System.Locale (defaultTimeLocale) import Database.SQLite3 as Base import Database.SQLite.Simple.Types (Null)@@ -127,7 +129,7 @@ {-# INLINE toField #-} instance ToField UTCTime where- toField = SQLText . T.pack . show+ toField = SQLText . T.pack . formatTime defaultTimeLocale "%F %X%Q" {-# INLINE toField #-} instance ToField Day where
sqlite-simple.cabal view
@@ -1,5 +1,5 @@ Name: sqlite-simple-Version: 0.1.0.1+Version: 0.1.0.2 Synopsis: Mid-Level SQLite client library Description: Mid-level SQLite client library, based on postgresql-simple.@@ -39,9 +39,10 @@ base < 5, bytestring >= 0.9, containers,- direct-sqlite >= 2.0,+ direct-sqlite >= 2.0 && < 2.1, text >= 0.11.1, time,+ old-locale >= 1.0.0.0, transformers default-extensions:@@ -70,6 +71,7 @@ Errors ParamConv Utf8Strings+ TestImports ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-unused-do-bind @@ -84,6 +86,7 @@ , bytestring , HUnit , sqlite-simple+ , direct-sqlite , text , time
test/Main.hs view
@@ -10,11 +10,15 @@ import Errors import Utf8Strings +import TestImports()+ tests :: [TestEnv -> Test] tests = [ TestLabel "Simple" . testSimpleSelect , TestLabel "Simple" . testSimpleOnePlusOne , TestLabel "Simple" . testSimpleParams+ , TestLabel "Simple" . testSimpleTime+ , TestLabel "Simple" . testSimpleTimeFract , TestLabel "ParamConv" . testParamConvInt , TestLabel "ParamConv" . testParamConvFloat , TestLabel "ParamConv" . testParamConvDateTime
test/Simple.hs view
@@ -1,9 +1,14 @@+{-# LANGUAGE OverloadedStrings #-} module Simple ( testSimpleOnePlusOne , testSimpleSelect- , testSimpleParams) where+ , testSimpleParams+ , testSimpleTime+ , testSimpleTimeFract) where +import qualified Data.Text as T+import Data.Time (UTCTime) import Common -- Simplest SELECT@@ -50,3 +55,35 @@ assertEqual "select params" "test2" row [Only i] <- query conn "SELECT ?+?" [42 :: Int, 1 :: Int] :: IO [Only Int] assertEqual "select int param" 43 i++testSimpleTime :: TestEnv -> Test+testSimpleTime TestEnv{..} = TestCase $ do+ let timestr = "2012-08-20 20:19:58"+ time = read timestr :: UTCTime+ execute_ conn "CREATE TABLE time (t TIMESTAMP)"+ execute conn "INSERT INTO time (t) VALUES (?)" (Only time)+ [Only t] <- query_ conn "SELECT * FROM time" :: IO [Only UTCTime]+ assertEqual "UTCTime conv" time t+ [Only t] <- query conn "SELECT * FROM time WHERE t = ?" (Only time) :: IO [Only UTCTime]+ assertEqual "UTCTime conv2" time t+ -- Try inserting timestamp directly as a string+ execute_ conn "CREATE TABLE time2 (t TIMESTAMP)"+ execute_ conn (Query (T.concat ["INSERT INTO time2 (t) VALUES ('", T.pack timestr, "')"]))+ [Only t] <- query_ conn "SELECT * FROM time2" :: IO [Only UTCTime]+ assertEqual "UTCTime" time t+ rows <- query conn "SELECT * FROM time2 WHERE t = ?" (Only time) :: IO [Only UTCTime]+ assertEqual "should see one row result" 1 (length rows)+ assertEqual "UTCTime" time t++testSimpleTimeFract :: TestEnv -> Test+testSimpleTimeFract TestEnv{..} = TestCase $ do+ let timestr = "2012-08-17 08:00:03.256887"+ time = read timestr :: UTCTime+ -- Try inserting timestamp directly as a string+ execute_ conn "CREATE TABLE timefract (t TIMESTAMP)"+ execute_ conn (Query (T.concat ["INSERT INTO timefract (t) VALUES ('", T.pack timestr, "')"]))+ [Only t] <- query_ conn "SELECT * FROM timefract" :: IO [Only UTCTime]+ assertEqual "UTCTime" time t+ rows <- query conn "SELECT * FROM timefract WHERE t = ?" (Only time) :: IO [Only UTCTime]+ assertEqual "should see one row result" 1 (length rows)+ assertEqual "UTCTime" time t
+ test/TestImports.hs view
@@ -0,0 +1,30 @@++module TestImports where++-- Test file to test that we can do most things with a single import+import Control.Applicative+import qualified Data.Text as T+import Database.SQLite.Simple++data Test = Test Int Int Int++-- Hook up sqlite-simple to know how to read Nvbugs+instance FromRow Test where+ fromRow = Test <$> field <*> field <*> field++foo :: IO ()+foo = do+ conn <- open ":memory:"+ [Only _v] <- query_ conn "SELECT * FROM test" :: IO [Only Int]+ [_v] <- query_ conn "SELECT * FROM test" :: IO [(Int,Int)]+ [_v] <- query_ conn "SELECT * FROM test" :: IO [Test]+ [_v] <- query conn "SELECT ?+?" (3::Int,4::Int):: IO [(Only Int)]+ close conn++foo2 :: IO ()+foo2 = do+ conn <- open ":memory:"+ [Only _v] <- query_ conn (Query q) :: IO [Only Int]+ close conn+ where+ q = T.concat ["SELECT * FROM ", "test"]