Don't use DBIx::Class::UTF8ColumnsB!

自分のつくったもジュールを紹介するハッカートラックということで、僕は DBIx::Class に同封されている DBIx::Class::UTF8Columns について書きます。

まず最初に、このモジュールをつかわないでください。 DBIx::Class::UTF8Columns は DBIx::Class のコアモジュールになってから、utf8 を扱う場合はこのモジュールを使うといいよという記述をいろいろなところで目にします。しかしこのモジュールがしていることを理解せずに使用すると予期せぬ不具合に悩まされるかもしれません。今日はこのモジュールを使わない方が良い理由と、その代替案を示します。

このモジュールは DBIC から取り出したデータの utf8 フラグを立て、逆にデータベースにデータを入れるときにはフラグを落とすという、モダンな Perl スクリプトの流儀を自動で行ってくれるモジュールです。

しかしこれが行われるのは get_columnset_column のときのみです。

DBIx::Class::Row のほとんどのファンクションはこのどちらかのメソッドを最終的には読んでいる物ばかりなので問題にはなりません。しかし ResultSet ではどうでしょう。

$rs->search({ name => '村瀬' })

と言った場合、これは UTF8Columns の扱う範疇を超えてしまいます。 UTF8Columns はこの場合なにもしません。したがって DBD の実装によっては何らかの不具合がおきたり、うまく検索できなかったりするでしょう。

UTF8Columns は Result クラスのためのものであり、ResultSet クラスでは動作しません。

どうするのがいいのでしょう。代替案としては二つあります。

  1. ResultSet メソッドには (encode|decode)_utf8 して使う
  2. DBD が提供している utf8 関係の機能を使用する。

前者は従来どおり、自前でutf8周りの処理をすると言う物です。

後者は DBD::SQLite、 DBD::mysql、DBD::Pg、などが提供する utf8 用の機能を使用します。 これらを使用すると DBD 側でデータ取得時には utf8 フラグをたててくれ、入れるときにはフラグを落としてくれます。こちらを使用すると Result でも ResultSet でも問題なく使用できるためいちばんスムーズな回答となります。

具体的なオプションとしては

DBD::SQLite:

sqlite_unicode => 1
unicode => 1 # DBD::SQLite 1.26 以前

DBD::mysql:

mysql_enable_utf8 => 1

DBD::Pg:

pg_enable_utf8  => 1

などがあります。

これを DBIx::Class などから使用する場合は

my $schema = MySchema->connect(
    'dbi:mysql:database', 'username', 'password',
    {
        mysql_enable_utf8 => 1,
        on_connect_do     => ['SET NAMES utf8'],
    },
);

みたいな感じでやればOKです。

とはいえ、MySQL の場合 mysql_enable_utf8

This option is experimental and may change in future versions.

なので、その辺を理解して使用ください。

UTF8Columns はそれがやっていることを理解して使わないと予期せぬバグを生むかもしれないということと、二つの代替案を提案してみました。個人的には最後の方法がいちばんおすすめです。参考になれば幸いです。

明日は kazuho さんです!