fix some issues with multi-db_schema support
Rafael Kitover [Wed, 28 Sep 2011 16:28:05 +0000 (12:28 -0400)]
Fix unique constraint detection for Oracle in multischema setups
(RT#70851) previously identically named tables in different schemas with
an identically named unique constraint would cause the columns in the
constraint definition to be repeated. Modifies the multischema extra
tests for all databases to test for the correctness of the dumped unique
constraint.

Update Oracle multischema extra tests to include the complete set of
test cases used in multischema test sets for other databases.

Update Oracle is_auto_increment detection code to work in multischema
setups. The new logic is by necessity fuzzier, please report false
positives to RT. At some point this will get rewritten to use a PL/SQL
parser on the trigger code.

Add a test for the miscaching of table keys when there are identically
named tables in different schemas for all databases. This was fixed for
MySQL in master.

Completely rewrite the table monikerization code to work better with
moniker_parts, preserving the functionality of all naming versions.
Schema/database names are transformed to parts of the moniker name using
String::ToIdentifier::EN::Unicode (or String::ToIdentifier::EN if

  naming => { force_ascii => 1 }

is set.) to translate non-identifier characters.

Add the next naming version, v8 as an experimental mode. In this mode,
the table names go through String::ToIdentifier::EN::Unicode as
well, instead of non-identifier characters being stripped out.

The clashing monikers error message is updated to mention moniker_parts
in multischema configurations.

The ::RelBuilder duplicate relationship name disambiguator has been
updated to handle the case of relationships in the same class pointing
to identically named tables in different schemas/databases.

Improve the multischema extra tests to include an identically named
table in both schemas and to use moniker_parts. This also reduces the
chances of moniker clashes on a tester's database.

Change the multidatabase tests to test both

    db_schema => { db1 => '%', db2 => '%' }

and

    db_schema => { '%' => '%' }

as well as to include an identically named table.

13 files changed:
Changes
Makefile.PL
lib/DBIx/Class/Schema/Loader/Base.pm
lib/DBIx/Class/Schema/Loader/DBI/Oracle.pm
lib/DBIx/Class/Schema/Loader/RelBuilder.pm
t/10_02mysql_common.t
t/10_03pg_common.t
t/10_04db2_common.t
t/10_05ora_common.t
t/10_06sybase_common.t
t/10_07mssql_common.t
t/10_08sqlanywhere_common.t
t/10_10informix_common.t

diff --git a/Changes b/Changes
index 5ece3fb..7442c61 100644 (file)
--- a/Changes
+++ b/Changes
@@ -3,6 +3,7 @@ Revision history for Perl extension DBIx::Class::Schema::Loader
         - remove dependency on File::Slurp
         - allow the constraint and exclude options to be used simultaneously
           (bphillips)
+        - fix Oracle multi-db_schema unique detection (RT#70851)
         - fix Oracle common tests fail with multi_schema due to not resetting
           the preserve_case option after the preserve_case tests (RT#70829)
         - handle <type> DEFAULT NULL for Pg
@@ -56,7 +57,6 @@ Revision history for Perl extension DBIx::Class::Schema::Loader
         - for dynamic schemas, if the naming option is set, will automatically
           turn on use_namespaces=1 as well. Set use_namespaces=0 to disable
           this behavior (RT#59849)
-        - bump File::Slurp dependency to 9999.14 - minimum supporting binmode
 
 0.07010  2011-03-04 08:26:31
         - add result_component_map option
index 359ff9a..e11bfb7 100644 (file)
@@ -54,6 +54,7 @@ requires 'namespace::clean'            => '0.20';
 requires 'Scope::Guard'                => 0;
 requires 'Exporter'                    => '5.63';
 requires 'Try::Tiny'                   => 0;
+requires 'String::ToIdentifier::EN'    => '0.04';
 
 if ($Module::Install::AUTHOR && ! $args->{skip_author_deps}) {
     eval { require Module::Install::ReadmeFromPod }
index befe435..b42584b 100644 (file)
@@ -5,15 +5,17 @@ use warnings;
 use base qw/Class::Accessor::Grouped Class::C3::Componentised/;
 use mro 'c3';
 use Carp::Clan qw/^DBIx::Class/;
-use DBIx::Class::Schema::Loader::RelBuilder;
-use Data::Dump qw/ dump /;
-use POSIX qw//;
-use File::Spec qw//;
-use Cwd qw//;
-use Digest::MD5 qw//;
-use Lingua::EN::Inflect::Number qw//;
-use Lingua::EN::Inflect::Phrase qw//;
-use File::Temp qw//;
+use DBIx::Class::Schema::Loader::RelBuilder ();
+use Data::Dump 'dump';
+use POSIX ();
+use File::Spec ();
+use Cwd ();
+use Digest::MD5 ();
+use Lingua::EN::Inflect::Number ();
+use Lingua::EN::Inflect::Phrase ();
+use String::ToIdentifier::EN ();
+use String::ToIdentifier::EN::Unicode ();
+use File::Temp ();
 use Class::Unload;
 use Class::Inspector ();
 use Scalar::Util 'looks_like_number';
@@ -184,6 +186,12 @@ How to name Result classes.
 
 How to name column accessors in Result classes.
 
+=item force_ascii
+
+For L</v8> mode and later, uses L<String::ToIdentifier::EN> instead of
+L<String::ToIdentifier::EM::Unicode> to force monikers and other identifiers
+such as relationship names to ASCII.
+
 =back
 
 The values can be:
@@ -228,6 +236,17 @@ transition instead of just being lowercased, so C<FooId> becomes C<foo_id>.
 If you don't have any CamelCase table or column names, you can upgrade without
 breaking any of your code.
 
+=item v8
+
+(EXPERIMENTAL)
+
+The default mode is L</v7>, to get L</v8> mode, you have to specify it in
+L</naming> explictly until C<0.08> comes out.
+
+L</monikers> are created using L<String::ToIdentifier::EN::Unicode> or
+L<String::ToIdentifier::EN> if L</force_ascii> is set; this is only significant
+for table names with non C<\w> characters such as C<.>.
+
 =item preserve
 
 For L</monikers>, this option does not inflect the table names but makes
@@ -1425,7 +1444,8 @@ sub _load_tables {
 
     if (@clashes) {
       die   'Unable to load schema - chosen moniker/class naming style results in moniker clashes. '
-          . 'Either change the naming style, or supply an explicit moniker_map: '
+          . 'In multi db_schema configurations you may need to set moniker_parts, '
+          . 'otherwise change the naming style, or supply an explicit moniker_map: '
           . join ('; ', @clashes)
           . "\n"
       ;
@@ -2297,41 +2317,51 @@ sub tables {
 
 # Make a moniker from a table
 sub _default_table2moniker {
-    no warnings 'uninitialized';
     my ($self, $table) = @_;
 
+    my ($v) = ($self->naming->{monikers}||$CURRENT_V) =~ /^v(\d+)\z/;
+
     my @name_parts = map $table->$_, @{ $self->moniker_parts };
 
     my $name_idx = firstidx { $_ eq 'name' } @{ $self->moniker_parts };
 
-    if ($self->naming->{monikers} eq 'v4') {
-        return join '', map ucfirst, map split(/[\W_]+/, lc $_), @name_parts;
-    }
-    elsif ($self->naming->{monikers} eq 'v5') {
-        my @parts = map lc, @name_parts;
-        $parts[$name_idx] = Lingua::EN::Inflect::Number::to_S($parts[$name_idx]);
+    my $to_identifier = $self->naming->{force_ascii} ?
+        \&String::ToIdentifier::EN::to_identifier
+        : \&String::ToIdentifier::EN::Unicode::to_identifier;
 
-        return join '', map ucfirst, map split(/[\W_]+/, $_), @parts;
-    }
-    elsif ($self->naming->{monikers} eq 'v6') {
-        (my $as_phrase = join '', map lc, @name_parts) =~ s/_+/ /g;
-        my $inflected = Lingua::EN::Inflect::Phrase::to_S($as_phrase);
+    my @all_parts;
 
-        return join '', map ucfirst, split /\W+/, $inflected;
-    }
+    foreach my $i (0 .. $#name_parts) {
+        my $part = $name_parts[$i];
 
-    my @words = map lc, map split_name $_, @name_parts;
-    my $as_phrase = join ' ', @words;
+        if ($i != $name_idx || $v > 7) {
+            $part = $to_identifier->($part, '_');
+        }
+
+        if ($i == $name_idx && $v == 5) {
+            $part = Lingua::EN::Inflect::Number::to_S($part);
+        }
+
+        my @part_parts = map lc, $v > 6 ? split_name $part : split /[\W_]+/, $part;
+
+        if ($i == $name_idx && $v >= 6) {
+            my $as_phrase = join ' ', @part_parts;
 
-    my $inflected = $self->naming->{monikers} eq 'plural' ?
-        Lingua::EN::Inflect::Phrase::to_PL($as_phrase)
-        :
-        $self->naming->{monikers} eq 'preserve' ?
-            $as_phrase
-            :
-            Lingua::EN::Inflect::Phrase::to_S($as_phrase);
+            my $inflected = ($self->naming->{monikers}||'') eq 'plural' ?
+                Lingua::EN::Inflect::Phrase::to_PL($as_phrase)
+                :
+                ($self->naming->{monikers}||'') eq 'preserve' ?
+                    $as_phrase
+                    :
+                    Lingua::EN::Inflect::Phrase::to_S($as_phrase);
+
+            @part_parts = split /\s+/, $inflected;
+        }
+
+        push @all_parts, map ucfirst, @part_parts;
+    }
 
-    return join '', map ucfirst, split /\W+/, $inflected;
+    return join '', @all_parts;
 }
 
 sub _table2moniker {
index 68b2ff6..4f65da7 100644 (file)
@@ -91,10 +91,12 @@ sub _table_uniq_info {
     my ($self, $table) = @_;
 
     my $sth = $self->dbh->prepare_cached(<<'EOF', {}, 1);
-SELECT constraint_name, acc.column_name
-FROM all_constraints
-JOIN all_cons_columns acc USING (constraint_name)
-WHERE acc.table_name=? and acc.owner = ? AND constraint_type='U'
+SELECT ac.constraint_name, acc.column_name
+FROM all_constraints ac, all_cons_columns acc
+WHERE acc.table_name=? AND acc.owner = ?
+    AND ac.table_name = acc.table_name AND ac.owner = acc.owner
+    AND acc.constraint_name = ac.constraint_name
+    AND ac.constraint_type='U'
 ORDER BY acc.position
 EOF
 
@@ -158,26 +160,26 @@ sub _columns_info_for {
     local $self->dbh->{LongTruncOk} = 1;
 
     my $sth = $self->dbh->prepare_cached(<<'EOF', {}, 1);
-SELECT atc.column_name, ut.trigger_body
-FROM all_triggers ut
-JOIN all_trigger_cols atc USING (trigger_name)
-WHERE atc.table_name = ?
-AND lower(column_usage) LIKE '%new%' AND lower(column_usage) LIKE '%out%'
+SELECT trigger_body
+FROM all_triggers
+WHERE table_name = ? AND table_owner = ?
 AND upper(trigger_type) LIKE '%BEFORE EACH ROW%' AND lower(triggering_event) LIKE '%insert%'
 EOF
 
-    $sth->execute($table->name);
+    $sth->execute($table->name, $table->schema);
 
-    while (my ($col_name, $trigger_body) = $sth->fetchrow_array) {
-        $col_name = $self->_lc($col_name);
+    while (my ($trigger_body) = $sth->fetchrow_array) {
+        if (my ($seq_schema, $seq_name) = $trigger_body =~ /(?:\."?(\w+)"?)?"?(\w+)"?\.nextval/i) {
+            if (my ($col_name) = $trigger_body =~ /:new\.(\w+)/i) {
+                $col_name = $self->_lc($col_name);
 
-        $result->{$col_name}{is_auto_increment} = 1;
+                $result->{$col_name}{is_auto_increment} = 1;
 
-        if (my ($seq_schema, $seq_name) = $trigger_body =~ /(?:\."?(\w+)"?)?"?(\w+)"?\.nextval/i) {
-            $seq_schema = $self->_lc($seq_schema || $table->schema);
-            $seq_name   = $self->_lc($seq_name);
+                $seq_schema = $self->_lc($seq_schema || $table->schema);
+                $seq_name   = $self->_lc($seq_name);
 
-            $result->{$col_name}{sequence} = ($self->qualify_objects ? ($seq_schema . '.') : '') . $seq_name;
+                $result->{$col_name}{sequence} = ($self->qualify_objects ? ($seq_schema . '.') : '') . $seq_name;
+            }
         }
     }
 
index 29d17f4..9b75e80 100644 (file)
@@ -8,10 +8,12 @@ use Carp::Clan qw/^DBIx::Class/;
 use Scalar::Util 'weaken';
 use DBIx::Class::Schema::Loader::Utils qw/split_name slurp_file/;
 use Try::Tiny;
-use List::MoreUtils 'apply';
+use List::MoreUtils qw/apply uniq any/;
 use namespace::clean;
 use Lingua::EN::Inflect::Phrase ();
 use Lingua::EN::Tagger ();
+use String::ToIdentifier::EN ();
+use String::ToIdentifier::EN::Unicode ();
 use Class::Unload ();
 use Class::Inspector ();
 
@@ -501,12 +503,57 @@ sub _adjectives {
     return @adjectives;
 }
 
+sub _name_to_identifier {
+    my ($self, $name) = @_;
+
+    my $to_identifier = $self->loader->naming->{force_ascii} ?
+        \&String::ToIdentifier::EN::to_identifier
+        : \&String::ToIdentifier::EN::Unicode::to_identifier;
+
+    return join '_', map lc, split_name $to_identifier->($name, '_');
+}
+
 sub _disambiguate {
     my ($self, $all_rels, $dups) = @_;
 
-    foreach my $dup (keys %$dups) {
+    DUP: foreach my $dup (keys %$dups) {
         my @rels = @{ $dups->{$dup} };
 
+        # Check if there are rels to the same table name in different
+        # schemas/databases, if so qualify them.
+        my @tables = map $self->loader->moniker_to_table->{$_->{extra}{remote_moniker}},
+                        @rels;
+
+        # databases are different, prepend database
+        if ($tables[0]->can('database') && (uniq map $_->database||'', @tables) > 1) {
+            # If any rels are in the same database, we have to distinguish by
+            # both schema and database.
+            my %db_counts;
+            $db_counts{$_}++ for map $_->database, @tables;
+            my $use_schema = any { $_ > 1 } values %db_counts;
+
+            foreach my $i (0..$#rels) {
+                my $rel   = $rels[$i];
+                my $table = $tables[$i];
+
+                $rel->{args}[0] = $self->_name_to_identifier($table->database)
+                    . ($use_schema ? ('_' . $self->name_to_identifier($table->schema)) : '')
+                    . '_' . $rel->{args}[0];
+            }
+            next DUP;
+        }
+        # schemas are different, prepend schema
+        elsif ((uniq map $_->schema||'', @tables) > 1) {
+            foreach my $i (0..$#rels) {
+                my $rel   = $rels[$i];
+                my $table = $tables[$i];
+
+                $rel->{args}[0] = $self->_name_to_identifier($table->schema)
+                    . '_' . $rel->{args}[0];
+            }
+            next DUP;
+        }
+
         foreach my $rel (@rels) {
             next if $rel->{method} eq 'belongs_to';
 
index 65292e6..bc457cd 100644 (file)
@@ -183,7 +183,7 @@ my $tester = dbixcsl_common_tests->new(
         ],
         pre_drop_ddl => [ 'DROP VIEW mysql_loader_test2', ],
         drop => [ 'mysql_loader-test1', 'mysql_loader_test3' ],
-        count => 5 + 28 * 2,
+        count => 5 + 30 * 2,
         run => sub {
             my ($monikers, $classes);
             ($schema, $monikers, $classes) = @_;
@@ -219,7 +219,7 @@ my $tester = dbixcsl_common_tests->new(
                     $dbh->do('CREATE DATABASE `dbicsl-test`');
                 }
                 catch {
-                    skip "no CREATE DATABASE privileges", 28 * 2;
+                    skip "no CREATE DATABASE privileges", 30 * 2;
                 };
 
                 $dbh->do(<<"EOF");
@@ -232,11 +232,27 @@ EOF
                     CREATE TABLE `dbicsl-test`.mysql_loader_test5 (
                         id INT AUTO_INCREMENT PRIMARY KEY,
                         value VARCHAR(100),
-                        four_id INTEGER UNIQUE,
+                        four_id INTEGER,
+                        CONSTRAINT loader_test5_uniq UNIQUE (four_id),
                         FOREIGN KEY (four_id) REFERENCES `dbicsl-test`.mysql_loader_test4 (id)
                     ) $innodb
 EOF
+
                 $dbh->do('CREATE DATABASE `dbicsl.test`');
+
+                # Test that keys are correctly cached by naming the primary and
+                # unique keys in this table with the same name as a table in
+                # the `dbicsl-test` schema differently.
+                $dbh->do(<<"EOF");
+                    CREATE TABLE `dbicsl.test`.mysql_loader_test5 (
+                        pk INT AUTO_INCREMENT PRIMARY KEY,
+                        value VARCHAR(100),
+                        four_id INTEGER,
+                        CONSTRAINT loader_test5_uniq UNIQUE (four_id),
+                        FOREIGN KEY (four_id) REFERENCES `dbicsl-test`.mysql_loader_test4 (id)
+                    ) $innodb
+EOF
+
                 $dbh->do(<<"EOF");
                     CREATE TABLE `dbicsl.test`.mysql_loader_test6 (
                         id INT AUTO_INCREMENT PRIMARY KEY,
@@ -303,6 +319,7 @@ EOF
                             {
                                 naming => 'current',
                                 db_schema => $db_schema,
+                                moniker_parts => ['schema', 'name'],
                                 dump_directory => EXTRA_DUMP_DIR,
                                 quiet => 1,
                             },
@@ -321,7 +338,7 @@ EOF
                     } 'connected test schema';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('MysqlLoaderTest4');
+                        ok $rsrc = $test_schema->source('DbicslDashTestMysqlLoaderTest4');
                     } 'got source for table in database name with dash';
 
                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -334,7 +351,7 @@ EOF
                         'column in database name with dash';
 
                     lives_and {
-                        ok $rs = $test_schema->resultset('MysqlLoaderTest4');
+                        ok $rs = $test_schema->resultset('DbicslDashTestMysqlLoaderTest4');
                     } 'got resultset for table in database name with dash';
 
                     lives_and {
@@ -344,7 +361,7 @@ EOF
                     SKIP: {
                         skip 'set the environment variable DBICTEST_MYSQL_INNODB=1 to test relationships', 3 unless $test_innodb;
 
-                        $rel_info = try { $rsrc->relationship_info('mysql_loader_test5') };
+                        $rel_info = try { $rsrc->relationship_info('dbicsl_dash_test_mysql_loader_test5') };
 
                         is_deeply $rel_info->{cond}, {
                             'foreign.four_id' => 'self.id'
@@ -358,7 +375,7 @@ EOF
                     }
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('MysqlLoaderTest5');
+                        ok $rsrc = $test_schema->source('DbicslDashTestMysqlLoaderTest5');
                     } 'got source for table in database name with dash';
 
                     %uniqs = try { $rsrc->unique_constraints };
@@ -366,8 +383,13 @@ EOF
                     is keys %uniqs, 2,
                         'got unique and primary constraint in database name with dash';
 
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['four_id'],
+                        'unique constraint is correct in database name with dash');
+
                     lives_and {
-                        ok $rsrc = $test_schema->source('MysqlLoaderTest6');
+                        ok $rsrc = $test_schema->source('DbicslDotTestMysqlLoaderTest6');
                     } 'got source for table in database name with dot';
 
                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -380,7 +402,7 @@ EOF
                         'column in database name with dot introspected correctly';
 
                     lives_and {
-                        ok $rs = $test_schema->resultset('MysqlLoaderTest6');
+                        ok $rs = $test_schema->resultset('DbicslDotTestMysqlLoaderTest6');
                     } 'got resultset for table in database name with dot';
 
                     lives_and {
@@ -404,7 +426,7 @@ EOF
                     }
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('MysqlLoaderTest7');
+                        ok $rsrc = $test_schema->source('DbicslDotTestMysqlLoaderTest7');
                     } 'got source for table in database name with dot';
 
                     %uniqs = try { $rsrc->unique_constraints };
@@ -412,26 +434,31 @@ EOF
                     is keys %uniqs, 2,
                         'got unique and primary constraint in database name with dot';
 
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['six_id'],
+                        'unique constraint is correct in database name with dot');
+
                     SKIP: {
                         skip 'set the environment variable DBICTEST_MYSQL_INNODB=1 to test relationships', 4 unless $test_innodb;
 
                         lives_and {
-                            ok $test_schema->source('MysqlLoaderTest6')
+                            ok $test_schema->source('DbicslDotTestMysqlLoaderTest6')
                                 ->has_relationship('mysql_loader_test4');
                         } 'cross-database relationship in multi-db_schema';
 
                         lives_and {
-                            ok $test_schema->source('MysqlLoaderTest4')
+                            ok $test_schema->source('DbicslDashTestMysqlLoaderTest4')
                                 ->has_relationship('mysql_loader_test6s');
                         } 'cross-database relationship in multi-db_schema';
 
                         lives_and {
-                            ok $test_schema->source('MysqlLoaderTest8')
+                            ok $test_schema->source('DbicslDashTestMysqlLoaderTest8')
                                 ->has_relationship('mysql_loader_test7');
                         } 'cross-database relationship in multi-db_schema';
 
                         lives_and {
-                            ok $test_schema->source('MysqlLoaderTest7')
+                            ok $test_schema->source('DbicslDotTestMysqlLoaderTest7')
                                 ->has_relationship('mysql_loader_test8s');
                         } 'cross-database relationship in multi-db_schema';
                     }
@@ -457,6 +484,7 @@ END {
                                '`dbicsl-test`.mysql_loader_test8',
                                '`dbicsl.test`.mysql_loader_test7',
                                '`dbicsl.test`.mysql_loader_test6',
+                               '`dbicsl.test`.mysql_loader_test5',
                                '`dbicsl-test`.mysql_loader_test5',
                                '`dbicsl-test`.mysql_loader_test4') {
                 try {
index eb845a7..8d17f1d 100644 (file)
@@ -190,13 +190,22 @@ my $tester = dbixcsl_common_tests->new(
                 CREATE TABLE "dbicsl-test".pg_loader_test5 (
                     id SERIAL PRIMARY KEY,
                     value VARCHAR(100),
-                    four_id INTEGER UNIQUE REFERENCES "dbicsl-test".pg_loader_test4 (id)
+                    four_id INTEGER REFERENCES "dbicsl-test".pg_loader_test4 (id),
+                    CONSTRAINT loader_test5_uniq UNIQUE (four_id)
                 )
             },
             q{
                 CREATE SCHEMA "dbicsl.test"
             },
             q{
+                CREATE TABLE "dbicsl.test".pg_loader_test5 (
+                    pk SERIAL PRIMARY KEY,
+                    value VARCHAR(100),
+                    four_id INTEGER REFERENCES "dbicsl-test".pg_loader_test4 (id),
+                    CONSTRAINT loader_test5_uniq UNIQUE (four_id)
+                )
+            },
+            q{
                 CREATE TABLE "dbicsl.test".pg_loader_test6 (
                     id SERIAL PRIMARY KEY,
                     value VARCHAR(100),
@@ -225,7 +234,7 @@ my $tester = dbixcsl_common_tests->new(
             'DROP TYPE pg_loader_test_enum',
         ],
         drop  => [ qw/ pg_loader_test1 pg_loader_test2 / ],
-        count => 4 + 28 * 2,
+        count => 4 + 30 * 2,
         run   => sub {
             my ($schema, $monikers, $classes) = @_;
 
@@ -266,6 +275,7 @@ my $tester = dbixcsl_common_tests->new(
                         {
                             naming => 'current',
                             db_schema => $db_schema,
+                            moniker_parts => [qw/schema name/],
                             preserve_case => 1,
                             dump_directory => EXTRA_DUMP_DIR,
                             quiet => 1,
@@ -289,7 +299,7 @@ my $tester = dbixcsl_common_tests->new(
                 } 'connected test schema';
 
                 lives_and {
-                    ok $rsrc = $test_schema->source('PgLoaderTest4');
+                    ok $rsrc = $test_schema->source('DbicslDashTestPgLoaderTest4');
                 } 'got source for table in schema name with dash';
 
                 is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -302,14 +312,14 @@ my $tester = dbixcsl_common_tests->new(
                     'column in schema name with dash';
 
                 lives_and {
-                    ok $rs = $test_schema->resultset('PgLoaderTest4');
+                    ok $rs = $test_schema->resultset('DbicslDashTestPgLoaderTest4');
                 } 'got resultset for table in schema name with dash';
 
                 lives_and {
                     ok $row = $rs->create({ value => 'foo' });
                 } 'executed SQL on table in schema name with dash';
 
-                $rel_info = try { $rsrc->relationship_info('pg_loader_test5') };
+                $rel_info = try { $rsrc->relationship_info('dbicsl_dash_test_pg_loader_test5') };
 
                 is_deeply $rel_info->{cond}, {
                     'foreign.four_id' => 'self.id'
@@ -322,7 +332,7 @@ my $tester = dbixcsl_common_tests->new(
                     'relationship in schema name with dash';
 
                 lives_and {
-                    ok $rsrc = $test_schema->source('PgLoaderTest5');
+                    ok $rsrc = $test_schema->source('DbicslDashTestPgLoaderTest5');
                 } 'got source for table in schema name with dash';
 
                 %uniqs = try { $rsrc->unique_constraints };
@@ -330,8 +340,13 @@ my $tester = dbixcsl_common_tests->new(
                 is keys %uniqs, 2,
                     'got unique and primary constraint in schema name with dash';
 
+                delete $uniqs{primary};
+
+                is_deeply ((values %uniqs)[0], ['four_id'],
+                    'unique constraint is correct in schema name with dash');
+
                 lives_and {
-                    ok $rsrc = $test_schema->source('PgLoaderTest6');
+                    ok $rsrc = $test_schema->source('DbicslDotTestPgLoaderTest6');
                 } 'got source for table in schema name with dot';
 
                 is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -344,7 +359,7 @@ my $tester = dbixcsl_common_tests->new(
                     'column in schema name with dot introspected correctly';
 
                 lives_and {
-                    ok $rs = $test_schema->resultset('PgLoaderTest6');
+                    ok $rs = $test_schema->resultset('DbicslDotTestPgLoaderTest6');
                 } 'got resultset for table in schema name with dot';
 
                 lives_and {
@@ -364,7 +379,7 @@ my $tester = dbixcsl_common_tests->new(
                     'relationship in schema name with dot';
 
                 lives_and {
-                    ok $rsrc = $test_schema->source('PgLoaderTest7');
+                    ok $rsrc = $test_schema->source('DbicslDotTestPgLoaderTest7');
                 } 'got source for table in schema name with dot';
 
                 %uniqs = try { $rsrc->unique_constraints };
@@ -372,23 +387,28 @@ my $tester = dbixcsl_common_tests->new(
                 is keys %uniqs, 2,
                     'got unique and primary constraint in schema name with dot';
 
+                delete $uniqs{primary};
+
+                is_deeply ((values %uniqs)[0], ['six_id'],
+                    'unique constraint is correct in schema name with dot');
+
                 lives_and {
-                    ok $test_schema->source('PgLoaderTest6')
+                    ok $test_schema->source('DbicslDotTestPgLoaderTest6')
                         ->has_relationship('pg_loader_test4');
                 } 'cross-schema relationship in multi-db_schema';
 
                 lives_and {
-                    ok $test_schema->source('PgLoaderTest4')
+                    ok $test_schema->source('DbicslDashTestPgLoaderTest4')
                         ->has_relationship('pg_loader_test6s');
                 } 'cross-schema relationship in multi-db_schema';
 
                 lives_and {
-                    ok $test_schema->source('PgLoaderTest8')
+                    ok $test_schema->source('DbicslDashTestPgLoaderTest8')
                         ->has_relationship('pg_loader_test7');
                 } 'cross-schema relationship in multi-db_schema';
 
                 lives_and {
-                    ok $test_schema->source('PgLoaderTest7')
+                    ok $test_schema->source('DbicslDotTestPgLoaderTest7')
                         ->has_relationship('pg_loader_test8s');
                 } 'cross-schema relationship in multi-db_schema';
             }
index 03b4b54..b86ca49 100644 (file)
@@ -104,7 +104,7 @@ my $tester = dbixcsl_common_tests->new(
 #        datalink           => { data_type => 'datalink' },
     },
     extra => {
-        count => 28 * 2,
+        count => 30 * 2,
         run => sub {
             SKIP: {
                 $schema = shift;
@@ -129,12 +129,22 @@ EOF
                     CREATE TABLE "dbicsl-test".db2_loader_test5 (
                         id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,
                         value VARCHAR(100),
-                        four_id INTEGER NOT NULL UNIQUE,
+                        four_id INTEGER NOT NULL,
+                        CONSTRAINT loader_test5_uniq UNIQUE (four_id),
                         FOREIGN KEY (four_id) REFERENCES "dbicsl-test".db2_loader_test4 (id)
                     )
 EOF
                 $dbh->do('CREATE SCHEMA "dbicsl.test"');
                 $dbh->do(<<"EOF");
+                    CREATE TABLE "dbicsl.test".db2_loader_test5 (
+                        pk INT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,
+                        value VARCHAR(100),
+                        four_id INTEGER NOT NULL,
+                        CONSTRAINT loader_test5_uniq UNIQUE (four_id),
+                        FOREIGN KEY (four_id) REFERENCES "dbicsl-test".db2_loader_test4 (id)
+                    )
+EOF
+                $dbh->do(<<"EOF");
                     CREATE TABLE "dbicsl.test".db2_loader_test6 (
                         id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,
                         value VARCHAR(100),
@@ -175,6 +185,7 @@ EOF
                             {
                                 naming => 'current',
                                 db_schema => $db_schema,
+                                moniker_parts => [qw/schema name/],
                                 dump_directory => EXTRA_DUMP_DIR,
                                 quiet => 1,
                             },
@@ -193,7 +204,7 @@ EOF
                     } 'connected test schema';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('Db2LoaderTest4');
+                        ok $rsrc = $test_schema->source('DbicslDashTestDb2LoaderTest4');
                     } 'got source for table in schema name with dash';
 
                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -206,14 +217,14 @@ EOF
                         'column in schema name with dash';
 
                     lives_and {
-                        ok $rs = $test_schema->resultset('Db2LoaderTest4');
+                        ok $rs = $test_schema->resultset('DbicslDashTestDb2LoaderTest4');
                     } 'got resultset for table in schema name with dash';
 
                     lives_and {
                         ok $row = $rs->create({ value => 'foo' });
                     } 'executed SQL on table in schema name with dash';
 
-                    $rel_info = try { $rsrc->relationship_info('db2_loader_test5') };
+                    $rel_info = try { $rsrc->relationship_info('dbicsl_dash_test_db2_loader_test5') };
 
                     is_deeply $rel_info->{cond}, {
                         'foreign.four_id' => 'self.id'
@@ -226,7 +237,7 @@ EOF
                         'relationship in schema name with dash';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('Db2LoaderTest5');
+                        ok $rsrc = $test_schema->source('DbicslDashTestDb2LoaderTest5');
                     } 'got source for table in schema name with dash';
 
                     %uniqs = try { $rsrc->unique_constraints };
@@ -234,8 +245,13 @@ EOF
                     is keys %uniqs, 2,
                         'got unique and primary constraint in schema name with dash';
 
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['four_id'],
+                        'correct unique constraint in schema name with dash');
+
                     lives_and {
-                        ok $rsrc = $test_schema->source('Db2LoaderTest6');
+                        ok $rsrc = $test_schema->source('DbicslDotTestDb2LoaderTest6');
                     } 'got source for table in schema name with dot';
 
                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -248,7 +264,7 @@ EOF
                         'column in schema name with dot introspected correctly';
 
                     lives_and {
-                        ok $rs = $test_schema->resultset('Db2LoaderTest6');
+                        ok $rs = $test_schema->resultset('DbicslDotTestDb2LoaderTest6');
                     } 'got resultset for table in schema name with dot';
 
                     lives_and {
@@ -268,7 +284,7 @@ EOF
                         'relationship in schema name with dot';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('Db2LoaderTest7');
+                        ok $rsrc = $test_schema->source('DbicslDotTestDb2LoaderTest7');
                     } 'got source for table in schema name with dot';
 
                     %uniqs = try { $rsrc->unique_constraints };
@@ -276,23 +292,28 @@ EOF
                     is keys %uniqs, 2,
                         'got unique and primary constraint in schema name with dot';
 
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['six_id'],
+                        'correct unique constraint in schema name with dot');
+
                     lives_and {
-                        ok $test_schema->source('Db2LoaderTest6')
+                        ok $test_schema->source('DbicslDotTestDb2LoaderTest6')
                             ->has_relationship('db2_loader_test4');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('Db2LoaderTest4')
+                        ok $test_schema->source('DbicslDashTestDb2LoaderTest4')
                             ->has_relationship('db2_loader_test6s');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('Db2LoaderTest8')
+                        ok $test_schema->source('DbicslDashTestDb2LoaderTest8')
                             ->has_relationship('db2_loader_test7');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('Db2LoaderTest7')
+                        ok $test_schema->source('DbicslDotTestDb2LoaderTest7')
                             ->has_relationship('db2_loader_test8s');
                     } 'cross-schema relationship in multi-db_schema';
                 }
@@ -311,6 +332,7 @@ END {
                                '"dbicsl.test".db2_loader_test7',
                                '"dbicsl.test".db2_loader_test6',
                                '"dbicsl-test".db2_loader_test5',
+                               '"dbicsl.test".db2_loader_test5',
                                '"dbicsl-test".db2_loader_test4') {
                 try {
                     $dbh->do("DROP TABLE $table");
index 29127e5..463c968 100644 (file)
@@ -3,9 +3,10 @@ use warnings;
 use Test::More;
 use Test::Exception;
 use DBIx::Class::Schema::Loader 'make_schema_at';
-use DBIx::Class::Schema::Loader::Utils 'slurp_file';
+use DBIx::Class::Schema::Loader::Utils qw/slurp_file split_name/;
 use Try::Tiny;
 use File::Path 'rmtree';
+use String::ToIdentifier::EN::Unicode 'to_identifier';
 use namespace::clean;
 
 use lib qw(t/lib);
@@ -20,27 +21,31 @@ my $password = $ENV{DBICTEST_ORA_PASS} || '';
 
 my ($schema, $extra_schema); # for cleanup in END for extra tests
 
+my $auto_inc_cb = sub {
+    my ($table, $col) = @_;
+    return (
+        qq{ CREATE SEQUENCE ${table}_${col}_seq START WITH 1 INCREMENT BY 1},
+        qq{ 
+            CREATE OR REPLACE TRIGGER ${table}_${col}_trigger
+            BEFORE INSERT ON ${table}
+            FOR EACH ROW
+            BEGIN
+                SELECT ${table}_${col}_seq.nextval INTO :NEW.${col} FROM dual;
+            END;
+        }
+    );
+};
+
+my $auto_inc_drop_cb = sub {
+    my ($table, $col) = @_;
+    return qq{ DROP SEQUENCE ${table}_${col}_seq };
+};
+
 my $tester = dbixcsl_common_tests->new(
     vendor      => 'Oracle',
     auto_inc_pk => 'INTEGER NOT NULL PRIMARY KEY',
-    auto_inc_cb => sub {
-        my ($table, $col) = @_;
-        return (
-            qq{ CREATE SEQUENCE ${table}_${col}_seq START WITH 1 INCREMENT BY 1},
-            qq{ 
-                CREATE OR REPLACE TRIGGER ${table}_${col}_trigger
-                BEFORE INSERT ON ${table}
-                FOR EACH ROW
-                BEGIN
-                    SELECT ${table}_${col}_seq.nextval INTO :NEW.${col} FROM dual;
-                END;
-            }
-        );
-    },
-    auto_inc_drop_cb => sub {
-        my ($table, $col) = @_;
-        return qq{ DROP SEQUENCE ${table}_${col}_seq };
-    },
+    auto_inc_cb => $auto_inc_cb,
+    auto_inc_drop_cb => $auto_inc_drop_cb, 
     preserve_case_mode_is_exclusive => 1,
     quote_char                      => '"',
     dsn         => $dsn,
@@ -154,7 +159,7 @@ my $tester = dbixcsl_common_tests->new(
             q{ COMMENT ON COLUMN oracle_loader_test1.value IS 'oracle_loader_test1.value column comment' },
         ],
         drop  => [qw/oracle_loader_test1/],
-        count => 3 + 6 * 2,
+        count => 3 + 30 * 2,
         run   => sub {
             my ($monikers, $classes);
             ($schema, $monikers, $classes) = @_;
@@ -202,7 +207,36 @@ my $tester = dbixcsl_common_tests->new(
                         value VARCHAR(100)
                     )
 EOF
+
+                $dbh1->do($_) for $auto_inc_cb->('oracle_loader_test4', 'id');
+
                 $dbh1->do("GRANT ALL ON oracle_loader_test4 TO $schema2");
+                $dbh1->do("GRANT ALL ON oracle_loader_test4_id_seq TO $schema2");
+
+                $dbh1->do(<<"EOF");
+                    CREATE TABLE oracle_loader_test5 (
+                        id INT NOT NULL PRIMARY KEY,
+                        value VARCHAR(100),
+                        four_id INT REFERENCES ${schema1}.oracle_loader_test4 (id),
+                        CONSTRAINT ora_loader5_uniq UNIQUE (four_id)
+                    )
+EOF
+                $dbh1->do($_) for $auto_inc_cb->('oracle_loader_test5', 'id');
+                $dbh1->do("GRANT ALL ON oracle_loader_test5 TO $schema2");
+                $dbh1->do("GRANT ALL ON oracle_loader_test5_id_seq TO $schema2");
+
+                $dbh2->do(<<"EOF");
+                    CREATE TABLE oracle_loader_test5 (
+                        pk INT NOT NULL PRIMARY KEY,
+                        value VARCHAR(100),
+                        four_id INT REFERENCES ${schema1}.oracle_loader_test4 (id),
+                        CONSTRAINT ora_loader5_uniq UNIQUE (four_id)
+                    )
+EOF
+                $dbh2->do($_) for $auto_inc_cb->('oracle_loader_test5', 'pk');
+                $dbh2->do("GRANT ALL ON oracle_loader_test5 TO $schema1");
+                $dbh2->do("GRANT ALL ON oracle_loader_test5_pk_seq TO $schema1");
+
                 $dbh2->do(<<"EOF");
                     CREATE TABLE oracle_loader_test6 (
                         id INT NOT NULL PRIMARY KEY,
@@ -210,14 +244,21 @@ EOF
                         oracle_loader_test4_id INT REFERENCES ${schema1}.oracle_loader_test4 (id)
                     )
 EOF
+                $dbh2->do($_) for $auto_inc_cb->('oracle_loader_test6', 'id');
                 $dbh2->do("GRANT ALL ON oracle_loader_test6 to $schema1");
+                $dbh2->do("GRANT ALL ON oracle_loader_test6_id_seq TO $schema1");
+
                 $dbh2->do(<<"EOF");
                     CREATE TABLE oracle_loader_test7 (
                         id INT NOT NULL PRIMARY KEY,
-                        value VARCHAR(100)
+                        value VARCHAR(100),
+                        six_id INT UNIQUE REFERENCES ${schema2}.oracle_loader_test6 (id)
                     )
 EOF
+                $dbh2->do($_) for $auto_inc_cb->('oracle_loader_test7', 'id');
                 $dbh2->do("GRANT ALL ON oracle_loader_test7 to $schema1");
+                $dbh2->do("GRANT ALL ON oracle_loader_test7_id_seq TO $schema1");
+
                 $dbh1->do(<<"EOF");
                     CREATE TABLE oracle_loader_test8 (
                         id INT NOT NULL PRIMARY KEY,
@@ -225,6 +266,22 @@ EOF
                         oracle_loader_test7_id INT REFERENCES ${schema2}.oracle_loader_test7 (id)
                     )
 EOF
+                $dbh1->do($_) for $auto_inc_cb->('oracle_loader_test8', 'id');
+                $dbh1->do("GRANT ALL ON oracle_loader_test8 to $schema2");
+                $dbh1->do("GRANT ALL ON oracle_loader_test8_id_seq TO $schema2");
+
+                # We add schema to moniker_parts, so make a monikers hash for
+                # the tests, of the form schemanum.tablenum
+                my $schema1_moniker = join '', map ucfirst lc, split_name to_identifier $schema1;
+                my $schema2_moniker = join '', map ucfirst lc, split_name to_identifier $schema2;
+
+                my %monikers;
+                $monikers{'1.4'} = $schema1_moniker . 'OracleLoaderTest4';
+                $monikers{'1.5'} = $schema1_moniker . 'OracleLoaderTest5';
+                $monikers{'2.5'} = $schema2_moniker . 'OracleLoaderTest5';
+                $monikers{'2.6'} = $schema2_moniker . 'OracleLoaderTest6';
+                $monikers{'2.7'} = $schema2_moniker . 'OracleLoaderTest7';
+                $monikers{'1.8'} = $schema1_moniker . 'OracleLoaderTest8';
 
                 foreach my $db_schema ([$schema1, $schema2], '%') {
                     lives_and {
@@ -240,7 +297,7 @@ EOF
                             {
                                 naming => 'current',
                                 db_schema => $db_schema,
-                                preserve_case => 1,
+                                moniker_parts => [qw/schema name/],
                                 dump_directory => EXTRA_DUMP_DIR,
                                 quiet => 1,
                             },
@@ -252,29 +309,127 @@ EOF
                         is @warns, 0;
                     } qq{dumped schema for "$schema1" and "$schema2" schemas with no warnings};
 
-                    my $test_schema;
+                    my ($test_schema, $rsrc, $rs, $row, %uniqs, $rel_info);
 
                     lives_and {
                         ok $test_schema = OracleMultiSchema->connect($dsn, $user, $password);
                     } 'connected test schema';
 
                     lives_and {
-                        ok $test_schema->source('OracleLoaderTest6')
+                        ok $rsrc = $test_schema->source($monikers{'1.4'});
+                    } 'got source for table in schema1';
+
+                    is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
+                        'column in schema1';
+
+                    is try { $rsrc->column_info('value')->{data_type} }, 'varchar2',
+                        'column in schema1';
+
+                    is try { $rsrc->column_info('value')->{size} }, 100,
+                        'column in schema1';
+
+                    lives_and {
+                        ok $rs = $test_schema->resultset($monikers{'1.4'});
+                    } 'got resultset for table in schema1';
+
+                    lives_and {
+                        ok $row = $rs->create({ value => 'foo' });
+                    } 'executed SQL on table in schema1';
+
+                    my $schema1_identifier = join '_', map lc, split_name to_identifier $schema1;
+
+                    $rel_info = try { $rsrc->relationship_info(
+                        $schema1_identifier . '_oracle_loader_test5'
+                    ) };
+
+                    is_deeply $rel_info->{cond}, {
+                        'foreign.four_id' => 'self.id'
+                    }, 'relationship in schema1';
+
+                    is $rel_info->{attrs}{accessor}, 'single',
+                        'relationship in schema1';
+
+                    is $rel_info->{attrs}{join_type}, 'LEFT',
+                        'relationship in schema1';
+
+                    lives_and {
+                        ok $rsrc = $test_schema->source($monikers{'1.5'});
+                    } 'got source for table in schema1';
+
+                    %uniqs = try { $rsrc->unique_constraints };
+
+                    is keys %uniqs, 2,
+                        'got unique and primary constraint in schema1';
+
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['four_id'],
+                        'correct unique constraint in schema1');
+
+                    lives_and {
+                        ok $rsrc = $test_schema->source($monikers{'2.6'});
+                    } 'got source for table in schema2';
+
+                    is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
+                        'column in schema2 introspected correctly';
+
+                    is try { $rsrc->column_info('value')->{data_type} }, 'varchar2',
+                        'column in schema2 introspected correctly';
+
+                    is try { $rsrc->column_info('value')->{size} }, 100,
+                        'column in schema2 introspected correctly';
+
+                    lives_and {
+                        ok $rs = $test_schema->resultset($monikers{'2.6'});
+                    } 'got resultset for table in schema2';
+
+                    lives_and {
+                        ok $row = $rs->create({ value => 'foo' });
+                    } 'executed SQL on table in schema2';
+
+                    $rel_info = try { $rsrc->relationship_info('oracle_loader_test7') };
+
+                    is_deeply $rel_info->{cond}, {
+                        'foreign.six_id' => 'self.id'
+                    }, 'relationship in schema2';
+
+                    is $rel_info->{attrs}{accessor}, 'single',
+                        'relationship in schema2';
+
+                    is $rel_info->{attrs}{join_type}, 'LEFT',
+                        'relationship in schema2';
+
+                    lives_and {
+                        ok $rsrc = $test_schema->source($monikers{'2.7'});
+                    } 'got source for table in schema2';
+
+                    %uniqs = try { $rsrc->unique_constraints };
+
+                    is keys %uniqs, 2,
+                        'got unique and primary constraint in schema2';
+
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['six_id'],
+                        'correct unique constraint in schema2');
+
+                    lives_and {
+                        ok $test_schema->source($monikers{'2.6'})
                             ->has_relationship('oracle_loader_test4');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('OracleLoaderTest4')
+                        ok $test_schema->source($monikers{'1.4'})
                             ->has_relationship('oracle_loader_test6s');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('OracleLoaderTest8')
+                        ok $test_schema->source($monikers{'1.8'})
                             ->has_relationship('oracle_loader_test7');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('OracleLoaderTest7')
+                        ok $test_schema->source($monikers{'2.7'})
                             ->has_relationship('oracle_loader_test8s');
                     } 'cross-schema relationship in multi-db_schema';
                 }
@@ -296,10 +451,24 @@ END {
             my $dbh1 = $schema->storage->dbh;
 
             try {
-                $dbh2->do('DROP TABLE oracle_loader_test6');
-                $dbh1->do('DROP TABLE oracle_loader_test4');
+                $dbh1->do($_) for $auto_inc_drop_cb->('oracle_loader_test8', 'id');
+                $dbh2->do($_) for $auto_inc_drop_cb->('oracle_loader_test7', 'id');
+                $dbh2->do($_) for $auto_inc_drop_cb->('oracle_loader_test6', 'id');
+                $dbh2->do($_) for $auto_inc_drop_cb->('oracle_loader_test5', 'pk');
+                $dbh1->do($_) for $auto_inc_drop_cb->('oracle_loader_test5', 'id');
+                $dbh1->do($_) for $auto_inc_drop_cb->('oracle_loader_test4', 'id');
+            }
+            catch {
+                die "Error dropping sequences for cross-schema test tables: $_";
+            };
+
+            try {
                 $dbh1->do('DROP TABLE oracle_loader_test8');
                 $dbh2->do('DROP TABLE oracle_loader_test7');
+                $dbh2->do('DROP TABLE oracle_loader_test6');
+                $dbh2->do('DROP TABLE oracle_loader_test5');
+                $dbh1->do('DROP TABLE oracle_loader_test5');
+                $dbh1->do('DROP TABLE oracle_loader_test4');
             }
             catch {
                 die "Error dropping cross-schema test tables: $_";
index bf7ed8f..f5f7868 100644 (file)
@@ -5,6 +5,7 @@ use Test::Exception;
 use Try::Tiny;
 use File::Path 'rmtree';
 use DBIx::Class::Schema::Loader 'make_schema_at';
+use namespace::clean;
 use DBI ();
 
 use lib qw(t/lib);
@@ -105,7 +106,7 @@ my $tester = dbixcsl_common_tests->new(
             },
         ],
         drop => [ qw/sybase_loader_test1 sybase_loader_test2/ ],
-        count => 28 * 4,
+        count => 30 * 4,
         run => sub {
             $schema = shift;
 
@@ -116,7 +117,7 @@ my $tester = dbixcsl_common_tests->new(
                     $dbh->do('USE master');
                 }
                 catch {
-                    skip "these tests require the sysadmin role", 28 * 4;
+                    skip "these tests require the sysadmin role", 30 * 4;
                 };
 
                 try {
@@ -124,7 +125,7 @@ my $tester = dbixcsl_common_tests->new(
                     $dbh->do('CREATE DATABASE [dbicsl_test2]');
                 }
                 catch {
-                    skip "cannot create databases: $_", 28 * 4;
+                    skip "cannot create databases: $_", 30 * 4;
                 };
 
                 try {
@@ -150,7 +151,7 @@ my $tester = dbixcsl_common_tests->new(
                     $dbh->do("GRANT ALL TO dbicsl_user1");
                 }
                 catch {
-                    skip "cannot add logins: $_", 28 * 4;
+                    skip "cannot add logins: $_", 30 * 4;
                 };
 
                 my ($dbh1, $dbh2);
@@ -184,11 +185,21 @@ EOF
                     CREATE TABLE sybase_loader_test5 (
                         id INT IDENTITY PRIMARY KEY,
                         value VARCHAR(100) NULL,
-                        four_id INTEGER UNIQUE,
+                        four_id INTEGER,
+                        CONSTRAINT loader_test5_uniq UNIQUE (four_id),
                         FOREIGN KEY (four_id) REFERENCES sybase_loader_test4 (id)
                     )
 EOF
                 $dbh2->do(<<"EOF");
+                    CREATE TABLE sybase_loader_test5 (
+                        pk INT IDENTITY PRIMARY KEY,
+                        value VARCHAR(100) NULL,
+                        four_id INTEGER,
+                        CONSTRAINT loader_test5_uniq UNIQUE (four_id),
+                        FOREIGN KEY (four_id) REFERENCES [dbicsl_test1].dbicsl_user1.sybase_loader_test4 (id)
+                    )
+EOF
+                $dbh2->do(<<"EOF");
                     CREATE TABLE sybase_loader_test6 (
                         id INT IDENTITY PRIMARY KEY,
                         value VARCHAR(100) NULL,
@@ -242,6 +253,7 @@ EOF
                                 {
                                     naming => 'current',
                                     db_schema => $db_schema,
+                                    moniker_parts => [qw/database name/],
                                     dump_directory => EXTRA_DUMP_DIR,
                                     quiet => 1,
                                 },
@@ -260,7 +272,7 @@ EOF
                         } 'connected test schema';
 
                         lives_and {
-                            ok $rsrc = $test_schema->source('SybaseLoaderTest4');
+                            ok $rsrc = $test_schema->source('DbicslTest1SybaseLoaderTest4');
                         } 'got source for table in database one';
 
                         is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -273,14 +285,14 @@ EOF
                             'column in database one';
 
                         lives_and {
-                            ok $rs = $test_schema->resultset('SybaseLoaderTest4');
+                            ok $rs = $test_schema->resultset('DbicslTest1SybaseLoaderTest4');
                         } 'got resultset for table in database one';
 
                         lives_and {
                             ok $row = $rs->create({ value => 'foo' });
                         } 'executed SQL on table in database one';
 
-                        $rel_info = try { $rsrc->relationship_info('sybase_loader_test5') };
+                        $rel_info = try { $rsrc->relationship_info('dbicsl_test1_sybase_loader_test5') };
 
                         is_deeply $rel_info->{cond}, {
                             'foreign.four_id' => 'self.id'
@@ -293,7 +305,7 @@ EOF
                             'relationship in database one';
 
                         lives_and {
-                            ok $rsrc = $test_schema->source('SybaseLoaderTest5');
+                            ok $rsrc = $test_schema->source('DbicslTest1SybaseLoaderTest5');
                         } 'got source for table in database one';
 
                         %uniqs = try { $rsrc->unique_constraints };
@@ -301,8 +313,13 @@ EOF
                         is keys %uniqs, 2,
                             'got unique and primary constraint in database one';
 
+                        delete $uniqs{primary};
+
+                        is_deeply ((values %uniqs)[0], ['four_id'],
+                            'correct unique constraint in database one');
+
                         lives_and {
-                            ok $rsrc = $test_schema->source('SybaseLoaderTest6');
+                            ok $rsrc = $test_schema->source('DbicslTest2SybaseLoaderTest6');
                         } 'got source for table in database two';
 
                         is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -315,7 +332,7 @@ EOF
                             'column in database two introspected correctly';
 
                         lives_and {
-                            ok $rs = $test_schema->resultset('SybaseLoaderTest6');
+                            ok $rs = $test_schema->resultset('DbicslTest2SybaseLoaderTest6');
                         } 'got resultset for table in database two';
 
                         lives_and {
@@ -335,7 +352,7 @@ EOF
                             'relationship in database two';
 
                         lives_and {
-                            ok $rsrc = $test_schema->source('SybaseLoaderTest7');
+                            ok $rsrc = $test_schema->source('DbicslTest2SybaseLoaderTest7');
                         } 'got source for table in database two';
 
                         %uniqs = try { $rsrc->unique_constraints };
@@ -343,23 +360,28 @@ EOF
                         is keys %uniqs, 2,
                             'got unique and primary constraint in database two';
 
+                        delete $uniqs{primary};
+
+                        is_deeply ((values %uniqs)[0], ['six_id'],
+                            'correct unique constraint in database two');
+
                         lives_and {
-                            ok $test_schema->source('SybaseLoaderTest6')
+                            ok $test_schema->source('DbicslTest2SybaseLoaderTest6')
                                 ->has_relationship('sybase_loader_test4');
                         } 'cross-database relationship in multi database schema';
 
                         lives_and {
-                            ok $test_schema->source('SybaseLoaderTest4')
+                            ok $test_schema->source('DbicslTest1SybaseLoaderTest4')
                                 ->has_relationship('sybase_loader_test6s');
                         } 'cross-database relationship in multi database schema';
 
                         lives_and {
-                            ok $test_schema->source('SybaseLoaderTest8')
+                            ok $test_schema->source('DbicslTest1SybaseLoaderTest8')
                                 ->has_relationship('sybase_loader_test7');
                         } 'cross-database relationship in multi database schema';
 
                         lives_and {
-                            ok $test_schema->source('SybaseLoaderTest7')
+                            ok $test_schema->source('DbicslTest2SybaseLoaderTest7')
                                 ->has_relationship('sybase_loader_test8s');
                         } 'cross-database relationship in multi database schema';
                     }
@@ -399,6 +421,7 @@ END {
             foreach my $table ('[dbicsl_test1].dbicsl_user1.sybase_loader_test8',
                                '[dbicsl_test2].dbicsl_user2.sybase_loader_test7',
                                '[dbicsl_test2].dbicsl_user2.sybase_loader_test6',
+                               '[dbicsl_test2].dbicsl_user2.sybase_loader_test5',
                                '[dbicsl_test1].dbicsl_user1.sybase_loader_test5',
                                '[dbicsl_test1].dbicsl_user1.sybase_loader_test4') {
                 try {
index df6ba37..ccdc8ff 100644 (file)
@@ -7,6 +7,7 @@ use Try::Tiny;
 use File::Path 'rmtree';
 use DBIx::Class::Schema::Loader 'make_schema_at';
 use namespace::clean;
+use Scope::Guard ();
 
 # use this if you keep a copy of DBD::Sybase linked to FreeTDS somewhere else
 BEGIN {
@@ -22,8 +23,8 @@ use dbixcsl_test_dir '$tdir';
 
 use constant EXTRA_DUMP_DIR => "$tdir/mssql_extra_dump";
 
-# for cleanup in END for extra tests
-my ($schema, $schemas_created, $databases_created);
+# for extra tests cleanup
+my $schema;
 
 my ($dsns, $common_version);
 
@@ -210,7 +211,7 @@ my $tester = dbixcsl_common_tests->new(
             'MSSQL_Loader_Test6',
             'MSSQL_Loader_Test5',
         ],
-        count  => 10 + 28 * 2 + 24,
+        count  => 10 + 30 * 2 + 26 * 2, # extra + multi-schema + mutli-db
         run    => sub {
             my ($monikers, $classes, $self);
             ($schema, $monikers, $classes, $self) = @_;
@@ -289,11 +290,10 @@ my $tester = dbixcsl_common_tests->new(
                 my $dbh = $schema->storage->dbh;
 
                 try {
-                    $dbh->do('CREATE SCHEMA "dbicsl-test"');
+                    $dbh->do('CREATE SCHEMA [dbicsl-test]');
                 }
                 catch {
-                    $schemas_created = 0;
-                    skip "no CREATE SCHEMA privileges", 28 * 2;
+                    skip "no CREATE SCHEMA privileges", 30 * 2;
                 };
 
                 $dbh->do(<<"EOF");
@@ -306,12 +306,22 @@ EOF
                     CREATE TABLE [dbicsl-test].mssql_loader_test9 (
                         id INT IDENTITY PRIMARY KEY,
                         value VARCHAR(100),
-                        eight_id INTEGER NOT NULL UNIQUE,
+                        eight_id INTEGER NOT NULL,
+                        CONSTRAINT loader_test9_uniq UNIQUE (eight_id),
                         FOREIGN KEY (eight_id) REFERENCES [dbicsl-test].mssql_loader_test8 (id)
                     )
 EOF
                 $dbh->do('CREATE SCHEMA [dbicsl.test]');
                 $dbh->do(<<"EOF");
+                    CREATE TABLE [dbicsl.test].mssql_loader_test9 (
+                        pk INT IDENTITY PRIMARY KEY,
+                        value VARCHAR(100),
+                        eight_id INTEGER NOT NULL,
+                        CONSTRAINT loader_test9_uniq UNIQUE (eight_id),
+                        FOREIGN KEY (eight_id) REFERENCES [dbicsl-test].mssql_loader_test8 (id)
+                    )
+EOF
+                $dbh->do(<<"EOF");
                     CREATE TABLE [dbicsl.test].mssql_loader_test10 (
                         id INT IDENTITY PRIMARY KEY,
                         value VARCHAR(100),
@@ -336,7 +346,7 @@ EOF
                     )
 EOF
 
-                $schemas_created = 1;
+                my $guard = Scope::Guard->new(\&cleanup_schemas);
 
                 foreach my $db_schema (['dbicsl-test', 'dbicsl.test'], '%') {
                     lives_and {
@@ -352,6 +362,7 @@ EOF
                             {
                                 naming => 'current',
                                 db_schema => $db_schema,
+                                moniker_parts => [qw/schema name/],
                                 dump_directory => EXTRA_DUMP_DIR,
                                 quiet => 1,
                             },
@@ -370,7 +381,7 @@ EOF
                     } 'connected test schema';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('MssqlLoaderTest8');
+                        ok $rsrc = $test_schema->source('DbicslDashTestMssqlLoaderTest8');
                     } 'got source for table in schema name with dash';
 
                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -383,14 +394,14 @@ EOF
                         'column in schema name with dash';
 
                     lives_and {
-                        ok $rs = $test_schema->resultset('MssqlLoaderTest8');
+                        ok $rs = $test_schema->resultset('DbicslDashTestMssqlLoaderTest8');
                     } 'got resultset for table in schema name with dash';
 
                     lives_and {
                         ok $row = $rs->create({ value => 'foo' });
                     } 'executed SQL on table in schema name with dash';
 
-                    $rel_info = try { $rsrc->relationship_info('mssql_loader_test9') };
+                    $rel_info = try { $rsrc->relationship_info('dbicsl_dash_test_mssql_loader_test9') };
 
                     is_deeply $rel_info->{cond}, {
                         'foreign.eight_id' => 'self.id'
@@ -403,7 +414,7 @@ EOF
                         'relationship in schema name with dash';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('MssqlLoaderTest9');
+                        ok $rsrc = $test_schema->source('DbicslDashTestMssqlLoaderTest9');
                     } 'got source for table in schema name with dash';
 
                     %uniqs = try { $rsrc->unique_constraints };
@@ -411,8 +422,13 @@ EOF
                     is keys %uniqs, 2,
                         'got unique and primary constraint in schema name with dash';
 
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['eight_id'],
+                        'correct unique constraint in schema name with dash');
+
                     lives_and {
-                        ok $rsrc = $test_schema->source('MssqlLoaderTest10');
+                        ok $rsrc = $test_schema->source('DbicslDotTestMssqlLoaderTest10');
                     } 'got source for table in schema name with dot';
 
                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -425,7 +441,7 @@ EOF
                         'column in schema name with dot introspected correctly';
 
                     lives_and {
-                        ok $rs = $test_schema->resultset('MssqlLoaderTest10');
+                        ok $rs = $test_schema->resultset('DbicslDotTestMssqlLoaderTest10');
                     } 'got resultset for table in schema name with dot';
 
                     lives_and {
@@ -445,7 +461,7 @@ EOF
                         'relationship in schema name with dot';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('MssqlLoaderTest11');
+                        ok $rsrc = $test_schema->source('DbicslDotTestMssqlLoaderTest11');
                     } 'got source for table in schema name with dot';
 
                     %uniqs = try { $rsrc->unique_constraints };
@@ -453,23 +469,28 @@ EOF
                     is keys %uniqs, 2,
                         'got unique and primary constraint in schema name with dot';
 
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['ten_id'],
+                        'correct unique constraint in schema name with dot');
+
                     lives_and {
-                        ok $test_schema->source('MssqlLoaderTest10')
+                        ok $test_schema->source('DbicslDotTestMssqlLoaderTest10')
                             ->has_relationship('mssql_loader_test8');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('MssqlLoaderTest8')
+                        ok $test_schema->source('DbicslDashTestMssqlLoaderTest8')
                             ->has_relationship('mssql_loader_test10s');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('MssqlLoaderTest12')
+                        ok $test_schema->source('DbicslDashTestMssqlLoaderTest12')
                             ->has_relationship('mssql_loader_test11');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('MssqlLoaderTest11')
+                        ok $test_schema->source('DbicslDotTestMssqlLoaderTest11')
                             ->has_relationship('mssql_loader_test12s');
                     } 'cross-schema relationship in multi-db_schema';
                 }
@@ -483,7 +504,7 @@ EOF
                     $dbh->do('CREATE DATABASE dbicsl_test1');
                 }
                 catch {
-                    skip "no CREATE DATABASE privileges", 24;
+                    skip "no CREATE DATABASE privileges", 26 * 2;
                 };
 
                 $dbh->do('CREATE DATABASE dbicsl_test2');
@@ -500,13 +521,22 @@ EOF
                     CREATE TABLE mssql_loader_test14 (
                         id INT IDENTITY PRIMARY KEY,
                         value VARCHAR(100),
-                        thirteen_id INTEGER UNIQUE REFERENCES mssql_loader_test13 (id)
+                        thirteen_id INTEGER REFERENCES mssql_loader_test13 (id),
+                        CONSTRAINT loader_test14_uniq UNIQUE (thirteen_id)
                     )
 EOF
 
-                $dbh->do('USE master');
                 $dbh->do('USE dbicsl_test2');
 
+                $dbh->do(<<'EOF');
+                    CREATE TABLE mssql_loader_test14 (
+                        pk INT IDENTITY PRIMARY KEY,
+                        value VARCHAR(100),
+                        thirteen_id INTEGER,
+                        CONSTRAINT loader_test14_uniq UNIQUE (thirteen_id)
+                    )
+EOF
+
                 $dbh->do(<<"EOF");
                     CREATE TABLE mssql_loader_test15 (
                         id INT IDENTITY PRIMARY KEY,
@@ -521,121 +551,134 @@ EOF
                     )
 EOF
 
-                $databases_created = 1;
+                my $guard = Scope::Guard->new(\&cleanup_databases);
 
-                lives_and {
-                    my @warns;
-                    local $SIG{__WARN__} = sub {
-                        push @warns, $_[0] unless $_[0] =~ /\bcollides\b/;
-                    };
-                    make_schema_at(
-                        'MSSQLMultiDatabase',
-                        {
-                            naming => 'current',
-                            db_schema => { '%' => '%' },
-                            dump_directory => EXTRA_DUMP_DIR,
-                            quiet => 1,
-                        },
-                        $connect_info,
-                    );
+                foreach my $db_schema ({ dbicsl_test1 => '%', dbicsl_test2 => '%' }, { '%' => '%' }) {
+                    lives_and {
+                        my @warns;
+                        local $SIG{__WARN__} = sub {
+                            push @warns, $_[0] unless $_[0] =~ /\bcollides\b/;
+                        };
+     
+                        make_schema_at(
+                            'MSSQLMultiDatabase',
+                            {
+                                naming => 'current',
+                                db_schema => $db_schema,
+                                moniker_parts => [qw/database name/],
+                                dump_directory => EXTRA_DUMP_DIR,
+                                quiet => 1,
+                            },
+                            $connect_info,
+                        );
 
-                    diag join "\n", @warns if @warns;
+                        diag join "\n", @warns if @warns;
 
-                    is @warns, 0;
-                } 'dumped schema for all databases with no warnings';
+                        is @warns, 0;
+                    } "dumped schema for databases 'dbicsl_test1' and 'dbicsl_test2' with no warnings";
 
-                my $test_schema;
+                    my $test_schema;
 
-                lives_and {
-                    ok $test_schema = MSSQLMultiDatabase->connect(@$connect_info);
-                } 'connected test schema';
+                    lives_and {
+                        ok $test_schema = MSSQLMultiDatabase->connect(@$connect_info);
+                    } 'connected test schema';
 
-                my ($rsrc, $rs, $row, $rel_info, %uniqs);
+                    my ($rsrc, $rs, $row, $rel_info, %uniqs);
 
-                lives_and {
-                    ok $rsrc = $test_schema->source('MssqlLoaderTest13');
-                } 'got source for table in database one';
+                    lives_and {
+                        ok $rsrc = $test_schema->source('DbicslTest1MssqlLoaderTest13');
+                    } 'got source for table in database one';
 
-                is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
-                    'column in database one';
+                    is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
+                        'column in database one';
 
-                is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
-                    'column in database one';
+                    is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
+                        'column in database one';
 
-                is try { $rsrc->column_info('value')->{size} }, 100,
-                    'column in database one';
+                    is try { $rsrc->column_info('value')->{size} }, 100,
+                        'column in database one';
 
-                lives_and {
-                    ok $rs = $test_schema->resultset('MssqlLoaderTest13');
-                } 'got resultset for table in database one';
+                    lives_and {
+                        ok $rs = $test_schema->resultset('DbicslTest1MssqlLoaderTest13');
+                    } 'got resultset for table in database one';
 
-                lives_and {
-                    ok $row = $rs->create({ value => 'foo' });
-                } 'executed SQL on table in database one';
+                    lives_and {
+                        ok $row = $rs->create({ value => 'foo' });
+                    } 'executed SQL on table in database one';
 
-                $rel_info = try { $rsrc->relationship_info('mssql_loader_test14') };
+                    $rel_info = try { $rsrc->relationship_info('mssql_loader_test14') };
 
-                is_deeply $rel_info->{cond}, {
-                    'foreign.thirteen_id' => 'self.id'
-                }, 'relationship in database one';
+                    is_deeply $rel_info->{cond}, {
+                        'foreign.thirteen_id' => 'self.id'
+                    }, 'relationship in database one';
 
-                is $rel_info->{attrs}{accessor}, 'single',
-                    'relationship in database one';
+                    is $rel_info->{attrs}{accessor}, 'single',
+                        'relationship in database one';
 
-                is $rel_info->{attrs}{join_type}, 'LEFT',
-                    'relationship in database one';
+                    is $rel_info->{attrs}{join_type}, 'LEFT',
+                        'relationship in database one';
 
-                lives_and {
-                    ok $rsrc = $test_schema->source('MssqlLoaderTest14');
-                } 'got source for table in database one';
+                    lives_and {
+                        ok $rsrc = $test_schema->source('DbicslTest1MssqlLoaderTest14');
+                    } 'got source for table in database one';
 
-                %uniqs = try { $rsrc->unique_constraints };
+                    %uniqs = try { $rsrc->unique_constraints };
 
-                is keys %uniqs, 2,
-                    'got unique and primary constraint in database one';
+                    is keys %uniqs, 2,
+                        'got unique and primary constraint in database one';
 
-                lives_and {
-                    ok $rsrc = $test_schema->source('MssqlLoaderTest15');
-                } 'got source for table in database two';
+                    delete $uniqs{primary};
 
-                is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
-                    'column in database two introspected correctly';
+                    is_deeply ((values %uniqs)[0], ['thirteen_id'],
+                        'correct unique constraint in database one');
 
-                is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
-                    'column in database two introspected correctly';
+                    lives_and {
+                        ok $rsrc = $test_schema->source('DbicslTest2MssqlLoaderTest15');
+                    } 'got source for table in database two';
 
-                is try { $rsrc->column_info('value')->{size} }, 100,
-                    'column in database two introspected correctly';
+                    is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
+                        'column in database two introspected correctly';
 
-                lives_and {
-                    ok $rs = $test_schema->resultset('MssqlLoaderTest15');
-                } 'got resultset for table in database two';
+                    is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
+                        'column in database two introspected correctly';
 
-                lives_and {
-                    ok $row = $rs->create({ value => 'foo' });
-                } 'executed SQL on table in database two';
+                    is try { $rsrc->column_info('value')->{size} }, 100,
+                        'column in database two introspected correctly';
 
-                $rel_info = try { $rsrc->relationship_info('mssql_loader_test16') };
+                    lives_and {
+                        ok $rs = $test_schema->resultset('DbicslTest2MssqlLoaderTest15');
+                    } 'got resultset for table in database two';
 
-                is_deeply $rel_info->{cond}, {
-                    'foreign.fifteen_id' => 'self.id'
-                }, 'relationship in database two';
+                    lives_and {
+                        ok $row = $rs->create({ value => 'foo' });
+                    } 'executed SQL on table in database two';
 
-                is $rel_info->{attrs}{accessor}, 'single',
-                    'relationship in database two';
+                    $rel_info = try { $rsrc->relationship_info('mssql_loader_test16') };
 
-                is $rel_info->{attrs}{join_type}, 'LEFT',
-                    'relationship in database two';
+                    is_deeply $rel_info->{cond}, {
+                        'foreign.fifteen_id' => 'self.id'
+                    }, 'relationship in database two';
 
-                lives_and {
-                    ok $rsrc = $test_schema->source('MssqlLoaderTest16');
-                } 'got source for table in database two';
+                    is $rel_info->{attrs}{accessor}, 'single',
+                        'relationship in database two';
+
+                    is $rel_info->{attrs}{join_type}, 'LEFT',
+                        'relationship in database two';
+
+                    lives_and {
+                        ok $rsrc = $test_schema->source('DbicslTest2MssqlLoaderTest16');
+                    } 'got source for table in database two';
 
-                %uniqs = try { $rsrc->unique_constraints };
+                    %uniqs = try { $rsrc->unique_constraints };
+
+                    is keys %uniqs, 2,
+                        'got unique and primary constraint in database two';
 
-                is keys %uniqs, 2,
-                    'got unique and primary constraint in database two';
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['fifteen_id'],
+                        'correct unique constraint in database two');
+                }
             }
         },
     },
@@ -643,76 +686,80 @@ EOF
 
 $tester->run_tests();
 
-END {
-    if (not $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP}) {
-        if ($schema) {
-            # switch back to default database
-            $schema->storage->disconnect;
-            my $dbh = $schema->storage->dbh;
+sub cleanup_schemas {
+    return if $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP};
+
+    # switch back to default database
+    $schema->storage->disconnect;
+    my $dbh = $schema->storage->dbh;
+
+    foreach my $table ('[dbicsl-test].mssql_loader_test12',
+                       '[dbicsl.test].mssql_loader_test11',
+                       '[dbicsl.test].mssql_loader_test10',
+                       '[dbicsl.test].mssql_loader_test9',
+                       '[dbicsl-test].mssql_loader_test9',
+                       '[dbicsl-test].mssql_loader_test8') {
+        try {
+            $dbh->do("DROP TABLE $table");
+        }
+        catch {
+            diag "Error dropping table: $_";
+        };
+    }
 
-            if ($schemas_created) {
-                foreach my $table ('[dbicsl-test].mssql_loader_test12',
-                                   '[dbicsl.test].mssql_loader_test11',
-                                   '[dbicsl.test].mssql_loader_test10',
-                                   '[dbicsl-test].mssql_loader_test9',
-                                   '[dbicsl-test].mssql_loader_test8') {
-                    try {
-                        $dbh->do("DROP TABLE $table");
-                    }
-                    catch {
-                        diag "Error dropping table: $_";
-                    };
-                }
+    foreach my $db_schema (qw/dbicsl-test dbicsl.test/) {
+        try {
+            $dbh->do(qq{DROP SCHEMA [$db_schema]});
+        }
+        catch {
+            diag "Error dropping test schema $db_schema: $_";
+        };
+    }
 
-                foreach my $db_schema (qw/dbicsl-test dbicsl.test/) {
-                    try {
-                        $dbh->do(qq{DROP SCHEMA [$db_schema]});
-                    }
-                    catch {
-                        diag "Error dropping test schema $db_schema: $_";
-                    };
-                }
-            }
+    rmtree EXTRA_DUMP_DIR;
+}
 
-            if ($databases_created) {
-                $dbh->do('USE dbicsl_test1');
+sub cleanup_databases {
+    return if $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP};
 
-                foreach my $table ('mssql_loader_test14',
-                                   'mssql_loader_test13') {
-                    try {
-                        $dbh->do("DROP TABLE $table");
-                    }
-                    catch {
-                        diag "Error dropping table: $_";
-                    };
-                }
+    my $dbh = $schema->storage->dbh;
 
-                $dbh->do('USE dbicsl_test2');
+    $dbh->do('USE dbicsl_test1');
 
-                foreach my $table ('mssql_loader_test16',
-                                   'mssql_loader_test15') {
-                    try {
-                        $dbh->do("DROP TABLE $table");
-                    }
-                    catch {
-                        diag "Error dropping table: $_";
-                    };
-                }
+    foreach my $table ('mssql_loader_test14',
+                       'mssql_loader_test13') {
+        try {
+            $dbh->do("DROP TABLE $table");
+        }
+        catch {
+            diag "Error dropping table: $_";
+        };
+    }
 
-                $dbh->do('USE master');
+    $dbh->do('USE dbicsl_test2');
 
-                foreach my $database (qw/dbicsl_test1 dbicsl_test2/) {
-                    try {
-                        $dbh->do(qq{DROP DATABASE $database});
-                    }
-                    catch {
-                        diag "Error dropping test database '$database': $_";
-                    };
-                }
-            }
+    foreach my $table ('mssql_loader_test16',
+                       'mssql_loader_test15',
+                       'mssql_loader_test14') {
+        try {
+            $dbh->do("DROP TABLE $table");
+        }
+        catch {
+            diag "Error dropping table: $_";
+        };
+    }
 
-            rmtree EXTRA_DUMP_DIR;
+    $dbh->do('USE master');
+
+    foreach my $database (qw/dbicsl_test1 dbicsl_test2/) {
+        try {
+            $dbh->do(qq{DROP DATABASE $database});
         }
+        catch {
+            diag "Error dropping test database '$database': $_";
+        };
     }
+
+    rmtree EXTRA_DUMP_DIR;
 }
 # vim:et sts=4 sw=4 tw=0:
index f52bc4e..d5353f8 100644 (file)
@@ -144,7 +144,7 @@ my $tester = dbixcsl_common_tests->new(
         'ntext'        => { data_type => 'ntext' },
     },
     extra => {
-        count => 28 * 2,
+        count => 30 * 2,
         run => sub {
             SKIP: {
                 $schema  = $_[0];
@@ -159,7 +159,7 @@ my $tester = dbixcsl_common_tests->new(
                 }
                 catch {
                     $schemas_created = 0;
-                    skip "no CREATE USER privileges", 28 * 2;
+                    skip "no CREATE USER privileges", 30 * 2;
                 };
 
                 $dbh->do(<<"EOF");
@@ -172,12 +172,22 @@ EOF
                     CREATE TABLE dbicsl_test1.sqlanywhere_loader_test5 (
                         id INT IDENTITY NOT NULL PRIMARY KEY,
                         value VARCHAR(100),
-                        four_id INTEGER NOT NULL UNIQUE,
+                        four_id INTEGER NOT NULL,
+                        CONSTRAINT loader_test5_uniq UNIQUE (four_id),
                         FOREIGN KEY (four_id) REFERENCES dbicsl_test1.sqlanywhere_loader_test4 (id)
                     )
 EOF
                 $dbh->do("CREATE USER dbicsl_test2 identified by 'dbicsl'");
                 $dbh->do(<<"EOF");
+                    CREATE TABLE dbicsl_test2.sqlanywhere_loader_test5 (
+                        pk INT IDENTITY NOT NULL PRIMARY KEY,
+                        value VARCHAR(100),
+                        four_id INTEGER NOT NULL,
+                        CONSTRAINT loader_test5_uniq UNIQUE (four_id),
+                        FOREIGN KEY (four_id) REFERENCES dbicsl_test1.sqlanywhere_loader_test4 (id)
+                    )
+EOF
+                $dbh->do(<<"EOF");
                     CREATE TABLE dbicsl_test2.sqlanywhere_loader_test6 (
                         id INT IDENTITY NOT NULL PRIMARY KEY,
                         value VARCHAR(100),
@@ -220,6 +230,7 @@ EOF
                             {
                                 naming => 'current',
                                 db_schema => $db_schema,
+                                moniker_parts => [qw/schema name/],
                                 dump_directory => EXTRA_DUMP_DIR,
                                 quiet => 1,
                             },
@@ -238,7 +249,7 @@ EOF
                     } 'connected test schema';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('SqlanywhereLoaderTest4');
+                        ok $rsrc = $test_schema->source('DbicslTest1SqlanywhereLoaderTest4');
                     } 'got source for table in schema one';
 
                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -251,14 +262,14 @@ EOF
                         'column in schema one';
 
                     lives_and {
-                        ok $rs = $test_schema->resultset('SqlanywhereLoaderTest4');
+                        ok $rs = $test_schema->resultset('DbicslTest1SqlanywhereLoaderTest4');
                     } 'got resultset for table in schema one';
 
                     lives_and {
                         ok $row = $rs->create({ value => 'foo' });
                     } 'executed SQL on table in schema one';
 
-                    $rel_info = try { $rsrc->relationship_info('sqlanywhere_loader_test5') };
+                    $rel_info = try { $rsrc->relationship_info('dbicsl_test1_sqlanywhere_loader_test5') };
 
                     is_deeply $rel_info->{cond}, {
                         'foreign.four_id' => 'self.id'
@@ -271,7 +282,7 @@ EOF
                         'relationship in schema one';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('SqlanywhereLoaderTest5');
+                        ok $rsrc = $test_schema->source('DbicslTest1SqlanywhereLoaderTest5');
                     } 'got source for table in schema one';
 
                     %uniqs = try { $rsrc->unique_constraints };
@@ -279,8 +290,13 @@ EOF
                     is keys %uniqs, 2,
                         'got unique and primary constraint in schema one';
 
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['four_id'],
+                        'correct unique constraint in schema one');
+
                     lives_and {
-                        ok $rsrc = $test_schema->source('SqlanywhereLoaderTest6');
+                        ok $rsrc = $test_schema->source('DbicslTest2SqlanywhereLoaderTest6');
                     } 'got source for table in schema two';
 
                     is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
@@ -293,7 +309,7 @@ EOF
                         'column in schema two introspected correctly';
 
                     lives_and {
-                        ok $rs = $test_schema->resultset('SqlanywhereLoaderTest6');
+                        ok $rs = $test_schema->resultset('DbicslTest2SqlanywhereLoaderTest6');
                     } 'got resultset for table in schema two';
 
                     lives_and {
@@ -313,7 +329,7 @@ EOF
                         'relationship in schema two';
 
                     lives_and {
-                        ok $rsrc = $test_schema->source('SqlanywhereLoaderTest7');
+                        ok $rsrc = $test_schema->source('DbicslTest2SqlanywhereLoaderTest7');
                     } 'got source for table in schema two';
 
                     %uniqs = try { $rsrc->unique_constraints };
@@ -321,23 +337,28 @@ EOF
                     is keys %uniqs, 2,
                         'got unique and primary constraint in schema two';
 
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['six_id'],
+                        'correct unique constraint in schema two');
+
                     lives_and {
-                        ok $test_schema->source('SqlanywhereLoaderTest6')
+                        ok $test_schema->source('DbicslTest2SqlanywhereLoaderTest6')
                             ->has_relationship('sqlanywhere_loader_test4');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('SqlanywhereLoaderTest4')
+                        ok $test_schema->source('DbicslTest1SqlanywhereLoaderTest4')
                             ->has_relationship('sqlanywhere_loader_test6s');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('SqlanywhereLoaderTest8')
+                        ok $test_schema->source('DbicslTest1SqlanywhereLoaderTest8')
                             ->has_relationship('sqlanywhere_loader_test7');
                     } 'cross-schema relationship in multi-db_schema';
 
                     lives_and {
-                        ok $test_schema->source('SqlanywhereLoaderTest7')
+                        ok $test_schema->source('DbicslTest2SqlanywhereLoaderTest7')
                             ->has_relationship('sqlanywhere_loader_test8s');
                     } 'cross-schema relationship in multi-db_schema';
                 }
@@ -359,6 +380,7 @@ sub extra_cleanup {
             foreach my $table ('dbicsl_test1.sqlanywhere_loader_test8',
                                'dbicsl_test2.sqlanywhere_loader_test7',
                                'dbicsl_test2.sqlanywhere_loader_test6',
+                               'dbicsl_test2.sqlanywhere_loader_test5',
                                'dbicsl_test1.sqlanywhere_loader_test5',
                                'dbicsl_test1.sqlanywhere_loader_test4') {
                 try {
index f3d5d53..00832c3 100644 (file)
@@ -5,6 +5,9 @@ use Test::Exception;
 use Try::Tiny;
 use File::Path 'rmtree';
 use DBIx::Class::Schema::Loader 'make_schema_at';
+use DBIx::Class::Schema::Loader::Utils 'split_name';
+use String::ToIdentifier::EN::Unicode 'to_identifier';
+use namespace::clean;
 
 use lib qw(t/lib);
 
@@ -127,12 +130,12 @@ my $tester = dbixcsl_common_tests->new(
                            => { data_type => 'set' },
     },
     extra => {
-        count => 24,
+        count => 26 * 2,
         run   => sub {
             ($schema) = @_;
 
             SKIP: {
-                skip 'Set the DBICTEST_INFORMIX_EXTRADB_DSN, _USER and _PASS environment variables to run the multi-database tests', 24
+                skip 'Set the DBICTEST_INFORMIX_EXTRADB_DSN, _USER and _PASS environment variables to run the multi-database tests', 26 * 2
                     unless $ENV{DBICTEST_INFORMIX_EXTRADB_DSN};
 
                 $extra_schema = $schema->clone;
@@ -153,9 +156,22 @@ EOF
                     CREATE TABLE informix_loader_test5 (
                         id SERIAL PRIMARY KEY,
                         value VARCHAR(100),
-                        four_id INTEGER UNIQUE REFERENCES informix_loader_test4 (id)
+                        four_id INTEGER REFERENCES informix_loader_test4 (id)
                     )
 EOF
+                $dbh1->do(<<'EOF');
+ALTER TABLE informix_loader_test5 ADD CONSTRAINT UNIQUE (four_id) CONSTRAINT loader_test5_uniq
+EOF
+                $dbh2->do(<<'EOF');
+                    CREATE TABLE informix_loader_test5 (
+                        pk SERIAL PRIMARY KEY,
+                        value VARCHAR(100),
+                        four_id INTEGER
+                    )
+EOF
+                $dbh2->do(<<'EOF');
+ALTER TABLE informix_loader_test5 ADD CONSTRAINT UNIQUE (four_id) CONSTRAINT loader_test5_uniq
+EOF
                 $dbh2->do(<<"EOF");
                     CREATE TABLE informix_loader_test6 (
                         id SERIAL PRIMARY KEY,
@@ -169,120 +185,140 @@ EOF
                         six_id INTEGER UNIQUE REFERENCES informix_loader_test6 (id)
                     )
 EOF
-                lives_and {
-                    my @warns;
-                    local $SIG{__WARN__} = sub {
-                        push @warns, $_[0] unless $_[0] =~ /\bcollides\b/
-                            || $_[0] =~ /unreferencable/;
-                    };
-                    make_schema_at(
-                        'InformixMultiDatabase',
-                        {
-                            naming => 'current',
-                            db_schema => { '%' => '%' },
-                            dump_directory => EXTRA_DUMP_DIR,
-                            quiet => 1,
-                        },
-                        [ $dsn, $user, $password ],
-                    );
 
-                    diag join "\n", @warns if @warns;
+                my $db1 = db_name($schema);
+                my $db2 = db_name($extra_schema);
+
+                my $db1_moniker = join '', map ucfirst lc, split_name to_identifier $db1;
+                my $db2_moniker = join '', map ucfirst lc, split_name to_identifier $db2;
+
+                foreach my $db_schema ({ $db1 => '%', $db2 => '%' }, { '%' => '%' }) {
+                    lives_and {
+                        my @warns;
+                        local $SIG{__WARN__} = sub {
+                            push @warns, $_[0] unless $_[0] =~ /\bcollides\b/
+                                || $_[0] =~ /unreferencable/;
+                        };
+     
+                        make_schema_at(
+                            'InformixMultiDatabase',
+                            {
+                                naming => 'current',
+                                db_schema => $db_schema,
+                                moniker_parts => [qw/database name/],
+                                dump_directory => EXTRA_DUMP_DIR,
+                                quiet => 1,
+                            },
+                            [ $dsn, $user, $password ],
+                        );
 
-                    is @warns, 0;
-                } 'dumped schema for all databases with no warnings';
+                        diag join "\n", @warns if @warns;
 
-                my $test_schema;
+                        is @warns, 0;
+                    } "dumped schema for databases $db1 and $db2 with no warnings";
 
-                lives_and {
-                    ok $test_schema = InformixMultiDatabase->connect($dsn, $user, $password);
-                } 'connected test schema';
+                    my $test_schema;
 
-                my ($rsrc, $rs, $row, $rel_info, %uniqs);
+                    lives_and {
+                        ok $test_schema = InformixMultiDatabase->connect($dsn, $user, $password);
+                    } 'connected test schema';
 
-                lives_and {
-                    ok $rsrc = $test_schema->source('InformixLoaderTest4');
-                } 'got source for table in database one';
+                    my ($rsrc, $rs, $row, $rel_info, %uniqs);
 
-                is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
-                    'column in database one';
+                    lives_and {
+                        ok $rsrc = $test_schema->source("${db1_moniker}InformixLoaderTest4");
+                    } 'got source for table in database one';
 
-                is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
-                    'column in database one';
+                    is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
+                        'column in database one';
 
-                is try { $rsrc->column_info('value')->{size} }, 100,
-                    'column in database one';
+                    is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
+                        'column in database one';
 
-                lives_and {
-                    ok $rs = $test_schema->resultset('InformixLoaderTest4');
-                } 'got resultset for table in database one';
+                    is try { $rsrc->column_info('value')->{size} }, 100,
+                        'column in database one';
 
-                lives_and {
-                    ok $row = $rs->create({ value => 'foo' });
-                } 'executed SQL on table in database one';
+                    lives_and {
+                        ok $rs = $test_schema->resultset("${db1_moniker}InformixLoaderTest4");
+                    } 'got resultset for table in database one';
 
-                $rel_info = try { $rsrc->relationship_info('informix_loader_test5') };
+                    lives_and {
+                        ok $row = $rs->create({ value => 'foo' });
+                    } 'executed SQL on table in database one';
 
-                is_deeply $rel_info->{cond}, {
-                    'foreign.four_id' => 'self.id'
-                }, 'relationship in database one';
+                    $rel_info = try { $rsrc->relationship_info("informix_loader_test5") };
 
-                is $rel_info->{attrs}{accessor}, 'single',
-                    'relationship in database one';
+                    is_deeply $rel_info->{cond}, {
+                        'foreign.four_id' => 'self.id'
+                    }, 'relationship in database one';
 
-                is $rel_info->{attrs}{join_type}, 'LEFT',
-                    'relationship in database one';
+                    is $rel_info->{attrs}{accessor}, 'single',
+                        'relationship in database one';
 
-                lives_and {
-                    ok $rsrc = $test_schema->source('InformixLoaderTest5');
-                } 'got source for table in database one';
+                    is $rel_info->{attrs}{join_type}, 'LEFT',
+                        'relationship in database one';
 
-                %uniqs = try { $rsrc->unique_constraints };
+                    lives_and {
+                        ok $rsrc = $test_schema->source("${db1_moniker}InformixLoaderTest5");
+                    } 'got source for table in database one';
 
-                is keys %uniqs, 2,
-                    'got unique and primary constraint in database one';
+                    %uniqs = try { $rsrc->unique_constraints };
 
-                lives_and {
-                    ok $rsrc = $test_schema->source('InformixLoaderTest6');
-                } 'got source for table in database two';
+                    is keys %uniqs, 2,
+                        'got unique and primary constraint in database one';
 
-                is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
-                    'column in database two introspected correctly';
+                    delete $uniqs{primary};
 
-                is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
-                    'column in database two introspected correctly';
+                    is_deeply ((values %uniqs)[0], ['four_id'],
+                        'correct unique constraint in database one');
 
-                is try { $rsrc->column_info('value')->{size} }, 100,
-                    'column in database two introspected correctly';
+                    lives_and {
+                        ok $rsrc = $test_schema->source("${db2_moniker}InformixLoaderTest6");
+                    } 'got source for table in database two';
 
-                lives_and {
-                    ok $rs = $test_schema->resultset('InformixLoaderTest6');
-                } 'got resultset for table in database two';
+                    is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
+                        'column in database two introspected correctly';
 
-                lives_and {
-                    ok $row = $rs->create({ value => 'foo' });
-                } 'executed SQL on table in database two';
+                    is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
+                        'column in database two introspected correctly';
 
-                $rel_info = try { $rsrc->relationship_info('informix_loader_test7') };
+                    is try { $rsrc->column_info('value')->{size} }, 100,
+                        'column in database two introspected correctly';
 
-                is_deeply $rel_info->{cond}, {
-                    'foreign.six_id' => 'self.id'
-                }, 'relationship in database two';
+                    lives_and {
+                        ok $rs = $test_schema->resultset("${db2_moniker}InformixLoaderTest6");
+                    } 'got resultset for table in database two';
 
-                is $rel_info->{attrs}{accessor}, 'single',
-                    'relationship in database two';
+                    lives_and {
+                        ok $row = $rs->create({ value => 'foo' });
+                    } 'executed SQL on table in database two';
 
-                is $rel_info->{attrs}{join_type}, 'LEFT',
-                    'relationship in database two';
+                    $rel_info = try { $rsrc->relationship_info('informix_loader_test7') };
 
-                lives_and {
-                    ok $rsrc = $test_schema->source('InformixLoaderTest7');
-                } 'got source for table in database two';
+                    is_deeply $rel_info->{cond}, {
+                        'foreign.six_id' => 'self.id'
+                    }, 'relationship in database two';
 
-                %uniqs = try { $rsrc->unique_constraints };
+                    is $rel_info->{attrs}{accessor}, 'single',
+                        'relationship in database two';
 
-                is keys %uniqs, 2,
-                    'got unique and primary constraint in database two';
+                    is $rel_info->{attrs}{join_type}, 'LEFT',
+                        'relationship in database two';
+
+                    lives_and {
+                        ok $rsrc = $test_schema->source("${db2_moniker}InformixLoaderTest7");
+                    } 'got source for table in database two';
+
+                    %uniqs = try { $rsrc->unique_constraints };
+
+                    is keys %uniqs, 2,
+                        'got unique and primary constraint in database two';
+
+                    delete $uniqs{primary};
+
+                    is_deeply ((values %uniqs)[0], ['six_id'],
+                        'correct unique constraint in database two');
+                }
             }
         },
     },
@@ -295,6 +331,16 @@ else {
     $tester->run_tests();
 }
 
+sub db_name {
+    my $schema = shift;
+
+    # When we clone the schema, it still references the original loader, which
+    # references the original schema.
+    local $schema->loader->{schema} = $schema;
+
+    return $schema->loader->_current_db;
+}
+
 END {
     if (not $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP}) {
         if (my $dbh2 = try { $extra_schema->storage->dbh }) {
@@ -303,6 +349,7 @@ END {
             try {
                 $dbh2->do('DROP TABLE informix_loader_test7');
                 $dbh2->do('DROP TABLE informix_loader_test6');
+                $dbh2->do('DROP TABLE informix_loader_test5');
                 $dbh1->do('DROP TABLE informix_loader_test5');
                 $dbh1->do('DROP TABLE informix_loader_test4');
             }