« 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