Haskell で MySQL を使ってみるウォーミングアップ。
こんな環境
- Fedora16
- GHC 7.0.4
- MySQL 5.5.18
====
まずテーブルを準備する。適当に mysql で MySQL に接続して以下のようにする。
mysql> create database hdbc_test;
mysql> connect hdbc_test;
mysql> create table greeting (id int not null auto_increment, text varchar(50), primary key(id));
mysql> insert into greeting(text) values ('Hello, world!');
mysql> select * from greeting;
+-----+---------------+
| id | text |
+-----+---------------+
| 1 | Hello, world! |
+-----+---------------+
1 row in set (0.00 sec)
テーブルの準備ができたら、次は使用するモジュールだけど、
この サイトによると、Haskell から DB を使うには、HDBC、HSQL、HaskellDBと言った選択肢があるらしい。とりあえず HDBC でいってみる。
$cabal install HDBC
$cabal install HDBC-mysql
※ cabal 自体は、Fedora のアプリケーションの追加/削除でインストールした。
※ ghc-pkg という低レベルのコマンドもある。
※ MySQL以外に HDBC でサポートされてるやつを調べるにはここ
できたら、MySQL に接続してみる。
まず ghci を立ち上げて、モジュールを取り込んでからDB接続を取得する。
ghci> :m +Database.HDBC Database.HDBC.MySQL
ghci> conn <- connectMySQL MySQLConnectInfo{mysqlHost="localhost", mysqlDatabase="hdbc_test", mysqlUser="root",mysqlPassword="XXXX", mysqlPort=3306, mysqlUnixSocket="/var/lib/mysql/mysql.sock"}
ghci>
mysqlUnixSocket に設定するファイル名は、/etc/my.conf に書いてあるのでそれを指定すればいい。成功したら、上のようにエラーメッセージなしで次のプロンプトに移る。
ちなみに、省略時値が既に設定されている defaultMySQLConnectInfo を使えば、この場合だと mysqlHost, mysqlUser, mysqlPort を省略して以下のようにできる。
ghci> conn <- connectMySQL defaultMySQLConnectInfo{mysqlDatabase="hdbc_test", mysqlPassword="XXXXX", mysqlUnixSocket="/var/lib/mysql/mysql.sock"}
※ソースを読みたかったら、ここで見られる
接続が得られたら、いよいよ HelloWorld。
ghci> quickQuery' conn "SELECT * from greeting" []
[[SqlInt32 1,SqlByteString "Hello, world!"]]
うん。できたっぽい。
ちなみに エラー無しで取得できたと思った Connection を、いざ使おうとしたら "No instance for (IConnection Connection)"なんて言われる事がある。自分もそうなったけど HDBC 関連のパッケージを更新したら解消した(参考URL)
====
ついでに、もうちょい他の事もやってみる。
■ prepared statement
ghci> stmt <- prepare conn "INSERT INTO greeting(text) VALUES (?)"
ghci> executeMany stmt [[toSql "Good-bye, world..."], [toSql "Hello, another world2!"]]
ghci> quickQuery' conn "select * from greeting"[]
[[SqlInt32 1,SqlByteString "Hello, world!"],[SqlInt32 2,SqlByteString "Good-bye, world..."],[SqlInt32 3,SqlByteString "Hello, another world!"]]
ghci> commit conn
JDBC やってるのと同じだね。
■ メタ情報
ghci> describeTable conn "greeting"
[("id",SqlColDesc {colType = SqlIntegerT, colSize = Nothing, colOctetLength = Nothing, colDecDigits = Nothing, colNullable = Just False}),("text",SqlColDesc {colType = SqlVarCharT, colSize = Nothing, colOctetLength = Nothing, colDecDigits = Nothing, colNullable = Just True})]
うーん…colType は良いけど、colSize がNothing になってるのはどういう訳だろう。ここは varchar (50) を反映していてほしかった。
標準SQL の INFORMATION_SCHEMA で、普通にメタ情報を得ることも、もちろんできる。
■ 日本語
ghci> run conn "UPDATE greeting SET text='こんにちは' WHERE id=1" []
ghci> quickQuery' conn "select * from greeting where id=1"[]
[[SqlInt32 1,SqlByteString "S\147kao"]]
ははは、文字化けした。mysql で見ても化けてる。面倒そうだから後で考えよっと。
--2012/08/12: 同じSQLを prepared statement でやったら問題なく「こんにちは」となる
--2012/08/12:
ghci> run conn "UPDATE greeting SET text=? WHERE id=1" [toSql "こんにちは"] で上手く行く
気になるところが幾つかあったけど、最初の試行としてはこんなものだろう。
====
最後に豆知識メモ。
ghci のプロンプトを変えるには、":set prompt "ghci> "と入力すればいい。これを永続化するには、~/.ghc/ghci.conf に同じ事を書く。ただし、どういうわけか、ファイルのパーミッションで、グループに w が付いていると無視される。無視されないようにするには、"chmod g-w .ghc .ghc/ghci.conf
"として、書き込み権限を取り除いておけばいい。
あと、いろいろググってると、HaskellDB と HSQL と HDBC とで、似て非なる事柄が一緒くたに引っかかってくるので、酒を飲みながら作業してたりすると、 HDBC.MySQL のソースのつもりで HaskellDB.HDBC.MySQL のソースを読んで小一時間頭をかきむしって苦しむ事になったりする。