更新:私の問題を解決しました
ここでも、make_schema_at の動作につまずきました (コード内の @INC の変更に関するコメントを参照してください。これについては、既にバグ レポートを提出しています)。
以下の私のコードの理由(最初のコメントで言及された修正、つまり定義
my $dbic_schema = MySchema->connect( sub { $dbh } );
インメモリ データベースの場合、make_schema_atがハンドル $dbh! との接続を切断するため、機能しませんでした。この問題は、db ハンドルのクローンを make_schema_at に渡すことで解決できます。この make_schema_at の動作もバグだと思いますが、好みの問題かもしれません。私はそれについて議論し、おそらくバグレポートを提出します。他の誰かが同じ問題を抱えている場合に備えて、プログラムの更新バージョンを追加することにしました。
SQLite_test.pl
use strict;
use warnings;
use Test::More;
use DBI;
use DBIx::RunSQL;
use Class::Load qw (load_class);
use DBIx::Class::Schema::Loader qw/ make_schema_at /;
plan tests => 1;
my $table = 'T';
my $dsn = 'dbi:SQLite:dbname=:memory:';
#Create our test table in the target database
my $dbh = DBIx::RunSQL->create(
dsn => $dsn,
sql => 'schema.sql',
force => 1,
verbose => 1,
);
#Dump the DBIx::Class Schema in the current directory
my $attrs = {
debug => 1,
dump_directory => '.',
};
#pass a clone of the database handle to make_schema_at since in the current
#version it will disconnect!
my $tmp_dbh = $dbh->clone();
make_schema_at( 'MySchema', $attrs, [ sub { $tmp_dbh }, {} ] );
#Import the resulting Schema
#In the current version, make_schema_at removes '.' from @INC,
#therefore we add it:
push @INC, '.';
eval {
require MySchema;
MySchema->import();
1;
} or do {
my $error = $@;
croak $error;
};
#Connect to the Schema and use it to count the rows in table T (just as an example)
my $dbic_schema = MySchema->connect( sub { $dbh } );
my $result_source = $dbic_schema->source('T');
my $cls = $result_source->result_class;
my $num_records = $dbic_schema->resultset($cls)->count;
is( $num_records, 5, 'check number of records' );
元の質問:
Perl モジュールのテストにインメモリ SQLite データベースを使用したいと考えていました。アイデアは、DBD::SQLite によって提供されるインメモリ SQLite データベースにいくつかのテーブルを作成し、DBIx::Class::Schema::Loader を使用して DBIx::Class スキーマをディスクにダンプし、結果のスキーマをロードしてテストを行うことです。このスキーマに対して。(例えば、DBIx::Class スキーマをそのように処理する理論的根拠については、http ://www.modernperlbooks.com/mt/2012/04/make-a-dbic-schema-from-ddl.html を参照してください。)ユーザーがモジュールをインストールするときにディスクへの書き込みなどを行いたくないため、メモリ内データベースを使用します。
私の問題は、オンディスク SQLite データベースを使用するのではなく、そのようなインメモリ データベースを使用すると違いが生じることです。
私が何を意味するかを示す完全な例を用意しました。この例では、ある行を別の行に変更すると違いが生じます。ディレクトリに次があるとします。
- 内容が以下に示され、テーブル「T」を作成するファイルschema.sql、その内容は無関係である必要があります
- テーブル「T」を(まだ)持っていないSQLiteデータベースsqlite_db
- プログラムSQLite_test.pl
スキーマ.sql:
CREATE TABLE T (
id INTEGER PRIMARY KEY,
refid INTEGER,
ud TEXT,
dt TEXT,
UNIQUE (ud,dt),
CONSTRAINT fkey FOREIGN KEY (refid) REFERENCES T (id)
);
INSERT INTO T (id, refid, ud, dt) VALUES (1,1,'A','12.04.2011');
INSERT INTO T (id, refid, ud, dt) VALUES (2,1,'B1','12.04.2011');
INSERT INTO T (id, refid, ud, dt) VALUES (3,1,'BB','13.04.2011');
INSERT INTO T (id, refid, ud, dt) VALUES (4,4,'CCC','15.04.2011');
INSERT INTO T (id, refid, ud, dt) VALUES (5,4,'X','11.04.2011');
SQLite_test.pl:
use strict;
use warnings;
use Test::More;
use DBI;
use DBIx::RunSQL;
use Class::Load qw (load_class);
use DBIx::Class::Schema::Loader qw/ make_schema_at /;
plan tests => 1;
my $table = 'T';
# (1) Using a database on disk works:
my $dsn = 'dbi:SQLite:dbname=sqlite_db';
# (2) Using an in-memory SQLite database not:
# my $dsn = 'dbi:SQLite:dbname=:memory:';
#Create our test table in the target database
my $dbh = DBIx::RunSQL->create(
dsn => $dsn,
sql => 'schema.sql',
force => 0,
verbose => 1,
);
#Dump the DBIx::Class Schema in the current directory
my $attrs = {
debug => 1,
dump_directory => '.',
};
make_schema_at( 'MySchema', $attrs, [ sub { $dbh }, {} ] );
#Import the resulting Schema
#Note: in the current version, make_schema_at removes '.' from @INC,
#therefore we add it:
push @INC, '.';
eval {
require MySchema;
MySchema->import();
1;
} or do {
my $error = $@;
croak $error;
};
#Connect to the Schema and use it to count the rows in table T
my $dbic_schema = MySchema->connect( $dsn, q{}, q{} );
my $result_source = $dbic_schema->source('T');
my $cls = $result_source->result_class;
my $num_records = $dbic_schema->resultset($cls)->count;
is( $num_records, 5, 'check number of records' );
ここで、示されているようにプログラム SQLite_test.pl を実行すると動作します (初めて実行するとき、次の実行の前に明らかにテーブル T を削除する必要があります。例を複雑にしたくありませんでした)。しかし、(1) の後の行をコメントアウトし、(2) の後の行をコメントアウトすると、テーブル「T」が見つからないという次のエラーが表示されます。
DBI Exception: DBD::SQLite::db prepare_cached failed: no such table: T [for Statement "SELECT COUNT( * ) FROM T me"] at
C:/strawberry/perl/site/lib/DBIx/Class/Schema.pm line 1101.
DBIx::Class::Schema::throw_exception('MySchema=HASH(0x1e49df4)', 'DBI Exception: DBD::SQLite::db prepare_cached
failed: no such...') called at C:/strawberry/perl/site/lib/DBIx/Class/Storage.pm line 112
DBIx::Class::Storage::throw_exception('DBIx::Class::Storage::DBI::SQLite=HASH(0x342c64c)', 'DBI Exception: DBD::
SQLite::db prepare_cached failed: no such...') called at C:/strawberry/perl/site/lib/DBIx/Class/Storage/DBI.pm line 1427
DBIx::Class::Storage::DBI::__ANON__('DBD::SQLite::db prepare_cached failed: no such table: T [for ...', 'DBI::db
=HASH(0x3311c7c)', undef) called at C:/strawberry/perl/site/lib/DBIx/Class/Storage/DBI.pm line 2418
...
私が見落としていたことに心当たりはありますか?
更新:環境は Windows 7 / Strawberry Perl 5.16.2.1 です。