packages feed

mysql-haskell 0.4.0.0 → 0.4.1.0

raw patch · 6 files changed

+85/−26 lines, 6 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Database.MySQL.BinLog: RowDeleteEvent :: !Word32 -> !BinLogTracker -> !TableMapEvent -> !DeleteRowsEvent -> RowBinLogEvent
+ Database.MySQL.BinLog: RowDeleteEvent :: {-# UNPACK #-} !Word32 -> !BinLogTracker -> !TableMapEvent -> !DeleteRowsEvent -> RowBinLogEvent
- Database.MySQL.BinLog: RowQueryEvent :: !Word32 -> !BinLogTracker -> !QueryEvent' -> RowBinLogEvent
+ Database.MySQL.BinLog: RowQueryEvent :: {-# UNPACK #-} !Word32 -> !BinLogTracker -> !QueryEvent' -> RowBinLogEvent
- Database.MySQL.BinLog: RowUpdateEvent :: !Word32 -> !BinLogTracker -> !TableMapEvent -> !UpdateRowsEvent -> RowBinLogEvent
+ Database.MySQL.BinLog: RowUpdateEvent :: {-# UNPACK #-} !Word32 -> !BinLogTracker -> !TableMapEvent -> !UpdateRowsEvent -> RowBinLogEvent
- Database.MySQL.BinLog: RowWriteEvent :: !Word32 -> !BinLogTracker -> !TableMapEvent -> !WriteRowsEvent -> RowBinLogEvent
+ Database.MySQL.BinLog: RowWriteEvent :: {-# UNPACK #-} !Word32 -> !BinLogTracker -> !TableMapEvent -> !WriteRowsEvent -> RowBinLogEvent

Files

Database/MySQL/Base.hs view
@@ -149,15 +149,15 @@     else do         StmtPrepareOK stid colCnt paramCnt _ <- getFromPacket getStmtPrepareOK p         _ <- replicateM_ paramCnt (readPacket is)-        _ <- unless (colCnt == 0) (void (readPacket is))  -- EOF-        _ <- replicateM_ colCnt (readPacket is)         _ <- unless (paramCnt == 0) (void (readPacket is))  -- EOF+        _ <- replicateM_ colCnt (readPacket is)+        _ <- unless (colCnt == 0) (void (readPacket is))  -- EOF         return stid  -- | Ask MySQL to prepare a query statement. -- -- All details from @COM_STMT_PREPARE@ Response are returned: the 'StmtPrepareOK' packet,--- params's 'ColumnDef', table's 'ColumnDef'.+-- params's 'ColumnDef', result's 'ColumnDef'. -- prepareStmtDetail :: MySQLConn -> Query -> IO (StmtPrepareOK, [ColumnDef], [ColumnDef]) prepareStmtDetail conn@(MySQLConn is os _ _) (Query stmt) = do@@ -169,9 +169,9 @@     else do         sOK@(StmtPrepareOK _ colCnt paramCnt _) <- getFromPacket getStmtPrepareOK p         pdefs <- replicateM paramCnt ((decodeFromPacket <=< readPacket) is)-        _ <- unless (colCnt == 0) (void (readPacket is))  -- EOF-        cdefs <- replicateM colCnt ((decodeFromPacket <=< readPacket) is)         _ <- unless (paramCnt == 0) (void (readPacket is))  -- EOF+        cdefs <- replicateM colCnt ((decodeFromPacket <=< readPacket) is)+        _ <- unless (colCnt == 0) (void (readPacket is))  -- EOF         return (sOK, pdefs, cdefs)  -- | Ask MySQL to closed a query statement.
Database/MySQL/BinLog.hs view
@@ -124,22 +124,22 @@             | isEOF p -> return Nothing             | isERR p -> decodeFromPacket p >>= throwIO . ERRException --- | Row based biblog event type.+-- | Row based binlog event type. -- -- It's recommended to enable row query event before 'dumpBinLog', so that you can get -- 'RowQueryEvent' in row based binlog(it's important for detect a table change for example), -- more information please refer <http://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_binlog_rows_query_log_events sysvar_binlog_rows_query_log_events> ----- a 'BinLogTracker' is included so that you can roll up your own HA solutions,+-- A 'BinLogTracker' is included so that you can roll up your own HA solutions, -- for example, writing the tracker to zookeeper when you done with an event. -- -- The first 'Word32' field is a timestamp present when this event is logged. -- data RowBinLogEvent-    = RowQueryEvent  !Word32 !BinLogTracker !QueryEvent'-    | RowDeleteEvent !Word32 !BinLogTracker !TableMapEvent !DeleteRowsEvent-    | RowWriteEvent  !Word32 !BinLogTracker !TableMapEvent !WriteRowsEvent-    | RowUpdateEvent !Word32 !BinLogTracker !TableMapEvent !UpdateRowsEvent+    = RowQueryEvent  {-# UNPACK #-} !Word32 !BinLogTracker !QueryEvent'+    | RowDeleteEvent {-# UNPACK #-} !Word32 !BinLogTracker !TableMapEvent !DeleteRowsEvent+    | RowWriteEvent  {-# UNPACK #-} !Word32 !BinLogTracker !TableMapEvent !WriteRowsEvent+    | RowUpdateEvent {-# UNPACK #-} !Word32 !BinLogTracker !TableMapEvent !UpdateRowsEvent   deriving (Show, Eq, Generic)  -- | decode row based event from 'BinLogPacket' stream.
Database/MySQL/BinLogProtocol/BinLogValue.hs view
@@ -11,7 +11,6 @@  -} - module Database.MySQL.BinLogProtocol.BinLogValue where  import           Control.Applicative@@ -47,7 +46,8 @@ -- see 'MySQLVaule' 's note. -- -- There's also no infomation about charset, so we use 'ByteString' to present both text--- and blob types.+-- and blob types, if you want to get text representation back, you have to query column charset+-- infomation, and use icu or iconv to decode. IT MAY NOT BE UTF-8. -- -- The @SET@ and @ENUM@ values are presented by their index's value and bitmap respectively, -- if you need get the string value back, you have to perform a 'DESC tablename' to get the@@ -93,7 +93,7 @@ getBinLogField (BINLOG_TYPE_BIT    _    bytes) = BinLogBit <$> getBits' bytes getBinLogField BINLOG_TYPE_TIMESTAMP           = BinLogTimeStamp <$> getWord32le --- ^ a integer in @YYYYMMDD@ format, for example:+-- A integer in @YYYYMMDD@ format, for example: -- 99991231 stand for @9999-12-31@ getBinLogField BINLOG_TYPE_DATE = do     i <- getWord24le@@ -108,7 +108,7 @@     ms <- fromIntegral <$> getMicroSecond fsp     pure (BinLogTimeStamp2 s ms) --- a integer in @YYYYMMDDhhmmss@, for example:+-- A integer in @YYYYMMDDhhmmss@, for example: -- 99991231235959 stand for @9999-12-31 23:59:59@ getBinLogField BINLOG_TYPE_DATETIME = do     i <- getWord64le@@ -149,7 +149,7 @@     ms <- fromIntegral <$> getMicroSecond fsp     pure (BinLogDateTime2 yyyy' mm' dd h m s ms) --- ^ a integer in @hhmmss@ format(can be negative), for example:+-- A integer in @hhmmss@ format(can be negative), for example: -- 8385959 stand for @838:59:59@ getBinLogField BINLOG_TYPE_TIME = do     i <- getWord24le
Database/MySQL/Connection.hs view
@@ -48,7 +48,7 @@  -- | Everything you need to establish a MySQL connection. ----- You may want some helpers in "System.IO.Streams.TLS" to setup TLS connection.+-- To setup a TLS connection, use module "Database.MySQL.TLS" or "Database.MySQL.OpenSSL". -- data ConnectInfo = ConnectInfo     { ciHost     :: HostName@@ -104,10 +104,10 @@     in Auth clientCap clientMaxPacketSize clientCharset user scambleBuf db   where     scramble :: ByteString -> ByteString -> ByteString-    scramble salt pass-        | B.null pass = B.empty+    scramble salt pass'+        | B.null pass' = B.empty         | otherwise   = B.pack (B.zipWith xor sha1pass withSalt)-        where sha1pass = sha1 pass+        where sha1pass = sha1 pass'               withSalt = sha1 (salt `B.append` sha1 sha1pass)      sha1 :: ByteString -> ByteString@@ -125,18 +125,18 @@     return . Just $ Packet len seqN body   where     loopRead acc 0 _  = return $! L.fromChunks (reverse acc)-    loopRead acc k is = do-        bs <- Stream.read is+    loopRead acc k is' = do+        bs <- Stream.read is'         case bs of Nothing -> throwIO NetworkException                    Just bs' -> do let l = fromIntegral (B.length bs')                                   if l >= k                                   then do                                       let (a, rest) = B.splitAt (fromIntegral k) bs'-                                      unless (B.null rest) (Stream.unRead rest is)+                                      unless (B.null rest) (Stream.unRead rest is')                                       return $! L.fromChunks (reverse (a:acc))                                   else do                                       let k' = k - l-                                      k' `seq` loopRead (bs':acc) k' is+                                      k' `seq` loopRead (bs':acc) k' is'  -- | Close a MySQL connection. --
README.md view
@@ -11,7 +11,7 @@ Is it fast? ---------- -In short, `select`(decode) is about 2 times slower than pure c/c++, `insert` (encode) is about 1.5 times slower than pure c/c++, there're many factors involved(tls, prepared statment, batch using multiple statement):+In short, `select`(decode) is about 2 times slower than pure c/c++ but 5 times faster than `mysql-simple`, `insert` (encode) is about 1.5 times slower than pure c/c++, there're many factors involved(tls, prepared statment, batch using multiple statement):  <img src="https://github.com/winterland1989/mysql-haskell/blob/master/benchmark/result.png?raw=true" width="100%"> @@ -34,6 +34,65 @@ + no replication protocol support.  `mysql-haskell` is intended to solve these problems, and provide foundation for higher level libraries such as groundhog and persistent, so that accessing MySQL is both fast and easy in haskell.++Guide+-----++The `Database.MySQL.Base` module provides everything you need to start making queries:++```haskell+{-# LANGUAGE OverloadedStrings #-}++module Main where++import Database.MySQL.Base+import qualified System.IO.Streams as Streams++main :: IO () +main = do+    conn <- connect+        defaultConnectInfo {ciUser = "username", ciPassword = "password", ciDatabase = "dbname"}+    (defs, is) <- query_ conn "SELECT * FROM some_table"+    print =<< Streams.toList is+```++`query/query_` will return a column definition list, and an `InputStream` of rows, you should consume this stream completely before start new queries.++It's recommanded to use prepared statement to improve query speed:++```haskell+    ...+    s <- prepareStmt conn "SELECT * FROM some_table where person_age > ?"+    ...+    (defs, is) <- queryStmt s [MySQLInt32U 18]+    ...+```++If you want to do batch inserting/deleting/updating, you can use `executeMany` to save considerable time.++The `Database.MySQL.BinLog` module provides binlog listenning functions and row-based event decoder, following program will automatically get last binlog position, and print every row event it receives:++```haskell+{-# LANGUAGE LambdaCase #-}+module Main where++import           Control.Monad         (forever)+import qualified Database.MySQL.BinLog as MySQL+import qualified System.IO.Streams     as Streams++main :: IO () +main = do+    conn <- MySQL.connect +        MySQL.defaultConnectInfo {ciUser = "username", ciPassword = "password", ciDatabase = "dbname"}+    MySQL.getLastBinLogTracker conn >>= \ case+        Just tracker -> do+            es <- MySQL.decodeRowBinLogEvent =<< MySQL.dumpBinLog conn 1024 tracker False+            forever $ do+                Streams.read es >>= \ case+                    Just v  -> print v+                    Nothing -> return ()+        Nothing -> error "can't get latest binlog position"+```  Build Test Benchmark --------------------
mysql-haskell.cabal view
@@ -1,5 +1,5 @@ name:                mysql-haskell-version:             0.4.0.0+version:             0.4.1.0 synopsis:            pure haskell MySQL driver description:         pure haskell MySQL driver license:             BSD3