( NOTE: This test drops and creates tables called 'artist', 'casecheck',
'array_test' and 'sequence_test' as well as following sequences:
'pkid1_seq', 'pkid2_seq' and 'nonpkid_seq''. as well as following
- schemas: 'testschema', 'anothertestschema', 'yetanothertestschema',
- 'unq_nextval_schema', and 'unq_nextval_schema2'
+ schemas: 'dbic_t_schema', 'dbic_t_schema_2', 'dbic_t_schema_3',
+ 'dbic_t_schema_4', and 'dbic_t_schema_5'
)
EOM
### connect, create postgres-specific test schema
my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
-my $dbh = $schema->storage->dbh;
-drop_test_schema($dbh, 'no warn');
-create_test_schema($dbh);
+drop_test_schema($schema, 'no warn');
+create_test_schema($schema);
### begin main tests
-### auto-pk / last_insert_id / sequence discovery
-{
-
- $schema->source("Artist")->name("testschema.artist");
-
- # This is in Core now, but it's here just to test that it doesn't break
- $schema->class('Artist')->load_components('PK::Auto');
- cmp_ok( $schema->resultset('Artist')->count, '==', 0, 'this should start with an empty artist table');
-
- # test that auto-pk also works with the defined search path by
- # un-schema-qualifying the table name
- my $artist_name_save = $schema->source("Artist")->name;
- $schema->source("Artist")->name("artist");
-
- my $unq_new;
- lives_ok {
- $unq_new = $schema->resultset('Artist')->create({ name => 'baz' });
- } 'insert into unqualified, shadowed table succeeds';
-
- is($unq_new && $unq_new->artistid, 1, "and got correct artistid");
-
- my @test_schemas = ( [qw| anothertestschema 1 |],
- [qw| yetanothertestschema 1 |],
- );
- foreach my $t ( @test_schemas ) {
- my ($sch_name, $start_num) = @$t;
- #test with anothertestschema
- $schema->source('Artist')->name("$sch_name.artist");
- $schema->source('Artist')->column_info('artistid')->{sequence} = undef; #< clear sequence name cache
- my $another_new;
- lives_ok {
- $another_new = $schema->resultset('Artist')->create({ name => 'Tollbooth Willy'});
- is( $another_new->artistid,$start_num, "got correct artistid for $sch_name")
- or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
- } "$sch_name liid 1 did not die"
- or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
- lives_ok {
- $another_new = $schema->resultset('Artist')->create({ name => 'Adam Sandler'});
- is( $another_new->artistid,$start_num+1, "got correct artistid for $sch_name")
- or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
- } "$sch_name liid 2 did not die"
- or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
-
- }
-
-
- my @todo_schemas = (
- [qw| unq_nextval_schema 2 |],
- [qw| unq_nextval_schema2 1 |],
- );
-
- foreach my $t ( @todo_schemas ) {
- my ($sch_name, $start_num) = @$t;
-
- #test with anothertestschema
- $schema->source('Artist')->name("$sch_name.artist");
- $schema->source('Artist')->column_info('artistid')->{sequence} = undef; #< clear sequence name cache
- my $another_new;
- lives_ok {
- $another_new = $schema->resultset('Artist')->create({ name => 'Tollbooth Willy'});
- is( $another_new->artistid,$start_num, "got correct artistid for $sch_name")
- or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
- } "$sch_name liid 1 did not die"
- or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
-
- lives_ok {
- $another_new = $schema->resultset('Artist')->create({ name => 'Adam Sandler'});
- is( $another_new->artistid,$start_num+1, "got correct artistid for $sch_name")
- or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
- } "$sch_name liid 2 did not die"
- or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
- }
-
- $schema->source('Artist')->column_info('artistid')->{sequence} = undef; #< clear sequence name cache
- $schema->source("Artist")->name($artist_name_save);
-}
-
-lives_ok {
- my $new = $schema->resultset('Artist')->create({ name => 'foo' });
- is($new->artistid, 4, "Auto-PK worked");
- $new = $schema->resultset('Artist')->create({ name => 'bar' });
- is($new->artistid, 5, "Auto-PK worked");
-} 'old auto-pk tests did not die either';
-
+# run a BIG bunch of tests for last-insert-id / Auto-PK / sequence
+# discovery
+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 $type_info = $schema->storage->columns_info_for('testschema.artist');
+my $type_info = $schema->storage->columns_info_for('dbic_t_schema.artist');
my $artistid_defval = delete $type_info->{artistid}->{default_value};
like($artistid_defval,
qr/^nextval\('([^\.]*\.){0,1}artist_artistid_seq'::(?:text|regclass)\)/,
use base 'DBIx::Class';
__PACKAGE__->load_components(qw/Core/);
- __PACKAGE__->table('testschema.array_test');
+ __PACKAGE__->table('dbic_t_schema.array_test');
__PACKAGE__->add_columns(qw/id arrayfield/);
__PACKAGE__->column_info_from_storage(1);
__PACKAGE__->set_primary_key('id');
use base 'DBIx::Class';
__PACKAGE__->load_components(qw/Core/);
- __PACKAGE__->table('testschema.casecheck');
+ __PACKAGE__->table('dbic_t_schema.casecheck');
__PACKAGE__->add_columns(qw/id name NAME uc_name storecolumn/);
__PACKAGE__->column_info_from_storage(1);
__PACKAGE__->set_primary_key('id');
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("testschema.artist");
+ $schema2->source("Artist")->name("dbic_t_schema.artist");
$schema->txn_do( sub {
my $artist = $schema->resultset('Artist')->search(
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("testschema.artist");
+ $schema2->source("Artist")->name("dbic_t_schema.artist");
$schema->txn_do( sub {
my $artist = $schema->resultset('Artist')->search(
}
-######## other Auto-pk tests
+######## other older Auto-pk tests
-$schema->source("SequenceTest")->name("testschema.sequence_test");
+$schema->source("SequenceTest")->name("dbic_t_schema.sequence_test");
for (1..5) {
my $st = $schema->resultset('SequenceTest')->create({ name => 'foo' });
is($st->pkid1, $_, "Oracle Auto-PK without trigger: First primary key");
my $st = $schema->resultset('SequenceTest')->create({ name => 'foo', pkid1 => 55 });
is($st->pkid1, 55, "Oracle Auto-PK without trigger: First primary key set manually");
-drop_test_schema($dbh);
done_testing;
exit;
-END { drop_test_schema($dbh) }
+
+END {
+ drop_test_schema($schema);
+ eapk_drop_all( $schema)
+};
######### SUBROUTINES
sub create_test_schema {
- my $dbh = shift;
+ my $schema = shift;
+ $schema->storage->dbh_do(sub {
+ my (undef,$dbh) = @_;
- local $SIG{__WARN__} = sub {};
+ local $dbh->{Warn} = 0;
- my $std_artist_table = <<EOS;
+ my $std_artist_table = <<EOS;
(
artistid serial PRIMARY KEY
, name VARCHAR(100)
)
EOS
- $dbh->do("CREATE SCHEMA testschema");
- $dbh->do("CREATE TABLE testschema.artist $std_artist_table");
- $dbh->do(<<EOS);
-CREATE TABLE testschema.sequence_test (
+ $dbh->do("CREATE SCHEMA dbic_t_schema");
+ $dbh->do("CREATE TABLE dbic_t_schema.artist $std_artist_table");
+ $dbh->do(<<EOS);
+CREATE TABLE dbic_t_schema.sequence_test (
pkid1 integer
, pkid2 integer
, nonpkid integer
, CONSTRAINT pk PRIMARY KEY(pkid1, pkid2)
)
EOS
- $dbh->do("CREATE SEQUENCE pkid1_seq START 1 MAXVALUE 999999 MINVALUE 0");
- $dbh->do("CREATE SEQUENCE pkid2_seq START 10 MAXVALUE 999999 MINVALUE 0");
- $dbh->do("CREATE SEQUENCE nonpkid_seq START 20 MAXVALUE 999999 MINVALUE 0");
- $dbh->do(<<EOS);
-CREATE TABLE testschema.casecheck (
+ $dbh->do("CREATE SEQUENCE pkid1_seq START 1 MAXVALUE 999999 MINVALUE 0");
+ $dbh->do("CREATE SEQUENCE pkid2_seq START 10 MAXVALUE 999999 MINVALUE 0");
+ $dbh->do("CREATE SEQUENCE nonpkid_seq START 20 MAXVALUE 999999 MINVALUE 0");
+ $dbh->do(<<EOS);
+CREATE TABLE dbic_t_schema.casecheck (
id serial PRIMARY KEY
, "name" VARCHAR(1)
, "NAME" VARCHAR(2)
, "storecolumn" VARCHAR(10)
)
EOS
- $dbh->do(<<EOS);
-CREATE TABLE testschema.array_test (
+ $dbh->do(<<EOS);
+CREATE TABLE dbic_t_schema.array_test (
id serial PRIMARY KEY
, arrayfield INTEGER[]
)
EOS
- $dbh->do("CREATE SCHEMA anothertestschema");
- $dbh->do("CREATE TABLE anothertestschema.artist $std_artist_table");
- $dbh->do("CREATE SCHEMA yetanothertestschema");
- $dbh->do("CREATE TABLE yetanothertestschema.artist $std_artist_table");
- $dbh->do('set search_path=testschema,public');
- $dbh->do("CREATE SCHEMA unq_nextval_schema");
- $dbh->do("CREATE SCHEMA unq_nextval_schema2");
- $dbh->do(<<EOS);
- CREATE TABLE unq_nextval_schema.artist
+ $dbh->do("CREATE SCHEMA dbic_t_schema_2");
+ $dbh->do("CREATE TABLE dbic_t_schema_2.artist $std_artist_table");
+ $dbh->do("CREATE SCHEMA dbic_t_schema_3");
+ $dbh->do("CREATE TABLE dbic_t_schema_3.artist $std_artist_table");
+ $dbh->do('set search_path=dbic_t_schema,public');
+ $dbh->do("CREATE SCHEMA dbic_t_schema_4");
+ $dbh->do("CREATE SCHEMA dbic_t_schema_5");
+ $dbh->do(<<EOS);
+ CREATE TABLE dbic_t_schema_4.artist
(
artistid integer not null default nextval('artist_artistid_seq'::regclass) PRIMARY KEY
, name VARCHAR(100)
, arrayfield INTEGER[]
);
EOS
- $dbh->do('set search_path=public,testschema,yetanothertestschema');
- $dbh->do('create sequence public.artist_artistid_seq'); #< in the public schema
- $dbh->do(<<EOS);
- CREATE TABLE unq_nextval_schema2.artist
+ $dbh->do('set search_path=public,dbic_t_schema,dbic_t_schema_3');
+ $dbh->do('create sequence public.artist_artistid_seq'); #< in the public schema
+ $dbh->do(<<EOS);
+ CREATE TABLE dbic_t_schema_5.artist
(
artistid integer not null default nextval('public.artist_artistid_seq'::regclass) PRIMARY KEY
, name VARCHAR(100)
, arrayfield INTEGER[]
);
EOS
- $dbh->do('set search_path=testschema,public');
+ $dbh->do('set search_path=dbic_t_schema,public');
+ });
}
sub drop_test_schema {
- my ( $dbh, $no_warn ) = @_;
-
- return unless $dbh->ping;
-
- for my $stat (
- 'DROP TABLE unq_nextval_schema2.artist',
- 'DROP SCHEMA unq_nextval_schema2',
- 'DROP SEQUENCE public.artist_artistid_seq',
- 'DROP TABLE unq_nextval_schema.artist',
- 'DROP SCHEMA unq_nextval_schema',
- 'DROP TABLE testschema.artist',
- 'DROP TABLE testschema.casecheck',
- 'DROP TABLE testschema.sequence_test',
- 'DROP TABLE testschema.array_test',
- 'DROP SEQUENCE pkid1_seq',
- 'DROP SEQUENCE pkid2_seq',
- 'DROP SEQUENCE nonpkid_seq',
- 'DROP SCHEMA testschema',
- 'DROP TABLE anothertestschema.artist',
- 'DROP SCHEMA anothertestschema',
- 'DROP TABLE yetanothertestschema.artist',
- 'DROP SCHEMA yetanothertestschema',
- ) {
- eval { $dbh->do ($stat) };
- diag $@ if $@ && !$no_warn;
- }
+ my ( $schema, $no_warn ) = @_;
+
+ $schema->storage->dbh_do(sub {
+ my (undef,$dbh) = @_;
+
+ local $dbh->{Warn} = 0;
+
+ for my $stat (
+ 'DROP SCHEMA dbic_t_schema_5 CASCADE',
+ 'DROP SEQUENCE public.artist_artistid_seq',
+ 'DROP SCHEMA dbic_t_schema_4 CASCADE',
+ 'DROP SCHEMA dbic_t_schema CASCADE',
+ 'DROP SEQUENCE pkid1_seq',
+ 'DROP SEQUENCE pkid2_seq',
+ 'DROP SEQUENCE nonpkid_seq',
+ 'DROP SCHEMA dbic_t_schema_2 CASCADE',
+ 'DROP SCHEMA dbic_t_schema_3 CASCADE',
+ ) {
+ eval { $dbh->do ($stat) };
+ diag $@ if $@ && !$no_warn;
+ }
+ });
+}
+
+
+### auto-pk / last_insert_id / sequence discovery
+sub run_apk_tests {
+ my $schema = shift;
+
+ # This is in Core now, but it's here just to test that it doesn't break
+ $schema->class('Artist')->load_components('PK::Auto');
+ cmp_ok( $schema->resultset('Artist')->count, '==', 0, 'this should start with an empty artist table');
+
+ # test that auto-pk also works with the defined search path by
+ # un-schema-qualifying the table name
+ apk_t_set($schema,'artist');
+
+ my $unq_new;
+ lives_ok {
+ $unq_new = $schema->resultset('Artist')->create({ name => 'baz' });
+ } 'insert into unqualified, shadowed table succeeds';
+
+ is($unq_new && $unq_new->artistid, 1, "and got correct artistid");
+
+ my @test_schemas = ( [qw| dbic_t_schema_2 1 |],
+ [qw| dbic_t_schema_3 1 |],
+ [qw| dbic_t_schema_4 2 |],
+ [qw| dbic_t_schema_5 1 |],
+ );
+ foreach my $t ( @test_schemas ) {
+ my ($sch_name, $start_num) = @$t;
+ #test with dbic_t_schema_2
+ apk_t_set($schema,"$sch_name.artist");
+ my $another_new;
+ lives_ok {
+ $another_new = $schema->resultset('Artist')->create({ name => 'Tollbooth Willy'});
+ is( $another_new->artistid,$start_num, "got correct artistid for $sch_name")
+ or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
+ } "$sch_name liid 1 did not die"
+ or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
+ lives_ok {
+ $another_new = $schema->resultset('Artist')->create({ name => 'Adam Sandler'});
+ is( $another_new->artistid,$start_num+1, "got correct artistid for $sch_name")
+ or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
+ } "$sch_name liid 2 did not die"
+ or diag "USED SEQUENCE: ".($schema->source('Artist')->column_info('artistid')->{sequence} || '<none>');
+
+ }
+
+ lives_ok {
+ apk_t_set($schema,'dbic_t_schema.artist');
+ my $new = $schema->resultset('Artist')->create({ name => 'foo' });
+ is($new->artistid, 4, "Auto-PK worked");
+ $new = $schema->resultset('Artist')->create({ name => 'bar' });
+ is($new->artistid, 5, "Auto-PK worked");
+ } 'old auto-pk tests did not die either';
+}
+
+# sets the artist table name and clears sequence name cache
+sub apk_t_set {
+ my ( $s, $n ) = @_;
+ $s->source("Artist")->name($n);
+ $s->source('Artist')->column_info('artistid')->{sequence} = undef; #< clear sequence name cache
+}
+
+
+######## 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';
+
+ __PACKAGE__->load_components(qw/Core/);
+ __PACKAGE__->table('apk');
+
+ @eapk_id_columns = qw( id1 id2 id3 id4 );
+ __PACKAGE__->add_columns(
+ map { $_ => { data_type => 'integer', is_auto_increment => 1 } }
+ @eapk_id_columns
+ );
+
+ __PACKAGE__->set_primary_key('id2'); #< note the SECOND column is
+ #the primary key
+}
+
+my @eapk_schemas;
+BEGIN{ @eapk_schemas = map "dbic_apk_$_", 0..5 }
+
+sub run_extended_apk_tests {
+ my $schema = shift;
+
+ #save the search path and reset it at the end
+ my $search_path_save = $schema->storage->dbh_do('_get_pg_search_path');
+
+ eapk_drop_all($schema,'no warn');
+
+ # make the test schemas and sequences
+ $schema->storage->dbh_do(sub {
+ my ( undef, $dbh ) = @_;
+
+ $dbh->do("CREATE SCHEMA $_")
+ for @eapk_schemas;
+
+ $dbh->do("CREATE SEQUENCE $eapk_schemas[5].fooseq");
+ $dbh->do("CREATE SEQUENCE $eapk_schemas[4].fooseq");
+ $dbh->do("CREATE SEQUENCE $eapk_schemas[3].fooseq");
+
+ $dbh->do("SET search_path = ".join ',', @eapk_schemas );
+ });
+
+ # 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, 0 );
+ eapk_poke( $schema, 2 );
+ eapk_poke( $schema, 4 );
+ eapk_poke( $schema, 1 );
+ eapk_poke( $schema, 0 );
+ eapk_poke( $schema, 1 );
+ 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)
+
+my %seqs; #< sanity-check hash of schema.table.col => currval of its sequence
+
+sub eapk_poke {
+ my ($s, $schema_num) = @_;
+
+ my $schema_name = defined $schema_num
+ ? $eapk_schemas[$schema_num]
+ : '';
+
+ my $schema_name_actual = $schema_name || $s->storage->dbh_do('_get_pg_search_path')->[0];
+
+ $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 {
+ my $new;
+ for my $inc (1,2,3) {
+ $new = $schema->resultset('ExtAPK')->create({});
+ for my $id (@eapk_id_columns) {
+ 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 || $s->storage->dbh_do('_get_pg_search_path')->[0];
+
+ diag "$schema.apk sequences: ",
+ join(', ',
+ map "$_:".($s->source('ExtAPK')->column_info($_)->{sequence} || '<none>'),
+ @eapk_id_columns
+ );
+}
+
+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) = @_;
+
+ $schema->storage->dbh_do(sub {
+ my (undef,$dbh) = @_;
+
+ my $searchpath_save;
+ if ( $a{with_search_path} ) {
+ ($searchpath_save) = $dbh->selectrow_array('SHOW search_path');
+
+ my $search_path = join ',',map {/\D/ ? $_ : $eapk_schemas[$_]} @{$a{with_search_path}};
+
+ $dbh->do("SET search_path = $search_path");
+ }
+
+ my $table_name = $a{qualify_table}
+ ? ($eapk_schemas[$a{qualify_table}] || die). ".apk"
+ : 'apk';
+ local $_[1]->{Warn} = 0;
+
+ my $id_def = $a{nextval}
+ ? "integer primary key not null default nextval('$a{nextval}'::regclass)"
+ : 'serial primary key';
+ $dbh->do(<<EOS);
+CREATE TABLE $table_name (
+ id1 serial
+ , id2 $id_def
+ , id3 serial
+ , id4 serial
+)
+EOS
+
+ if( $searchpath_save ) {
+ $dbh->do("SET search_path = $searchpath_save");
+ }
+ });
+}
+
+sub eapk_drop_all {
+ my ( $schema, $no_warn ) = @_;
+
+ $schema->storage->dbh_do(sub {
+ my (undef,$dbh) = @_;
+
+ local $dbh->{Warn} = 0;
+
+ # drop the test schemas
+ for (@eapk_schemas ) {
+ eval{ $dbh->do("DROP SCHEMA $_ CASCADE") };
+ diag $@ if $@ && !$no_warn;
+ }
+
+
+ });
+}