Обработка схемы DBIx::Class отличается в SQLite в памяти и на диске
Обновление: решил мою проблему
Я снова опрокинул поведение make_schema_at (см. Комментарий в коде об изменении @INC, я уже подал отчет об ошибке для этого).
Причина мой код ниже (с исправлением, упомянутым в моем первом комментарии, т.е. определение
my $dbic_schema = MySchema->connect( sub { $dbh } );
не работал в случае базы данных в памяти, что make_schema_at разрывает соединение с дескриптором $ dbh! Эту проблему можно решить, передав клон make-dsc в 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. Идея состоит в том, чтобы создать несколько таблиц в базе данных SQLite в памяти, предоставленной DBD::SQLite, выгрузить схему DBIx::Class на диск с помощью DBIx::Class::Schema::Loader, загрузить полученную схему и выполнить мое тестирование. против этой схемы. (См., Например, 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
schema.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.
1 ответ
Подключитесь к новой базе данных SQLite в памяти, как вы делаете, и затем запустите
$dbic_schema->deploy;
Это создаст и запустит все необходимые операторы DDL для базы данных. Нет необходимости использовать DBIx::RunSQL вообще, потому что DBIx::Class уже включает эту функциональность.
Обратите внимание, что вам нужно установить SQL::Translator.