« 9月6日の行動 | メイン | NONDRINKING »

2006年09月06日

[技術] Catalyst + Class::DBI で動作不良

開発環境で正しく動作していたCatalystフレームワークのPerlソースコードを同一サーバ内の別ディレクトリにコピーして動かすと、ORマッパーとして利用しているClass::DBIが正しくないSQLを発行してまう不具合が発生している。

解決策はこれから探るがやっと、原因だけは掴めたので、現時点でのまとめを忘れないうちに書いておく。

■環境
OS FedoraCore3
Perl 5.8.8
Catalyst 5.66
Class::DBI 3.0.14

■現象
Class::DBIのhas_manyで1対Nの外部参照を設定したテーブルがレコード取得するためのコードが発行されると例外が発生し、Catalystが以下のエラーメッセージを表示する

Couldn't render template "undef error - report is not a column of IZ::Model::CDBI::Contact 
at /usr/lib/perl5/site_perl/5.8.8/Class/DBI/Sweet.pm line 396"

データベース上のcontactテーブルから、同一reportidを持つレコードを取得するSQLを発行するところで、カラム名をreportとして、発行したためにエラーが発生している。(本来は reportidとなるべき)

■不具合箇所の予想
has_manyなどを記述したモデルクラスのReport.pm や Contact.pmのロード時にhas_many, has_aに関する設定が正しくなされていないのではないか?

■デバック方法
Perlのデバッカで、Catalystのテストサーバを起動して、モデルクラスのロード時の動作を正しく動く環境と比較した

% perl -d script/app_server.pl

■原因1
モデルクラスのロード順が正しく動く環境と異なる。例えば、正しく動く環境は、Contact, Reportの順にロードするが、エラーになる方は、Report、Contactの順にロードする。Reportに設定されたContactへのhas_many設定は、Contactクラスの情報を必要とするため不具合の元となっている。

■原因2
そうは言っても、個別のクラスファイルを読み込んでいるので、ロード順が常に依存関係通りに行く保証はない。そのため、Class::DBIでも、Class::DBI::Relationship::HasMany::remap_argument()で、Class::DBI::_require_class()を呼んで、必要なクラスがまだロードされていなければ、ロードする処理をしているのだが、正常動作しない環境ではなぜか、この関数が既にContactクラスがロード済みと判断してしまう。

現時点では解ったのはここまで。これから、Class::DBI::_require_class()などの動作をさらに詳しく検証して行く。

■回避策
翌日になって、解決策ではないが、とりあえず回避方法はわかった。それはこちら

投稿者 nekobara : 2006年09月06日 16:28


コメント