Обработка схемы 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.

Другие вопросы по тегам