2011年12月11日日曜日

Haskell+HDBC+MySQL で Hello World

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 のソースを読んで小一時間頭をかきむしって苦しむ事になったりする。

0 件のコメント:

コメントを投稿