HDBC-mysql 0.6.3 → 0.6.4.0
raw patch · 6 files changed
+286/−42 lines, 6 filesdep ~basenew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
+ Database.HDBC.MySQL: withRTSSignalsBlocked :: IO a -> IO a
Files
- ChangeLog +48/−0
- Database/HDBC/MySQL.hs +49/−26
- Database/HDBC/MySQL/Connection.hsc +10/−7
- Database/HDBC/MySQL/RTS.hsc +56/−0
- HDBC-mysql.cabal +33/−9
- README.markdown +90/−0
+ ChangeLog view
@@ -0,0 +1,48 @@+2010-06-01 Chris Waterson <waterson@maubi.net>++ * Database/HDBC/MySQL/Connection.hsc. Add runRaw, patch from Rune+ Harder Bak <rune@bak.dk>.++2010-04-16 Chris Waterson <waterson@maubi.net>++ * HDBC-mysql-0.6.2. Fix incorrect usage of "with" and+ "withCString" to allocate buffers for libmysqlclient. Send+ SqlString data as UTF-8 encoded byte strings; return string data+ as SqlByteStrings.++2009-03-01 Chris Waterson <waterson@maubi.net>++ * HDBC-mysql-0.6.1. Expose Connection type.++2009-06-05 Chris Waterson <waterson@maubi.net>++ * HDBC-mysql-0.6. Use Foreign.Concurrent to correctly incorporate+ Haskell finalizer for binds. Fix handling of SqlWord32 and+ SqlWord64 values: tip o' the cap to yeoh@cs.wisc.edu.++2009-03-02 Chris Waterson <waterson@maubi.net>++ * HDBC-mysql-0.5. Use "throwSqlError" instead of "throwDyn" so+ that we get legible errors under ghc-6.10. This also removes the+ dependency on base 3.++2009-02-26 Chris Waterson <waterson@maubi.net>++ * HDBC-mysql-0.4. Fix incorrect binding size (should always be 4+ bytes, not compiler's "long") for MYSQL_TYPE_LONG columns.++2009-02-18 Chris Waterson <waterson@maubi.net>++ * HDBC-mysql-0.3 Deal with some of the problems where statements+ would mysteriously "close". It turns out that only one statement+ can be "active" on a connection at a time, and that closing an+ inactive statement causes the current statement to close as well.++2009-02-14 Chris Waterson <waterson@maubi.net>++ * HDBC-mysql-0.2 Fix binding width for dates: MySQL's length+ indicates 19 characters (which is the length of the string), but+ we need the full size of the MYSQL_TIME struct. Leak connections+ and statements until I can figure out how to keep the ForeignPtr's+ alive.+
Database/HDBC/MySQL.hs view
@@ -1,42 +1,65 @@ {- | +Module : Database.HDBC.MySQL+Copyright : Copyright (c) 2009-2010 Chris Waterson+License : LGPL+Maintainer : bos@mailrank.com+Stability : experimental+Portability : GHC+ This module provides a MySQL driver for the HDBC database interface. To use it, invoke the 'connectMySQL' method to create an @Database.HDBC.IConnection@ that you can use to interact with a MySQL database. Use the 'defaultMySQLConnectInfo', overriding the default values as necessary. -> import Control.Monad-> import Database.HDBC-> import Database.HDBC.MySQL-> main = do conn <- connectMySQL defaultMySQLConnectInfo {-> mysqlHost = "db1.example.com",-> mysqlUser = "scott",-> mysqlPassword = "tiger"-> }->-> rows <- quickQuery' conn "SELECT 1 + 1" []-> forM_ rows $ \row -> putStrLn $ show row+@+import Control.Monad+import Database.HDBC+import Database.HDBC.MySQL +main = do+ rows <- 'withRTSSignalsBlocked' $ do+ conn <- 'connectMySQL' 'defaultMySQLConnectInfo' {+ 'mysqlHost' = \"db1.example.com\",+ 'mysqlUser' = \"scott\",+ 'mysqlPassword' = \"tiger\"+ }+ 'quickQuery'' conn \"SELECT 1 + 1\" []+ forM_ rows $ \row -> putStrLn $ show row+@+ There are some important caveats to note about this driver. -The first relates to transaction support. The MySQL server supports a-variety of backend \"engines\", only some of which support-transactional access (e.g., InnoDB). This driver will report that the-database supports transactions. Should you decide to make use of the-transactional support in the HDBC API,-/it is up to you to make sure that you use a MySQL engine that supports transactions/.+* RTS signals. If you are writing an application that links against GHC's+ threaded runtime (as most server-side applications do), you must use+ 'withRTSSignalsBlocked' to defend the @mysqlclient@ library against the+ signals the RTS uses, or you may experience crashes. -The next relates to dates and times. MySQL does not store time zone-information in @DATETIME@ or @TIMESTAMP@ columns: instead, it assumes-that all dates are stored in the \"server's time zone\". At some-point in the future, this driver may query for the server's time zone-and apply appropriate time zone conversion to these datatypes. For-now, it simply treats all times as UTC; i.e., it assumes the server's-time zone is UTC.+* Transaction support. The MySQL server supports a+ variety of backend \"engines\", only some of which support+ transactional access (e.g., InnoDB). This driver will report that the+ database supports transactions. Should you decide to make use of the+ transactional support in the HDBC API,+ /it is up to you to make sure that you use a MySQL engine that supports transactions/. +* Dates and times. MySQL does not store time zone+ information in @DATETIME@ or @TIMESTAMP@ columns: instead, it assumes+ that all dates are stored in the \"server's time zone\". At some+ point in the future, this driver may query for the server's time zone+ and apply appropriate time zone conversion to these datatypes. For+ now, it simply treats all times as UTC; i.e., it assumes the server's+ time zone is UTC.+ -} module Database.HDBC.MySQL- (connectMySQL, MySQLConnectInfo(..), defaultMySQLConnectInfo, Connection)-where+ (+ MySQLConnectInfo(..)+ , Connection+ , connectMySQL+ , defaultMySQLConnectInfo+ , withRTSSignalsBlocked+ ) where+ import Database.HDBC.MySQL.Connection+import Database.HDBC.MySQL.RTS
Database/HDBC/MySQL/Connection.hsc view
@@ -1,9 +1,12 @@--- -*- mode: haskell; -*--{-# OPTIONS -fglasgow-exts #-}+{-# LANGUAGE EmptyDataDecls, ForeignFunctionInterface, ScopedTypeVariables #-} module Database.HDBC.MySQL.Connection- (connectMySQL, MySQLConnectInfo(..), defaultMySQLConnectInfo, Connection)-where+ (+ connectMySQL+ , MySQLConnectInfo(..)+ , defaultMySQLConnectInfo+ , Connection+ ) where import Control.Exception import Control.Monad@@ -122,7 +125,7 @@ -- HDBC assumes that there is no such thing as auto-commit. -- So we'll turn it off here and start our first transaction.- mysql_autocommit mysql_ 0+ _ <- mysql_autocommit mysql_ 0 mysql__ <- newForeignPtr mysql_close mysql_ doStartTransaction mysql__@@ -671,7 +674,7 @@ doGetTables :: ForeignPtr MYSQL -> IO [String] doGetTables mysql__ = do stmt <- newStatement mysql__ "SHOW TABLES"- Types.execute stmt []+ _ <- Types.execute stmt [] rows <- unfoldRows stmt Types.finish stmt return $ map (fromSql . head) rows@@ -686,7 +689,7 @@ doDescribeTable :: ForeignPtr MYSQL -> String -> IO [(String, ColTypes.SqlColDesc)] doDescribeTable mysql__ table = do stmt <- newStatement mysql__ ("DESCRIBE " ++ table)- Types.execute stmt []+ _ <- Types.execute stmt [] rows <- unfoldRows stmt Types.finish stmt return $ map fromRow rows
+ Database/HDBC/MySQL/RTS.hsc view
@@ -0,0 +1,56 @@+{-# LANGUAGE EmptyDataDecls, ForeignFunctionInterface #-}++module Database.HDBC.MySQL.RTS (withRTSSignalsBlocked) where++import Control.Concurrent (runInBoundThread)+import Control.Exception (finally)+import Foreign.C.Types (CInt)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (Ptr, nullPtr)+import Foreign.Storable (Storable(..))++#include <signal.h>++-- | Execute an 'IO' action with signals used by GHC's runtime signals+-- blocked. The @mysqlclient@ C library does not correctly restart+-- system calls if they are interrupted by signals, so many MySQL API+-- calls can unexpectedly fail when called from a Haskell application.+-- This is most likely to occur if you are linking against GHC's+-- threaded runtime (using the @-threaded@ option).+--+-- This function blocks @SIGALRM@ and @SIGVTALRM@, runs your action,+-- then unblocks those signals. If you have a series of HDBC calls+-- that may block for a period of time, it may be wise to wrap them in+-- this action. Blocking and unblocking signals is cheap, but not+-- free.+--+-- Here is an example of an exception that could be avoided by+-- temporarily blocking GHC's runtime signals:+--+-- > SqlError {+-- > seState = "", +-- > seNativeError = 2003, +-- > seErrorMsg = "Can't connect to MySQL server on 'localhost' (4)"+-- > }+withRTSSignalsBlocked :: IO a -> IO a+withRTSSignalsBlocked act = runInBoundThread . alloca $ \set -> do+ sigemptyset set+ sigaddset set (#const SIGALRM)+ sigaddset set (#const SIGVTALRM)+ pthread_sigmask (#const SIG_BLOCK) set nullPtr+ act `finally` pthread_sigmask (#const SIG_UNBLOCK) set nullPtr++data SigSet++instance Storable SigSet where+ sizeOf _ = #{size sigset_t}+ alignment _ = alignment (undefined :: Ptr CInt)++foreign import ccall unsafe "signal.h sigaddset" sigaddset+ :: Ptr SigSet -> CInt -> IO ()++foreign import ccall unsafe "signal.h sigemptyset" sigemptyset+ :: Ptr SigSet -> IO ()++foreign import ccall unsafe "signal.h pthread_sigmask" pthread_sigmask+ :: CInt -> Ptr SigSet -> Ptr SigSet -> IO ()
HDBC-mysql.cabal view
@@ -1,18 +1,42 @@ Name: HDBC-mysql Category: Database Synopsis: MySQL driver for HDBC-Version: 0.6.3-Description: This package provides a MySQL driver for HDBC.+Version: 0.6.4.0 Stability: Experimental-Maintainer: Chris Waterson <waterson@maubi.net>+Maintainer: Bryan O'Sullivan <bos@mailrank.com> Author: Chris Waterson-Copyright: Copyright (c) 2009-2010 Chris Waterson+Copyright: 2009-2010 Chris Waterson, 2011 MailRank License: LGPL License-file: COPYING-Homepage: http://www.maubi.net/~waterson/hacks/hdbc-mysql.html-Build-Depends: base >= 2 && < 4, bytestring, time, utf8-string, HDBC >= 2.1.0-Exposed-modules: Database.HDBC.MySQL-Other-modules: Database.HDBC.MySQL.Connection-ghc-options: -Wall+Homepage: http://github.com/mailrank/hdbc-mysql+Bug-Reports: http://github.com/mailrank/hdbc-mysql/issues Build-Type: Custom Tested-with: GHC+Cabal-Version: >= 1.6+Description:+ This package provides a MySQL driver for HDBC, implemented via+ bindings to the C @mysqlclient@ library.+extra-source-files:+ ChangeLog+ README.markdown++library+ Exposed-modules: Database.HDBC.MySQL+ Other-modules:+ Database.HDBC.MySQL.Connection+ Database.HDBC.MySQL.RTS+ Build-Depends:+ HDBC >= 2.1.0,+ base >= 2 && < 5,+ bytestring,+ time,+ utf8-string+ ghc-options: -Wall++source-repository head+ type: git+ location: http://github.com/mailrank/hdbc-mysql++source-repository head+ type: mercurial+ location: http://bitbucket.org/bos/hdbc-mysql
+ README.markdown view
@@ -0,0 +1,90 @@+HDBC-mysql+==========++This is a "native" HDBC driver for MySQL that makes use of+libmysqlclient to communicate with a MySQL server. By way of+synopsis:++ import Control.Monad+ import Database.HDBC+ import Database.HDBC.MySQL+ main = do conn <- connectMySQL defaultMySQLConnectInfo {+ mysqlHost = "db1.example.com",+ mysqlUser = "scott",+ mysqlPassword = "tiger"+ }++ rows <- quickQuery' conn "SELECT 1 + 1" []+ forM_ rows $ \row -> putStrLn $ show row++At the moment, please consider this to be "alpha" software. As far as+I can tell, it works. There are some limitations that you should be+aware of.++Caveats+=======++ * This works with MySQL server and client libraries 5.0.75. I+ haven't tried with 4.x nor 5.1. I suspect that using a server+ version less than 4.1 won't work, due to lack of support for+ prepared statements.++ * MySQL DATETIME and TIMESTAMP columns have no time zone information,+ because they just don't. So, I'm just converting them blindly to+ SqlEpochTime values, and assuming the times are UTC. This is all+ fine if you're actually running your server in UTC, but it will+ probably be confusing if you're not. It might be possible to+ interpret the times using the current connection's default+ time zone setting, instead. Is that better? Or worse?++ * Regardless, the types I convert to are now deprecated, and I need+ to implement the new types (SqlLocalDate, etc.)++ * Out of the box, MySQL probably uses MyISAM tables to store its+ data, and MyISAM tables don't support transactions. Yet, I'm+ going to blindly respond "yes" if you ask whether the driver+ itself supports transactions, and assume that you know enough to+ use InnoDB tables in the database if you want to make use of+ HDBC's transactional support. I *suppose* I might be able to+ discover what the default table type is, and say "no" if it's not+ a table type that supports transactions, but... meh.++ * I'm not sure that I can tell the difference between a MySQL TEXT+ and a MySQL BLOB column. If you ask about the metadata of either,+ I'll tell you it's a SqlBinaryT.++ * The statement and table metadata could stand to be improved a bit.+ In particular, it would be nice if "describeTable foo" and+ "describeResults" on "SELECT * FROM foo" returned the same thing.+ (They're sorta close, I guess...)++ * Thread-safety could be an issue. In the driver code, there's+ definitely a race condition between "prepare" and "disconnect",+ for example. I haven't even *looked* at thread-safety issues for+ the MySQL driver. I'm not sure if I should worry about it, or if+ we assume that's going to be dealt with at a higher level.++ * We might crash if someone opens a connection, prepares a+ statement, explicitly disconnects the connection, and then tries+ to play with the statement.++ * It probably makes sense to marshall to the SqlByteString type when+ retrieving BLOB data.++Testing+=======++There's a little test program that runs a query and spews out the+results. To compile it,++ ghc -idist/build -L/opt/local/lib/mysql5/mysql -lmysqlclient --make Test++I'm still trying to get the Makefile right so that it can build the+test sources: it's not there yet. Here's how I've been doing it, for+now:++ cd testsrc+ ghc --make -package HUnit -package HDBC -Wall -i../dist/build -i.. -L/opt/local/lib/mysql5/mysql -lmysqlclient -o runtests runtests.hs++One issue is that I want the location of the MySQL library to come+from the configuration data, rather than be hard-coded.