DBIx::Class::Loader で View を扱う

Catalyst::Model::DBIC で View が扱えない話。

素の DBIx::Class ならば特に問題はない。

 package PgTables;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/Core DB/);
__PACKAGE__->connection('dbi:Pg:dbname=template1', 'fujiwara', '');
__PACKAGE__->table('pg_tables');
__PACKAGE__->add_columns( qw( schemaname tablename tableowner ));
__PACKAGE__->set_primary_key('tablename');

 

 package main;
my $it = PgTables->search();
while(my $t = $it->next){
print $t->tablename, '|', $t->tableowner, "\n";
}

Catalyst::Model::DBIC では、view は扱えない。というのは、更にその親の DBIx::Class::Loader が view の定義を読み込んでくれないから。

具体的には、 DBIx::Class::Loader::Pg の _tables メソッド内で

    my @tables = $is_dbd_pg_gte_131 ? 
$dbh->tables( undef, $self->{_schema}, "", "table", { noprefix => 1, pg_noprefix => 1 } )
: $dbh->tables;

としている部分。ここで DBD::Pg::tables メソッドの第 4引数に 'table' を渡しているため。 '' を渡せば view も返ってくる。

@INC の先に上記の部分を改造した DBIx/Class/Loader/Pg.pm を置いてやれば、 view も読み込んで扱えるようになることは確認した。 ……とはいえ、ローカルな改造を施したモジュールを使うのも気持ち悪いし (バージョンアップなどへの追従も問題だし)、どうしたものか。

_tables メソッドを ovreride した自前クラスを作り、そっちを使うのが正統なんだろうが、 DBIx::Class::Loader は dsn から自動的に Loader::* (実装クラス) をロードするので 自前のクラスを使わせることができないという罠。

……もの凄く BK (Bad Knowhow) なやりかたは思いついた。マネしないでください。

あらかじめ、view と同じ構造の table を作っておく。

 CREATE TABLE foo AS SELECT * FROM foo_v LIMIT 0;

で、DBIx::Class::Loader が foo の定義を読んでクラスを作ってくれた後で、

 package Bar::Model::DBIC::Foo;
__PACKAGE__->table('foo_v');

table メソッドを使って、見に行くテーブル名を view にすげ替える。 ちゃんと使える.