)
EOM
-### load any test classes that are defined further down in the file
+### load any test classes that are defined further down in the file via BEGIN blocks
our @test_classes; #< array that will be pushed into by test classes defined in this file
DBICTest::Schema->load_classes( map {s/.+:://;$_} @test_classes ) if @test_classes;
-### pre-connect tests
+### pre-connect tests (keep each test separate as to make sure rebless() runs)
{
my $s = DBICTest::Schema->connect($dsn, $user, $pass);
ok (!$s->storage->_dbh, 'definitely not connected');
# Check that datetime_parser returns correctly before we explicitly connect.
- SKIP: {
+ SKIP: {
eval { require DateTime::Format::Pg };
skip "DateTime::Format::Pg required", 2 if $@;
is( $parser, 'DateTime::Format::Pg', 'datetime_parser is as expected');
}
-
+ ok (!$s->storage->_dbh, 'still not connected');
+}
+{
+ my $s = DBICTest::Schema->connect($dsn, $user, $pass);
# make sure sqlt_type overrides work (::Storage::DBI::Pg does this)
+ ok (!$s->storage->_dbh, 'definitely not connected');
is ($s->storage->sqlt_type, 'PostgreSQL', 'sqlt_type correct pre-connection');
+ ok (!$s->storage->_dbh, 'still not connected');
}
### connect, create postgres-specific test schema
my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
-drop_test_schema($schema, 'no warn');
+drop_test_schema($schema);
create_test_schema($schema);
### begin main tests
run_apk_tests($schema); #< older set of auto-pk tests
run_extended_apk_tests($schema); #< new extended set of auto-pk tests
+
+
+
+
### type_info tests
my $test_type_info = {
use strict;
use warnings;
- use base 'DBIx::Class';
+ use base 'DBIx::Class::Core';
- __PACKAGE__->load_components(qw/Core/);
__PACKAGE__->table('dbic_t_schema.array_test');
__PACKAGE__->add_columns(qw/id arrayfield/);
__PACKAGE__->column_info_from_storage(1);
use strict;
use warnings;
- use base 'DBIx::Class';
+ use base 'DBIx::Class::Core';
- __PACKAGE__->load_components(qw/Core/);
__PACKAGE__->table('dbic_t_schema.casecheck');
- __PACKAGE__->add_columns(qw/id name NAME uc_name storecolumn/);
+ __PACKAGE__->add_columns(qw/id name NAME uc_name/);
__PACKAGE__->column_info_from_storage(1);
__PACKAGE__->set_primary_key('id');
-
- sub store_column {
- my ($self, $name, $value) = @_;
- $value = '#'.$value if($name eq "storecolumn");
- $self->maybe::next::method($name, $value);
- }
}
-# store_column is called once for create() for non sequence columns
-ok(my $storecolumn = $schema->resultset('Casecheck')->create({'storecolumn' => 'a'}));
-is($storecolumn->storecolumn, '#a'); # was '##a'
-
my $name_info = $schema->source('Casecheck')->column_info( 'name' );
is( $name_info->{size}, 1, "Case sensitive matching info for 'name'" );
## Test SELECT ... FOR UPDATE
-my $HaveSysSigAction = eval "require Sys::SigAction" && !$@;
-if( $HaveSysSigAction ) {
- Sys::SigAction->import( 'set_sig_handler' );
-}
SKIP: {
- skip "Sys::SigAction is not available", 3 unless $HaveSysSigAction;
- # create a new schema
- my $schema2 = DBICTest::Schema->connect($dsn, $user, $pass);
- $schema2->source("Artist")->name("dbic_t_schema.artist");
-
- $schema->txn_do( sub {
- my $artist = $schema->resultset('Artist')->search(
- {
- artistid => 1
- },
- {
- for => 'update'
- }
- )->first;
- is($artist->artistid, 1, "select for update returns artistid = 1");
+ if(eval "require Sys::SigAction" && !$@) {
+ Sys::SigAction->import( 'set_sig_handler' );
+ }
+ else {
+ skip "Sys::SigAction is not available", 6;
+ }
- my $artist_from_schema2;
- my $error_ok = 0;
- eval {
- my $h = set_sig_handler( 'ALRM', sub { die "DBICTestTimeout" } );
- alarm(2);
- $artist_from_schema2 = $schema2->resultset('Artist')->find(1);
- $artist_from_schema2->name('fooey');
- $artist_from_schema2->update;
- alarm(0);
- };
- if (my $e = $@) {
- $error_ok = $e =~ /DBICTestTimeout/;
- }
+ my ($timed_out, $artist2);
+ for my $t (
+ {
# Make sure that an error was raised, and that the update failed
- ok($error_ok, "update from second schema times out");
- ok($artist_from_schema2->is_column_changed('name'), "'name' column is still dirty from second schema");
- });
-}
-
-SKIP: {
- skip "Sys::SigAction is not available", 3 unless $HaveSysSigAction;
- # create a new schema
- my $schema2 = DBICTest::Schema->connect($dsn, $user, $pass);
- $schema2->source("Artist")->name("dbic_t_schema.artist");
-
- $schema->txn_do( sub {
+ update_lock => 1,
+ test_sub => sub {
+ ok($timed_out, "update from second schema times out");
+ ok($artist2->is_column_changed('name'), "'name' column is still dirty from second schema");
+ },
+ },
+ {
+ # Make sure that an error was NOT raised, and that the update succeeded
+ update_lock => 0,
+ test_sub => sub {
+ ok(! $timed_out, "update from second schema DOES NOT timeout");
+ ok(! $artist2->is_column_changed('name'), "'name' column is NOT dirty from second schema");
+ },
+ },
+ ) {
+ # create a new schema
+ my $schema2 = DBICTest::Schema->connect($dsn, $user, $pass);
+ $schema2->source("Artist")->name("dbic_t_schema.artist");
+
+ $schema->txn_do( sub {
my $artist = $schema->resultset('Artist')->search(
{
artistid => 1
},
+ $t->{update_lock} ? { for => 'update' } : {}
)->first;
- is($artist->artistid, 1, "select for update returns artistid = 1");
+ is($artist->artistid, 1, "select returns artistid = 1");
- my $artist_from_schema2;
- my $error_ok = 0;
+ $timed_out = 0;
eval {
my $h = set_sig_handler( 'ALRM', sub { die "DBICTestTimeout" } );
alarm(2);
- $artist_from_schema2 = $schema2->resultset('Artist')->find(1);
- $artist_from_schema2->name('fooey');
- $artist_from_schema2->update;
+ $artist2 = $schema2->resultset('Artist')->find(1);
+ $artist2->name('fooey');
+ $artist2->update;
alarm(0);
};
- if (my $e = $@) {
- $error_ok = $e =~ /DBICTestTimeout/;
- }
+ $timed_out = $@ =~ /DBICTestTimeout/;
+ });
- # Make sure that an error was NOT raised, and that the update succeeded
- ok(! $error_ok, "update from second schema DOES NOT timeout");
- ok(! $artist_from_schema2->is_column_changed('name'), "'name' column is NOT dirty from second schema");
- });
+ $t->{test_sub}->();
+ }
}
exit;
END {
+ return unless $schema;
drop_test_schema($schema);
eapk_drop_all( $schema)
};
, "name" VARCHAR(1)
, "NAME" VARCHAR(2)
, "UC_NAME" VARCHAR(3)
- , "storecolumn" VARCHAR(10)
)
EOS
$dbh->do(<<EOS);
sub drop_test_schema {
- my ( $schema, $no_warn ) = @_;
+ my ( $schema, $warn_exceptions ) = @_;
$schema->storage->dbh_do(sub {
my (undef,$dbh) = @_;
'DROP SCHEMA dbic_t_schema_3 CASCADE',
) {
eval { $dbh->do ($stat) };
- diag $@ if $@ && !$no_warn;
+ diag $@ if $@ && $warn_exceptions;
}
});
}
######## EXTENDED AUTO-PK TESTS
+my @eapk_id_columns;
BEGIN {
package DBICTest::Schema::ExtAPK;
push @main::test_classes, __PACKAGE__;
use strict;
use warnings;
- use base 'DBIx::Class';
+ use base 'DBIx::Class::Core';
- __PACKAGE__->load_components(qw/Core/);
- __PACKAGE__->table('apk_t');
+ __PACKAGE__->table('apk');
+ @eapk_id_columns = qw( id1 id2 id3 id4 );
__PACKAGE__->add_columns(
map { $_ => { data_type => 'integer', is_auto_increment => 1 } }
- qw( id1 id2 id3 id4 )
+ @eapk_id_columns
);
- __PACKAGE__->set_primary_key('id1');
+ __PACKAGE__->set_primary_key('id2'); #< note the SECOND column is
+ #the primary key
}
-my @apk_schemas;
-BEGIN{ @apk_schemas = map "dbic_apk_$_", 0..5 }
+my @eapk_schemas;
+BEGIN{ @eapk_schemas = map "dbic_apk_$_", 0..5 }
+my %seqs; #< hash of schema.table.col => currval of its (DBIC) primary key sequence
sub run_extended_apk_tests {
my $schema = shift;
- eapk_drop_all($schema,'no warn');
+ #save the search path and reset it at the end
+ my $search_path_save = eapk_get_search_path($schema);
+
+ eapk_drop_all($schema);
- # make the test schemas
+ # make the test schemas and sequences
$schema->storage->dbh_do(sub {
- $_[1]->do("CREATE SCHEMA $_")
- for @apk_schemas;
+ my ( undef, $dbh ) = @_;
+
+ $dbh->do("CREATE SCHEMA $_")
+ for @eapk_schemas;
+
+ $dbh->do("CREATE SEQUENCE $eapk_schemas[5].fooseq");
+ $dbh->do("SELECT setval('$eapk_schemas[5].fooseq',400)");
+ $seqs{"$eapk_schemas[1].apk.id2"} = 400;
+
+ $dbh->do("CREATE SEQUENCE $eapk_schemas[4].fooseq");
+ $dbh->do("SELECT setval('$eapk_schemas[4].fooseq',300)");
+ $seqs{"$eapk_schemas[3].apk.id2"} = 300;
+
+ $dbh->do("CREATE SEQUENCE $eapk_schemas[3].fooseq");
+ $dbh->do("SELECT setval('$eapk_schemas[3].fooseq',200)");
+ $seqs{"$eapk_schemas[4].apk.id2"} = 200;
+
+ $dbh->do("SET search_path = ".join ',', reverse @eapk_schemas );
});
- eapk_create($schema, with_search_path => [0,1]);
+ # clear our search_path cache
+ $schema->storage->{_pg_search_path} = undef;
+
+ eapk_create( $schema,
+ with_search_path => [0,1],
+ );
+ eapk_create( $schema,
+ with_search_path => [1,0,'public'],
+ nextval => "$eapk_schemas[5].fooseq",
+ );
+ eapk_create( $schema,
+ with_search_path => ['public',0,1],
+ qualify_table => 2,
+ );
+ eapk_create( $schema,
+ with_search_path => [3,1,0,'public'],
+ nextval => "$eapk_schemas[4].fooseq",
+ );
+ eapk_create( $schema,
+ with_search_path => [3,1,0,'public'],
+ nextval => "$eapk_schemas[3].fooseq",
+ qualify_table => 4,
+ );
+
+ eapk_poke( $schema );
+ eapk_poke( $schema, 0 );
+ eapk_poke( $schema, 2 );
+ eapk_poke( $schema, 4 );
+ eapk_poke( $schema, 1 );
+ eapk_poke( $schema, 0 );
+ eapk_poke( $schema, 1 );
+ eapk_poke( $schema );
+ eapk_poke( $schema, 4 );
+ eapk_poke( $schema, 3 );
+ eapk_poke( $schema, 1 );
+ eapk_poke( $schema, 2 );
+ eapk_poke( $schema, 0 );
+
+ # set our search path back
+ eapk_set_search_path( $schema, @$search_path_save );
+}
+
+# do a DBIC create on the apk table in the given schema number (which is an
+# index of @eapk_schemas)
+
+sub eapk_poke {
+ my ($s, $schema_num) = @_;
- #unqualified table, unqualified
+ my $schema_name = defined $schema_num
+ ? $eapk_schemas[$schema_num]
+ : '';
+
+ my $schema_name_actual = $schema_name || eapk_find_visible_schema($s);
+
+ $s->source('ExtAPK')->name($schema_name ? $schema_name.'.apk' : 'apk');
+ #< clear sequence name cache
+ $s->source('ExtAPK')->column_info($_)->{sequence} = undef
+ for @eapk_id_columns;
+
+ no warnings 'uninitialized';
lives_ok {
- $schema->resultset('ExtAPK')->create({});
- } 'create in first schema does not die';
+ my $new;
+ for my $inc (1,2,3) {
+ $new = $schema->resultset('ExtAPK')->create({ id1 => 1});
+ my $proper_seqval = ++$seqs{"$schema_name_actual.apk.id2"};
+ is( $new->id2, $proper_seqval, "$schema_name_actual.apk.id2 correct inc $inc" )
+ or eapk_seq_diag($s,$schema_name);
+ $new->discard_changes;
+ is( $new->id1, 1 );
+ for my $id ('id3','id4') {
+ my $proper_seqval = ++$seqs{"$schema_name_actual.apk.$id"};
+ is( $new->$id, $proper_seqval, "$schema_name_actual.apk.$id correct inc $inc" )
+ or eapk_seq_diag($s,$schema_name);
+ }
+ }
+ } "create in schema '$schema_name' lives"
+ or eapk_seq_diag($s,$schema_name);
+}
+# print diagnostic info on which sequences were found in the ExtAPK
+# class
+sub eapk_seq_diag {
+ my $s = shift;
+ my $schema = shift || eapk_find_visible_schema($s);
+
+ diag "$schema.apk sequences: ",
+ join(', ',
+ map "$_:".($s->source('ExtAPK')->column_info($_)->{sequence} || '<none>'),
+ @eapk_id_columns
+ );
}
+# get the postgres search path as an arrayref
+sub eapk_get_search_path {
+ my ( $s ) = @_;
+ # cache the search path as ['schema','schema',...] in the storage
+ # obj
+
+ return $s->storage->dbh_do(sub {
+ my (undef, $dbh) = @_;
+ my @search_path;
+ my ($sp_string) = $dbh->selectrow_array('SHOW search_path');
+ while ( $sp_string =~ s/("[^"]+"|[^,]+),?// ) {
+ unless( defined $1 and length $1 ) {
+ die "search path sanity check failed: '$1'";
+ }
+ push @search_path, $1;
+ }
+ \@search_path
+ });
+}
+sub eapk_set_search_path {
+ my ($s,@sp) = @_;
+ my $sp = join ',',@sp;
+ $s->storage->dbh_do( sub { $_[1]->do("SET search_path = $sp") } );
+}
+
+# create the apk table in the given schema, can set whether the table name is qualified, what the nextval is for the second ID
sub eapk_create {
my ($schema, %a) = @_;
if ( $a{with_search_path} ) {
($searchpath_save) = $dbh->selectrow_array('SHOW search_path');
- my $search_path = join ',',@apk_schemas[@{$a{with_search_path}}];
+ my $search_path = join ',',map {/\D/ ? $_ : $eapk_schemas[$_]} @{$a{with_search_path}};
$dbh->do("SET search_path = $search_path");
}
- my $schema = $a{qualify} ? "$a{qualify}." : '';
+ my $table_name = $a{qualify_table}
+ ? ($eapk_schemas[$a{qualify_table}] || die). ".apk"
+ : 'apk';
local $_[1]->{Warn} = 0;
+
+ my $id_def = $a{nextval}
+ ? "integer not null default nextval('$a{nextval}'::regclass)"
+ : 'serial';
$dbh->do(<<EOS);
-CREATE TABLE apk_t (
- id1 serial primary key
- , id2 serial
- , id3 serial
+CREATE TABLE $table_name (
+ id1 serial
+ , id2 $id_def
+ , id3 serial primary key
, id4 serial
)
EOS
}
sub eapk_drop_all {
- my ( $schema, $no_warn ) = @_;
+ my ( $schema, $warn_exceptions ) = @_;
$schema->storage->dbh_do(sub {
my (undef,$dbh) = @_;
local $dbh->{Warn} = 0;
# drop the test schemas
- for (@apk_schemas ) {
+ for (@eapk_schemas ) {
eval{ $dbh->do("DROP SCHEMA $_ CASCADE") };
- diag $@ if $@ && !$no_warn;
+ diag $@ if $@ && $warn_exceptions;
}
});
}
+
+sub eapk_find_visible_schema {
+ my ($s) = @_;
+
+ my ($schema) =
+ $s->storage->dbh_do(sub {
+ $_[1]->selectrow_array(<<EOS);
+SELECT n.nspname
+FROM pg_catalog.pg_namespace n
+JOIN pg_catalog.pg_class c ON c.relnamespace = n.oid
+WHERE c.relname = 'apk'
+ AND pg_catalog.pg_table_is_visible(c.oid)
+EOS
+ });
+ return $schema;
+}