handle column accessor collisions with UNIVERSAL methods
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / lib / dbixcsl_common_tests.pm
index e4aa0d2..c8bc757 100644 (file)
@@ -12,10 +12,14 @@ use DBI;
 use Digest::MD5;
 use File::Find 'find';
 use Class::Unload ();
-use Data::Dumper::Concise;
+use DBIx::Class::Schema::Loader::Utils 'dumper_squashed';
 use List::MoreUtils 'apply';
+use DBIx::Class::Schema::Loader::Optional::Dependencies ();
+use namespace::clean;
 
-my $DUMP_DIR = './t/_common_dump';
+use dbixcsl_test_dir qw/$tdir/;
+
+my $DUMP_DIR = "$tdir/common_dump";
 rmtree $DUMP_DIR;
 
 sub new {
@@ -42,7 +46,7 @@ sub new {
     # Optional extra tables and tests
     $self->{extra} ||= {};
 
-    $self->{date_datatype} ||= 'DATE';
+    $self->{basic_date_datatype} ||= 'DATE';
 
     # Not all DBS do SQL-standard CURRENT_TIMESTAMP
     $self->{default_function} ||= "current_timestamp";
@@ -88,7 +92,7 @@ sub run_tests {
 
     my $extra_count = $self->{extra}{count} || 0;
 
-    plan tests => @connect_info * (178 + $extra_count + ($self->{data_type_tests}{test_count} || 0));
+    plan tests => @connect_info * (183 + $extra_count + ($self->{data_type_tests}{test_count} || 0));
 
     foreach my $info_idx (0..$#connect_info) {
         my $info = $connect_info[$info_idx];
@@ -120,22 +124,13 @@ sub run_only_extra_tests {
         $self->drop_extra_tables_only;
 
         my $dbh = $self->dbconnect(1);
-        {
-            # Silence annoying but harmless postgres "NOTICE:  CREATE TABLE..."
-            local $SIG{__WARN__} = sub {
-                my $msg = shift;
-                warn $msg unless $msg =~ m{^NOTICE:\s+CREATE TABLE};
-            };
-
-
-            $dbh->do($_) for @{ $self->{extra}{create} || [] };
-            $dbh->do($_) for @{ $self->{data_type_tests}{ddl} || []};
-        }
+        $dbh->do($_) for @{ $self->{extra}{create} || [] };
+        $dbh->do($_) for @{ $self->{data_type_tests}{ddl} || []};
         $self->{_created} = 1;
 
         my $file_count = grep /CREATE (?:TABLE|VIEW)/i, @{ $self->{extra}{create} || [] };
         $file_count++; # schema
-        $file_count++ if $self->{data_type_tests}{ddl};
+        $file_count++ for @{ $self->{data_type_tests}{table_names} || [] };
 
         my $schema_class = $self->setup_schema($info, $file_count);
         my ($monikers, $classes) = $self->monikers_and_classes($schema_class);
@@ -155,6 +150,7 @@ sub drop_extra_tables_only {
     my $self = shift;
 
     my $dbh = $self->dbconnect(0);
+
     $dbh->do($_) for @{ $self->{extra}{pre_drop_ddl} || [] };
     $dbh->do("DROP TABLE $_") for @{ $self->{extra}{drop} || [] };
 
@@ -175,9 +171,19 @@ sub setup_schema {
 
     my $debug = ($self->{verbose} > 1) ? 1 : 0;
 
+    if (
+      $ENV{SCHEMA_LOADER_TESTS_USE_MOOSE}
+        &&
+      ! DBIx::Class::Schema::Loader::Optional::Dependencies->req_ok_for('use_moose')
+    ) {
+      die sprintf ("Missing dependencies for SCHEMA_LOADER_TESTS_USE_MOOSE: %s\n",
+        DBIx::Class::Schema::Loader::Optional::Dependencies->req_missing_for('use_moose')
+      );
+    }
+
     my %loader_opts = (
         constraint              =>
-           qr/^(?:\S+\.)?(?:(?:$self->{vendor}|extra)_)?loader_test[0-9]+(?!.*_)/i,
+          qr/^(?:\S+\.)?(?:(?:$self->{vendor}|extra)_?)?loader_?test[0-9]+(?!.*_)/i,
         relationships           => 1,
         additional_classes      => 'TestAdditional',
         additional_base_classes => 'TestAdditionalBase',
@@ -193,6 +199,7 @@ sub setup_schema {
         dump_directory          => $DUMP_DIR,
         datetime_timezone       => 'Europe/Berlin',
         datetime_locale         => 'de_DE',
+        use_moose               => $ENV{SCHEMA_LOADER_TESTS_USE_MOOSE},
         %{ $self->{loader_options} || {} },
     );
 
@@ -203,7 +210,7 @@ sub setup_schema {
     my $file_count;
     {
         my @loader_warnings;
-        local $SIG{__WARN__} = sub { push(@loader_warnings, $_[0]); };
+        local $SIG{__WARN__} = sub { push(@loader_warnings, @_); };
          eval qq{
              package $schema_class;
              use base qw/DBIx::Class::Schema::Loader/;
@@ -219,7 +226,8 @@ sub setup_schema {
         my $standard_sources = not defined $expected_count;
 
         if ($standard_sources) {
-            $expected_count = 36 + ($self->{data_type_tests}{test_count} ? 1 : 0);
+            $expected_count = 36;
+            $expected_count++ for @{ $self->{data_type_tests}{table_names} || [] };
 
             $expected_count += grep /CREATE (?:TABLE|VIEW)/i,
                 @{ $self->{extra}{create} || [] };
@@ -245,6 +253,8 @@ sub setup_schema {
  
         $warn_count++ for grep /\b(?!loader_test9)\w+ has no primary key/i, @loader_warnings;
 
+        $warn_count++ for grep { my $w = $_; grep $w =~ $_, @{ $self->{warnings} || [] } } @loader_warnings;
+
         if ($standard_sources) {
             if($self->{skip_rels}) {
                 SKIP: {
@@ -314,7 +324,10 @@ sub test_schema {
     isa_ok( $rsobj35, "DBIx::Class::ResultSet" );
 
     my @columns_lt2 = $class2->columns;
-    is_deeply( \@columns_lt2, [ qw/id dat dat2 set_primary_key dbix_class_testcomponent/ ], "Column Ordering" );
+    is_deeply( \@columns_lt2, [ qw/id dat dat2 set_primary_key can dbix_class_testcomponent meta/ ], "Column Ordering" );
+
+    is $class2->column_info('can')->{accessor}, undef,
+        'accessor for column name that conflicts with a UNIVERSAL method removed';
 
     is $class2->column_info('set_primary_key')->{accessor}, undef,
         'accessor for column name that conflicts with a result base class method removed';
@@ -322,6 +335,9 @@ sub test_schema {
     is $class2->column_info('dbix_class_testcomponent')->{accessor}, undef,
         'accessor for column name that conflicts with a component class method removed';
 
+    is $class2->column_info('meta')->{accessor}, undef,
+        'accessor for column name that conflicts with Moose removed';
+
     my %uniq1 = $class1->unique_constraints;
     my $uniq1_test = 0;
     foreach my $ucname (keys %uniq1) {
@@ -431,11 +447,21 @@ sub test_schema {
         'constant integer default',
     );
 
+    is(
+        $class35->column_info('a_negative_int')->{default_value}, -42,
+        'constant negative integer default',
+    );
+
     cmp_ok(
         $class35->column_info('a_double')->{default_value}, '==', 10.555,
         'constant numeric default',
     );
 
+    cmp_ok(
+        $class35->column_info('a_negative_double')->{default_value}, '==', -10.555,
+        'constant negative numeric default',
+    );
+
     my $function_default = $class35->column_info('a_function')->{default_value};
 
     isa_ok( $function_default, 'SCALAR', 'default_value for function default' );
@@ -607,8 +633,8 @@ sub test_schema {
         is $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_update}, 'CASCADE',
             "on_update => 'CASCADE' on belongs_to by default";
 
-        ok ((not exists $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{is_deferrable}),
-            "is_deferrable => 1 not on belongs_to by default");
+        is $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{is_deferrable}, 1,
+            "is_deferrable => 1 on belongs_to by default";
 
         ok ((not exists $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{cascade_delete}),
             'belongs_to does not have cascade_delete');
@@ -632,12 +658,14 @@ sub test_schema {
             'might_have does not have is_deferrable');
 
         # find on multi-col pk
-        my $obj5 = 
-           eval { $rsobj5->find({id1 => 1, iD2 => 1}) } ||
-           eval { $rsobj5->find({id1 => 1, id2 => 1}) };
-       die $@ if $@;
-
-        is( (eval { $obj5->id2 } || eval { $obj5->i_d2 }), 1, "Find on multi-col PK" );
+        if ($conn->_loader->preserve_case) {
+            my $obj5 = $rsobj5->find({id1 => 1, iD2 => 1});
+            is $obj5->i_d2, 1, 'Find on multi-col PK';
+        }
+        else {
+           my $obj5 = $rsobj5->find({id1 => 1, id2 => 1});
+            is $obj5->id2, 1, 'Find on multi-col PK';
+        }
 
         # mulit-col fk def
         my $obj6 = $rsobj6->find(1);
@@ -882,7 +910,7 @@ sub test_schema {
 
         my $find_cb = sub {
             return if -d;
-            return if $_ eq 'LoaderTest30.pm';
+            return if /^(?:LoaderTest30|LoaderTest1|LoaderTest2X)\.pm\z/;
 
             open my $fh, '<', $_ or die "Could not open $_ for reading: $!";
             binmode $fh;
@@ -891,35 +919,31 @@ sub test_schema {
 
         find $find_cb, $DUMP_DIR;
 
-        my $before_digest = $digest->digest;
+#        system "rm -f /tmp/before_rescan/* /tmp/after_rescan/*";
+#        system "cp $tdir/common_dump/DBIXCSL_Test/Schema/*.pm /tmp/before_rescan";
+
+        my $before_digest = $digest->b64digest;
 
         $conn->storage->disconnect; # needed for Firebird and Informix
         my $dbh = $self->dbconnect(1);
-
-        {
-            # Silence annoying but harmless postgres "NOTICE:  CREATE TABLE..."
-            local $SIG{__WARN__} = sub {
-                my $msg = shift;
-                warn $msg unless $msg =~ m{^NOTICE:\s+CREATE TABLE};
-            };
-
-            $dbh->do($_) for @statements_rescan;
-        }
-
+        $dbh->do($_) for @statements_rescan;
         $dbh->disconnect;
 
         sleep 1;
 
         my @new = do {
-            # kill the 'Dumping manual schema' warnings
-            local $SIG{__WARN__} = sub {};
+            local $SIG{__WARN__} = sub { warn @_
+                unless $_[0] =~ /(?i:loader_test)\d+ has no primary key|^Dumping manual schema|^Schema dump completed/
+            };
             $conn->rescan;
         };
         is_deeply(\@new, [ qw/LoaderTest30/ ], "Rescan");
 
+#        system "cp t/_common_dump/DBIXCSL_Test/Schema/*.pm /tmp/after_rescan";
+
         $digest = Digest::MD5->new;
         find $find_cb, $DUMP_DIR;
-        my $after_digest = $digest->digest;
+        my $after_digest = $digest->b64digest;
 
         is $before_digest, $after_digest,
             'dumped files are not rewritten when there is no modification';
@@ -941,7 +965,9 @@ sub test_schema {
         $conn->storage->dbh->do("DROP TABLE loader_test30");
 
         @new = do {
-            local $SIG{__WARN__} = sub {};
+            local $SIG{__WARN__} = sub { warn @_
+                unless $_[0] =~ /(?i:loader_test)\d+ has no primary key|^Dumping manual schema|^Schema dump completed/
+            };
             $conn->rescan;
         };
         is_deeply(\@new, [], 'no new tables on rescan');
@@ -956,6 +982,8 @@ sub test_schema {
     # run extra tests
     $self->{extra}{run}->($conn, $monikers, $classes) if $self->{extra}{run};
 
+    $self->test_preserve_case($conn);
+
     $self->drop_tables unless $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP};
 
     $conn->storage->disconnect;
@@ -976,19 +1004,9 @@ sub test_data_types {
                 my %info = %{ $rsrc->column_info($col_name) };
                 delete @info{qw/is_nullable timezone locale sequence/};
 
-                my $text_col_def = do {
-                    my $dd = Dumper;
-                    $dd->Indent(0);
-                    $dd->Values([\%info]);
-                    $dd->Dump;
-                };
+                my $text_col_def = dumper_squashed \%info;
 
-                my $text_expected_info = do {
-                    my $dd = Dumper;
-                    $dd->Indent(0);
-                    $dd->Values([$expected_info]);
-                    $dd->Dump;
-                };
+                my $text_expected_info = dumper_squashed $expected_info;
 
                 is_deeply \%info, $expected_info,
                     "test column $col_name has definition: $text_col_def expecting: $text_expected_info";
@@ -997,6 +1015,53 @@ sub test_data_types {
     }
 }
 
+sub test_preserve_case {
+    my ($self, $conn) = @_;
+
+    my ($oqt, $cqt) = $self->get_oqt_cqt(always => 1); # open quote, close quote
+
+    my $dbh = $self->dbconnect;
+
+    $dbh->do($_) for (
+qq|
+    CREATE TABLE ${oqt}LoaderTest40${cqt} (
+        ${oqt}Id${cqt} INTEGER NOT NULL PRIMARY KEY,
+        ${oqt}Foo3Bar${cqt} VARCHAR(100) NOT NULL
+    ) $self->{innodb}
+|,
+qq|
+    CREATE TABLE ${oqt}LoaderTest41${cqt} (
+        ${oqt}Id${cqt} INTEGER NOT NULL PRIMARY KEY,
+        ${oqt}LoaderTest40Id${cqt} INTEGER,
+        FOREIGN KEY (${oqt}LoaderTest40Id${cqt}) REFERENCES ${oqt}LoaderTest40${cqt} (${oqt}Id${cqt})
+    ) $self->{innodb}
+|,
+qq| INSERT INTO ${oqt}LoaderTest40${cqt} VALUES (1, 'foo') |,
+qq| INSERT INTO ${oqt}LoaderTest41${cqt} VALUES (1, 1) |,
+    );
+    $conn->storage->disconnect;
+
+    local $conn->_loader->{preserve_case} = 1;
+    $conn->_loader->_setup;
+
+
+    {
+        local $SIG{__WARN__} = sub { warn @_
+            unless $_[0] =~ /(?i:loader_test)\d+ has no primary key|^Dumping manual schema|^Schema dump completed/
+        };
+        $conn->rescan;
+    };
+
+    if (not $self->{skip_rels}) {
+        is $conn->resultset('LoaderTest41')->find(1)->loader_test40->foo3_bar, 'foo',
+            'rel and accessor for mixed-case column name in mixed case table';
+    }
+    else {
+        is $conn->resultset('LoaderTest40')->find(1)->foo3_bar, 'foo',
+            'accessor for mixed-case column name in mixed case table';
+    }
+}
+
 sub monikers_and_classes {
     my ($self, $schema_class) = @_;
     my ($monikers, $classes);
@@ -1057,11 +1122,33 @@ sub dbconnect {
     return $dbh;
 }
 
+sub get_oqt_cqt {
+    my $self = shift;
+    my %opts = @_;
+
+    if ((not $opts{always}) && $self->{preserve_case_mode_is_exclusive}) {
+        return ('', '');
+    }
+
+    # XXX should get quote_char from the storage of an initialized loader.
+    my ($oqt, $cqt); # open quote, close quote
+    if (ref $self->{quote_char}) {
+        ($oqt, $cqt) = @{ $self->{quote_char} };
+    }
+    else {
+        $oqt = $cqt = $self->{quote_char} || '';
+    }
+
+    return ($oqt, $cqt);
+}
+
 sub create {
     my $self = shift;
 
     $self->{_created} = 1;
 
+    $self->drop_tables;
+
     my $make_auto_inc = $self->{auto_inc_cb} || sub {};
     @statements = (
         qq{
@@ -1083,7 +1170,9 @@ sub create {
                 dat VARCHAR(32) NOT NULL,
                 dat2 VARCHAR(32) NOT NULL,
                 set_primary_key INTEGER $self->{null},
+                can INTEGER $self->{null},
                 dbix_class_testcomponent INTEGER $self->{null},
+                meta INTEGER $self->{null},
                 UNIQUE (dat2, dat)
             ) $self->{innodb}
         },
@@ -1113,7 +1202,9 @@ sub create {
                 id INTEGER NOT NULL PRIMARY KEY,
                 a_varchar VARCHAR(100) DEFAULT 'foo',
                 an_int INTEGER DEFAULT 42,
+                a_negative_int INTEGER DEFAULT -42,
                 a_double DOUBLE PRECISION DEFAULT 10.555,
+                a_negative_double DOUBLE PRECISION DEFAULT -10.555,
                 a_function $self->{default_function_def}
             ) $self->{innodb}
         },
@@ -1121,13 +1212,16 @@ sub create {
         qq{
             CREATE TABLE loader_test36 (
                 id INTEGER NOT NULL PRIMARY KEY,
-                a_date $self->{date_datatype},
+                a_date $self->{basic_date_datatype},
                 b_char_as_data VARCHAR(100),
                 c_char_as_data VARCHAR(100)
             ) $self->{innodb}
         },
     );
 
+    # some DBs require mixed case identifiers to be quoted
+    my ($oqt, $cqt) = $self->get_oqt_cqt;
+
     @statements_reltests = (
         qq{
             CREATE TABLE loader_test3 (
@@ -1155,33 +1249,33 @@ sub create {
         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(125,3,'ccc') },
         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(126,4,'ddd') },
 
-        qq{
+        qq|
             CREATE TABLE loader_test5 (
                 id1 INTEGER NOT NULL,
-                iD2 INTEGER NOT NULL,
+                ${oqt}iD2${cqt} INTEGER NOT NULL,
                 dat VARCHAR(8),
                 from_id INTEGER $self->{null},
                 to_id INTEGER $self->{null},
-                PRIMARY KEY (id1,iD2),
+                PRIMARY KEY (id1,${oqt}iD2${cqt}),
                 FOREIGN KEY (from_id) REFERENCES loader_test4 (id),
                 FOREIGN KEY (to_id) REFERENCES loader_test4 (id)
             ) $self->{innodb}
-        },
+        |,
 
-        q{ INSERT INTO loader_test5 (id1,iD2,dat) VALUES (1,1,'aaa') },
+        qq| INSERT INTO loader_test5 (id1,${oqt}iD2${cqt},dat) VALUES (1,1,'aaa') |,
 
-        qq{
+        qq|
             CREATE TABLE loader_test6 (
                 id INTEGER NOT NULL PRIMARY KEY,
-                Id2 INTEGER,
+                ${oqt}Id2${cqt} INTEGER,
                 loader_test2_id INTEGER,
                 dat VARCHAR(8),
                 FOREIGN KEY (loader_test2_id)  REFERENCES loader_test2 (id),
-                FOREIGN KEY(id,Id2) REFERENCES loader_test5 (id1,iD2)
+                FOREIGN KEY(id,${oqt}Id2${cqt}) REFERENCES loader_test5 (id1,${oqt}iD2${cqt})
             ) $self->{innodb}
-        },
+        |,
 
-        (q{ INSERT INTO loader_test6 (id, Id2,loader_test2_id,dat) } .
+        (qq| INSERT INTO loader_test6 (id, ${oqt}Id2${cqt},loader_test2_id,dat) | .
          q{ VALUES (1, 1,1,'aaa') }),
 
         qq{
@@ -1492,19 +1586,14 @@ sub create {
     );
 
     $self->drop_tables;
-    $self->drop_tables; # twice for good measure
 
     my $dbh = $self->dbconnect(1);
 
-    # Silence annoying but harmless postgres "NOTICE:  CREATE TABLE..."
-    local $SIG{__WARN__} = sub {
-        my $msg = shift;
-        warn $msg unless $msg =~ m{^NOTICE:\s+CREATE TABLE};
-    };
+    $dbh->do($_) for @{ $self->{pre_create} || [] };
 
     $dbh->do($_) foreach (@statements);
 
-    $dbh->do($_) foreach (@{ $self->{data_type_tests}{ddl} || {} });
+    $dbh->do($_) foreach (@{ $self->{data_type_tests}{ddl} || [] });
 
     unless($self->{skip_rels}) {
         # hack for now, since DB2 doesn't like inline comments, and we need
@@ -1533,6 +1622,7 @@ sub drop_tables {
     my $self = shift;
 
     my @tables = qw/
+        loader_test1
         loader_test1s
         loader_test2
         LOADER_test23
@@ -1594,20 +1684,27 @@ sub drop_tables {
 
     my @tables_rescan = qw/ loader_test30 /;
 
+    my @tables_preserve_case_tests = qw/ LoaderTest41 LoaderTest40 /;
+
     my $drop_fk_mysql =
         q{ALTER TABLE loader_test10 DROP FOREIGN KEY loader_test11_fk};
 
     my $drop_fk =
         q{ALTER TABLE loader_test10 DROP CONSTRAINT loader_test11_fk};
 
-    my $dbh = $self->dbconnect(0);
+    # For some reason some tests do this twice (I guess dependency issues?)
+    # do it twice for all drops
+    for (1,2) {
+      my $dbh = $self->dbconnect(0);
 
-    $dbh->do($_) for @{ $self->{extra}{pre_drop_ddl} || [] };
-    $dbh->do("DROP TABLE $_") for @{ $self->{extra}{drop} || [] };
+      $dbh->do($_) for @{ $self->{extra}{pre_drop_ddl} || [] };
 
-    my $drop_auto_inc = $self->{auto_inc_drop_cb} || sub {};
+      $dbh->do("DROP TABLE $_") for @{ $self->{extra}{drop} || [] };
 
-    unless($self->{skip_rels}) {
+      my $drop_auto_inc = $self->{auto_inc_drop_cb} || sub {};
+
+      unless($self->{skip_rels}) {
+        $dbh->do("DROP TABLE $_") for (@tables_reltests);
         $dbh->do("DROP TABLE $_") for (@tables_reltests);
         if($self->{vendor} =~ /mysql/i) {
             $dbh->do($drop_fk_mysql);
@@ -1624,20 +1721,20 @@ sub drop_tables {
         unless($self->{no_implicit_rels}) {
             $dbh->do("DROP TABLE $_") for (@tables_implicit_rels);
         }
-    }
-    $dbh->do($_) for map { $drop_auto_inc->(@$_) } @tables_auto_inc;
-    $dbh->do("DROP TABLE $_") for (@tables, @tables_rescan);
+      }
+      $dbh->do($_) for map { $drop_auto_inc->(@$_) } @tables_auto_inc;
+      $dbh->do("DROP TABLE $_") for (@tables, @tables_rescan);
 
-    foreach my $data_type_table (@{ $self->{data_type_tests}{table_names} || {} }) {
+      foreach my $data_type_table (@{ $self->{data_type_tests}{table_names} || [] }) {
         $dbh->do("DROP TABLE $data_type_table");
-    }
+      }
 
-    $dbh->disconnect;
+      my ($oqt, $cqt) = $self->get_oqt_cqt(always => 1);
 
-# fixup for Firebird
-    $dbh = $self->dbconnect(0);
-    $dbh->do('DROP TABLE loader_test2');
-    $dbh->disconnect;
+      $dbh->do("DROP TABLE ${oqt}${_}${cqt}") for @tables_preserve_case_tests;
+
+      $dbh->disconnect;
+    }
 }
 
 sub _custom_column_info {
@@ -1664,7 +1761,9 @@ sub _custom_column_info {
 }
 
 my %DATA_TYPE_MULTI_TABLE_OVERRIDES = (
-    oracle => qr/\blong\b/,
+    oracle => qr/\blong\b/i,
+    mssql  => qr/\b(?:timestamp|rowversion)\b/i,
+    informix => qr/\b(?:bigserial|serial8)\b/i,
 );
 
 sub setup_data_type_tests {
@@ -1675,9 +1774,14 @@ sub setup_data_type_tests {
     my $tests = $self->{data_type_tests} = {};
 
     # split types into tables based on overrides
-    my @types = keys %$types;
-    my @split_off_types   = grep  /$DATA_TYPE_MULTI_TABLE_OVERRIDES{lc($self->{vendor})}/i, @types;
-    my @first_table_types = grep !/$DATA_TYPE_MULTI_TABLE_OVERRIDES{lc($self->{vendor})}/i, @types;
+    my (@types, @split_off_types, @first_table_types);
+    {
+        my $split_off_re = $DATA_TYPE_MULTI_TABLE_OVERRIDES{lc($self->{vendor})} || qr/(?!)/;
+
+        @types = keys %$types;
+        @split_off_types   = grep  /$split_off_re/, @types;
+        @first_table_types = grep !/$split_off_re/, @types;
+    }
 
     @types = +{ map +($_, $types->{$_}), @first_table_types },
         map +{ $_, $types->{$_} }, @split_off_types;
@@ -1709,7 +1813,7 @@ sub setup_data_type_tests {
             my @size = split /,/, $size;
 
             # some DBs don't like very long column names
-            if ($self->{vendor} =~ /^(?:firebird|sqlanywhere|oracle)\z/i) {
+            if ($self->{vendor} =~ /^(?:firebird|sqlanywhere|oracle|db2)\z/i) {
                 my ($col_def, $default) = $type_alias =~ /^(.*)(default.*)?\z/i;
 
                 $type_alias = substr $col_def, 0, 15;
@@ -1753,8 +1857,8 @@ sub setup_data_type_tests {
 sub DESTROY {
     my $self = shift;
     unless ($ENV{SCHEMA_LOADER_TESTS_NOCLEANUP}) {
-       $self->drop_tables if $self->{_created};
-       rmtree $DUMP_DIR
+      $self->drop_tables if $self->{_created};
+      rmtree $DUMP_DIR
     }
 }