1

更新:私の問題を解決しました

ここでも、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 です。

4

1 に答える 1

0

新しいインメモリ SQLite db に接続して実行します。

$dbic_schema->deploy;

これにより、必要なすべての DDL ステートメントが生成され、データベースに対して実行されます。DBIx::Class にはすでにその機能が含まれているため、DBIx::RunSQL を使用する必要はまったくありません。

SQL::Translator をインストールする必要があることに注意してください。

于 2012-12-11T23:46:49.197 に答える