mysql-haskell 0.7.0.0 → 0.7.1.0
raw patch · 6 files changed
+96/−9 lines, 6 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Database.MySQL.Base: Many :: [MySQLValue] -> Param
+ Database.MySQL.Base: One :: MySQLValue -> Param
+ Database.MySQL.Base: class QueryParam a
+ Database.MySQL.Base: data Param
+ Database.MySQL.Base: render :: QueryParam a => a -> Put
- Database.MySQL.Base: execute :: MySQLConn -> Query -> [MySQLValue] -> IO OK
+ Database.MySQL.Base: execute :: QueryParam p => MySQLConn -> Query -> [p] -> IO OK
- Database.MySQL.Base: executeMany :: MySQLConn -> Query -> [[MySQLValue]] -> IO [OK]
+ Database.MySQL.Base: executeMany :: QueryParam p => MySQLConn -> Query -> [[p]] -> IO [OK]
- Database.MySQL.Base: query :: MySQLConn -> Query -> [MySQLValue] -> IO ([ColumnDef], InputStream [MySQLValue])
+ Database.MySQL.Base: query :: QueryParam p => MySQLConn -> Query -> [p] -> IO ([ColumnDef], InputStream [MySQLValue])
- Database.MySQL.Base: queryVector :: MySQLConn -> Query -> [MySQLValue] -> IO (Vector ColumnDef, InputStream (Vector MySQLValue))
+ Database.MySQL.Base: queryVector :: QueryParam p => MySQLConn -> Query -> [p] -> IO (Vector ColumnDef, InputStream (Vector MySQLValue))
- Database.MySQL.Base: renderParams :: Query -> [MySQLValue] -> Query
+ Database.MySQL.Base: renderParams :: QueryParam p => Query -> [p] -> Query
Files
- ChangeLog.md +5/−1
- Database/MySQL/Base.hs +18/−4
- Database/MySQL/Protocol/Auth.hs +2/−1
- Database/MySQL/Query.hs +33/−2
- mysql-haskell.cabal +1/−1
- test/TextRowNew.hs +37/−0
ChangeLog.md view
@@ -1,8 +1,12 @@ # Revision history for mysql-haskell +## 0.7.1.0 -- 2016-11-21++* Add `QueryParam` class and `Param` datatype for multi-valued parameter(s) by [naushadh](https://github.com/naushadh).+ ## 0.7.0.0 -- 2016-11-09 -* Split openssl support to [mysql-haskell-openssl](hackage.haskell.org/package/mysql-haskell-openssl).+* Split openssl support to [mysql-haskell-openssl](http://hackage.haskell.org/package/mysql-haskell-openssl). * Expose `Database.MySQL.Connection` module due to this split, it shouldn't be used by user directly. ## 0.6.0.0 -- 2016-10-25
Database/MySQL/Base.hs view
@@ -51,6 +51,8 @@ , resetStmt -- * Helpers , withTransaction+ , QueryParam(..)+ , Param (..) , Query(..) , renderParams , command@@ -95,9 +97,12 @@ -- will be escaped before get filled into the query, please DO NOT enable @NO_BACKSLASH_ESCAPES@, -- and you should consider using prepared statement if this's not an one shot query. ---execute :: MySQLConn -> Query -> [MySQLValue] -> IO OK+execute :: QueryParam p => MySQLConn -> Query -> [p] -> IO OK execute conn qry params = execute_ conn (renderParams qry params) +{-# SPECIALIZE execute :: MySQLConn -> Query -> [MySQLValue] -> IO OK #-}+{-# SPECIALIZE execute :: MySQLConn -> Query -> [Param] -> IO OK #-}+ -- | Execute a multi-row query which don't return result-set. -- -- Leverage MySQL's multi-statement support to do batch insert\/update\/delete,@@ -106,13 +111,16 @@ -- -- @since 0.2.0.0 ---executeMany :: MySQLConn -> Query -> [[MySQLValue]]-> IO [OK]+executeMany :: QueryParam p => MySQLConn -> Query -> [[p]] -> IO [OK] executeMany conn@(MySQLConn is os _ _) qry paramsList = do guardUnconsumed conn let qry' = L.intercalate ";" $ map (fromQuery . renderParams qry) paramsList writeCommand (COM_QUERY qry') os mapM (\ _ -> waitCommandReply is) paramsList +{-# SPECIALIZE executeMany :: MySQLConn -> Query -> [[MySQLValue]] -> IO [OK] #-}+{-# SPECIALIZE executeMany :: MySQLConn -> Query -> [[Param]] -> IO [OK] #-}+ -- | Execute a MySQL query which don't return a result-set. -- execute_ :: MySQLConn -> Query -> IO OK@@ -124,15 +132,21 @@ -- the same 'MySQLConn', or an 'UnconsumedResultSet' will be thrown. -- if you want to skip the result-set, use 'Stream.skipToEof'. ---query :: MySQLConn -> Query -> [MySQLValue] -> IO ([ColumnDef], InputStream [MySQLValue])+query :: QueryParam p => MySQLConn -> Query -> [p] -> IO ([ColumnDef], InputStream [MySQLValue]) query conn qry params = query_ conn (renderParams qry params) +{-# SPECIALIZE query :: MySQLConn -> Query -> [MySQLValue] -> IO ([ColumnDef], InputStream [MySQLValue]) #-}+{-# SPECIALIZE query :: MySQLConn -> Query -> [Param] -> IO ([ColumnDef], InputStream [MySQLValue]) #-}+ -- | 'V.Vector' version of 'query'. -- -- @since 0.5.1.0 ---queryVector :: MySQLConn -> Query -> [MySQLValue] -> IO (V.Vector ColumnDef, InputStream (V.Vector MySQLValue))+queryVector :: QueryParam p => MySQLConn -> Query -> [p] -> IO (V.Vector ColumnDef, InputStream (V.Vector MySQLValue)) queryVector conn qry params = queryVector_ conn (renderParams qry params)++{-# SPECIALIZE queryVector :: MySQLConn -> Query -> [MySQLValue] -> IO (V.Vector ColumnDef, InputStream (V.Vector MySQLValue)) #-}+{-# SPECIALIZE queryVector :: MySQLConn -> Query -> [Param] -> IO (V.Vector ColumnDef, InputStream (V.Vector MySQLValue)) #-} -- | Execute a MySQL query which return a result-set. --
Database/MySQL/Protocol/Auth.hs view
@@ -97,13 +97,14 @@ status <- getWord16le capH <- getWord16le let cap = fromIntegral capH `shiftL` 16 .|. fromIntegral capL- authPluginLen <- getWord8+ authPluginLen <- getWord8 -- this will issue an unused warning, see the notes below skipN 10 -- 10 * 0x00 salt2 <- if (cap .&. CLIENT_SECURE_CONNECTION) == 0 then pure B.empty else getByteStringNul -- This is different with the MySQL document here -- The doc said we should expect a MAX(13, length of auth-plugin-data - 8) -- length bytes, but doing so stop us from login+ -- anyway 'getByteStringNul' works perfectly here. authPlugin <- if (cap .&. CLIENT_PLUGIN_AUTH) == 0 then pure B.empty
Database/MySQL/Query.hs view
@@ -36,13 +36,44 @@ instance IsString Query where fromString = Query . BB.toLazyByteString . BB.stringUtf8 -renderParams :: Query -> [MySQLValue] -> Query+-- | A type to wrap a query parameter in to allow for single and multi-valued parameters.+--+-- The behavior of 'Param' can be illustrated by following example:+--+-- @+-- render $ One (MySQLText "hello") = hello+-- render $ Many [MySQLText "hello", MySQLText "world"] = hello, world+-- render $ Many [] = null+-- @+--+-- So you can now write a query like this: @ SELECT * FROM test WHERE _id IN (?, 888) @+-- and use 'Many' 'Param' to fill the hole. There's no equivalent for prepared statement sadly.+--+data Param = One MySQLValue+ | Many [MySQLValue]++-- | A type that may be used as a single parameter to a SQL query. Inspired from @mysql-simple@.+class QueryParam a where+ render :: a -> Put+ -- ^ Prepare a value for substitution into a query string.++instance QueryParam Param where+ render (One x) = putTextField x+ render (Many []) = putTextField MySQLNull+ render (Many (x:[]))= putTextField x+ render (Many (x:xs))= do putTextField x+ mapM_ (\f -> putCharUtf8 ',' >> putTextField f) xs++instance QueryParam MySQLValue where+ render = putTextField++renderParams :: QueryParam p => Query -> [p] -> Query renderParams (Query qry) params = let fragments = LC.split '?' qry in Query . runPut $ merge fragments params where merge [x] [] = putLazyByteString x- merge (x:xs) (y:ys) = putLazyByteString x >> putTextField y >> merge xs ys+ merge (x:xs) (y:ys) = putLazyByteString x >> render y >> merge xs ys merge _ _ = throw WrongParamsCount data WrongParamsCount = WrongParamsCount deriving (Show, Typeable)
mysql-haskell.cabal view
@@ -1,5 +1,5 @@ name: mysql-haskell-version: 0.7.0.0+version: 0.7.1.0 synopsis: pure haskell MySQL driver description: pure haskell MySQL driver license: BSD3
test/TextRowNew.hs view
@@ -107,3 +107,40 @@ ] Stream.skipToEof is++ let row0 =+ [ MySQLInt32 0+ , MySQLDateTime (LocalTime (fromGregorian 2016 08 08) (TimeOfDay 17 25 59.10))+ , MySQLTimeStamp (LocalTime (fromGregorian 2016 08 08) (TimeOfDay 17 25 59.12))+ , MySQLTime 0 (TimeOfDay 199 59 59.1234)+ ]+ let row1 =+ [ MySQLInt32 1+ , MySQLDateTime (LocalTime (fromGregorian 2016 08 09) (TimeOfDay 18 25 59.10))+ , MySQLTimeStamp (LocalTime (fromGregorian 2016 08 09) (TimeOfDay 18 25 59.12))+ , MySQLTime 0 (TimeOfDay 299 59 59.1234)+ ]+ execute c "UPDATE test_new SET \+ \__id = ? ,\+ \__datetime = ? ,\+ \__timestamp = ? ,\+ \__time = ? WHERE __id=0"+ row0+ execute c "INSERT INTO test_new VALUES(\+ \?,\+ \?,\+ \?,\+ \? \+ \)"+ row1++ (_, is) <- query c "SELECT * FROM test_new WHERE __id IN (?) ORDER BY __id" [Many [MySQLInt32 0, MySQLInt32 1]]+ Just v0 <- Stream.read is+ Just v1 <- Stream.read is++ assertEqual "select list of ids" [v0, v1] [row0, row1]++ Stream.skipToEof is+ execute_ c "DELETE FROM test_new where __id=1"++ return ()