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' );

原始问题:

我想使用内存中的 SQLite 数据库来测试 Perl 模块。想法是在 DBD::SQLite 提供的内存 SQLite 数据库中创建一些表,使用 DBIx::Class::Schema::Loader 将 DBIx::Class 模式转储到磁盘,加载生成的模式并进行测试反对这个模式。(请参阅例如http://www.modernperlbooks.com/mt/2012/04/make-a-dbic-schema-from-ddl.html以了解处理 DBIx::Class 模式的基本原理。)我想使用内存数据库,因为我不想在用户安装模块时做诸如写入磁盘之类的事情。

我的问题是,使用这种内存数据库与使用磁盘 SQLite 数据库不同。

我准备了一个完整的示例来说明我的意思以及将一条线更改为另一条线会产生不同。假设在一个目录中我们有:

  • 一个文件schema.sql,其内容如下所示并创建一个表“T”,其内容应该是无关紧要的
  • 一个 SQLite 数据库 sqlite_db 没有(还)有一个表“T”
  • 程序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::RunSQL,因为 DBIx::Class 已经包含该功能。

请注意,您需要安装 SQL::Translator。

于 2012-12-11T23:46:49.197 回答