DBIx::Encoding でPerlとDB間の文字コード & utf8 flagを透過的に扱うB!

はじめに

はじめまして、myfinderといいます。

Casual Trackの記事をupしたときに話が出たので、記事を書いてみたいと思います。

今回ご紹介するモジュールは DBIx::Encoding です。

DBIx::Encodingは非常に小粒なモジュールですが、BKだなと自分でも思っています。

cho45さんの紹介されているモジュールと割と趣が近い気がします。

Casual Trackのxaicronさんの記事にもあるように、いわゆるモダンなPerl開発ではソースコードをUTF-8で記述し「use utf8;」し、文字列は「入力時にdecode、出力時にencode」し、プログラム中の文字はすべて内部表現で扱う様にするのが基本です。

この場合、データベースの文字コードも同様にUTF-8に統一したいところですが、歴史あるプロジェクトのデータベースを共有する場合、全部が全部UTF-8とは限りません。

データベース側を変えることができれば良いのですが、そうすることができない場合は涙を飲んで今の文字コードに従うか、アプリケーションもしくはフレームワーク、あるいはO/Rマッパのどこかで文字コードを変換しつつflaggedにしたり、その逆の操作をする層を用意する必要が出てきます。

このモジュールは、DBIを利用して取り出したデータを指定した文字コードでdecodeし、逆にDBへデータを入れるときには指定の文字コードでencodeする機能を提供します。

「DBIを使っていて、データベースの文字コードがEUC-JPやShift-JIS(cp932)で作られているが、プログラムをUTF-8で書きつつ文字列をutf8 flaggedで扱いたい」というなケースで、DBIx::Encoding は役に立つかもしれません。

利用例

モジュールがインストールしてあれば、DSNと一緒に渡すアトリビュートでRootClassと文字コードを指定するだけで利用できます。

use DBIx::Encoding;

my @dsn = (
   'dbi:mysql:host=localhost;database=mysql;mysql_socket=/tmp/mysql.sock;',
   'root',
   '',
   {
       RootClass => 'DBIx::Encoding',
       encoding  => 'cp932',
   },
);

my $dbh = DBI->connect(@dsn) or die;

このように記述するだけで、このDBハンドルメソッドあるいはステートメントハンドルメソッドで実行するクエリに関して

  • データ取得の場合は指定文字コードでdecode
  • 作成、更新の場合は指定文字コードでencode

という操作を行ってくれます。

このモジュールはDBIのサブクラスで、内部的にはDBIの各メソッドをオーバーライドして、それぞれを呼ぶ直前/直後にencode/decodeする形で動作します。

アトリビュートで指定するので、DBICなどのO/Rマッパから利用する事も可能です。

おわりに

「え、それってMySQLならset namesして、mysql_enable_utf8指定すれば終わりじゃないの?」という突っ込みをしようとした方、その通りだと思います。

DBDやデータベースが提供してくれる機能で事が足りる場合は、そちらを利用する事をおすすめします。

このモジュールは、「データベースのバージョンが非常に古かったり、あるいはあまり手を付けられない境遇で、文字コードの変換とflagの扱いをちゃんとしたい」という要求を満たすために作ったものです。

多分にBKなモジュールではありますが、同じような境遇でお悩みの方への一つの解となれば幸いです。