omoonのブログ

旅の思い出を写真とともに書いてみるか。

DBIで文字コード変換

<追記:2007/7/19>
この記事のスクリプトはうまく動きません。その後の顛末をこちらに書きました↓
http://d.hatena.ne.jp/omoon/20070719/1184819170

人力検索に質問してしまうほど苦戦。詳細は下記を参照のこと。

http://q.hatena.ne.jp/1183789393

要は、スクリプト文字コードがutf8で、mysql文字コードsjis。しかも、mysqlのバージョンが4.0.24なので、

set names xxxx;

も使えない。
また別のサービスが稼働中のため、文字コードを変えることもできない、と。
じゃあ、perlスクリプト側で変換するにはどんな方法がいいのかなあ、ということ。

人力検索でいただいた回答には、DBD::mysqlの方をいじるのでは、という話もあって、下記の本を手に入れたりして、色々調べてみたのだが、今ひとつぴんとこなかった。なんかおおごと過ぎるような気が。。。

Perlを256倍使うための本 DBI編

Perlを256倍使うための本 DBI編


今回やりたいことはいたってシンプルで、

(1) mysqlに投げるsql文をsjisに変換する
(2) 返ってきた結果をutf8に変換する

の2点。

もちろん毎回、

$sth = $dbh->prepare("select * from foo where bar = ?");
$sth->execute(encode('sjis', $baz));

てしたり、

while ($data = $sth->fechrow_arrayref()) {
	@$data = map { encode('utf8', $_) } @$data;
}

みたいに変換すればうまくいくことはわかっているんだけど、結構規模の大きなアプリになりそうなので、いちいちそんなこともしてられない。

ということで、perlもカタコト、サブクラスとかいってもあんまりピンと来ない僕が、DBIのサブクラスを作って、ちょちょいとやってみよう、というのが、今回のお題です。
現在(2007/7/10)のところ、最終的な解決にはいたっていません。進展あり次第追記していくつもりです。

CPAN読んでがんばってみた

http://search.cpan.org/~timb/DBI-1.58/DBI.pm#Subclassing_the_DBI

ここにあるじゃない!!

ステートメントハンドラは、DBI::dbに返させる、excuteとかfetchとかはDBI::stにあるとかいうことだと思うのだけれど、何しろperl自体がカタコトなので、理解するのにすごく時間がかかった。
Data::Dumperなどで、ちまちまダンプしながら、どんな値が返ってきているのか確認しながら、何とか書いたのがこれ。
思いっきり中途半端でして、fetchrow_hashrefで取ってくることしか考えていない。とりあえず、動作はしたよーというレベルですが、晒します。

色々教えてください。
人力検索の回答でコメントいただいても結構です。

package MyDBI;
use strict;
use warnings;
use base qw(DBI);

package MyDBI::db;
use base qw(DBI::db);

sub prepare {
	my ($dbh, @args) = @_;
	my $sth = $dbh->SUPER::prepare(@args);
	return $sth;
}

package MyDBI::st;
use base qw(DBI::st);

sub execute {
	my ($sth, @args) = @_;
	foreach (@args) {
		$_ = Encode::encode('cp932', $_);
	}
	return $sth->SUPER::execute(@args);
}

sub fetchrow_hashref {
	my ($sth, @args) = @_;
	my $ref = $sth->SUPER::fetchrow_hashref(@args);
	foreach (%$ref) {
		$_ = Encode::decode('cp932', $_);
	}
	return $ref;
}

1;


で、これを、

use MyDBI;
my $dbh = MyDBI->connect('DBI:mysql:databese:localhost', 'user', 'pass');
my $sth = $dbh->prepare("select * from foo where bar=?");
$sth->execute("テスト");
foreach ($sth->fetchrow_hashref) {
	print $_->{bar};	# テストと出力される
}

みたいな感じで使う。
いまのところここまで。今日はここからがんばります。