introspect ON DELETE/UPDATE clauses for MySQL FKs
Rafael Kitover [Tue, 21 Aug 2012 23:23:15 +0000 (19:23 -0400)]
The default for these has been CASCADE for a while now, the idea was
that this is the most useful default for deployed schemas and it would
be replaced with the actual introspected value when this capability
would be added.

A user wrote in to RT that this capability was missing for MySQL so I
decided to start on adding it, with MySQL first.

This means that for MySQL the default for on_delete/on_update is now
RESTRICT rather than CASCADE, and this will probably soon be true for
other backends that support these clauses.

Also there are some whitespace changes in this commit due to some
cleanup autocommands I put into my .vimrc, read it with git diff -w.

Changes
lib/DBIx/Class/Schema/Loader/Base.pm
lib/DBIx/Class/Schema/Loader/DBI/mysql.pm
lib/DBIx/Class/Schema/Loader/RelBuilder.pm
t/10_02mysql_common.t
t/lib/dbixcsl_common_tests.pm

diff --git a/Changes b/Changes
index a38feeb..9b572be 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,10 @@
 Revision history for Perl extension DBIx::Class::Schema::Loader
 
+        - MySQL: introspect ON DELETE/UPDATE clauses for foreign keys.
+        - MySQL WARNING: the default on_delete/on_update attributes for
+          belongs_to relationships is now RESTRICT, *NOT* CASCADE! This is
+          overridable via the relationship_attrs option.
+
 0.07025  2012-06-08 22:48:05
         - support SQL Server 2000 again (broken in 0.07011)
         - some slight optimization for SQL Server driver
@@ -470,7 +475,7 @@ Revision history for Perl extension DBIx::Class::Schema::Loader
         - Workaround for new incompatible changes in DBD::mysql's "tables"
           method, which was causing us to find no tables w/ DBD::mysql
           4.002+
-        - Fixed quoting problem in _table_columns (could cause crash when 
+        - Fixed quoting problem in _table_columns (could cause crash when
           dumping/doing a static create) (from ash)
 
 0.03009  Wed Nov 15 14:03:37 UTC 2006
index 77b7031..7072a5c 100644 (file)
@@ -448,7 +448,7 @@ C<schema>, C<name>
 
 =item * Informix, MSSQL, Sybase ASE
 
-C<database>, C<schema>, C<name>    
+C<database>, C<schema>, C<name>
 
 =back
 
@@ -594,7 +594,7 @@ load certain components for specified Result classes. For example:
                             'InflateColumn::DateTime',
                         ],
   }
-  
+
 You may use this in conjunction with L</components>.
 
 =head2 result_roles
@@ -614,7 +614,7 @@ certain roles for specified Result classes. For example:
                         ],
       RouteChange    => 'YourApp::Role::TripEvent',
   }
-  
+
 You may use this in conjunction with L</result_roles>.
 
 =head2 use_namespaces
@@ -885,7 +885,7 @@ sub new {
         }
         $self->result_components_map($self->{result_component_map})
     }
-    
+
     if (defined $self->{result_role_map}) {
         if (defined $self->result_roles_map) {
             croak "Specify only one of result_roles_map or result_role_map";
@@ -1530,9 +1530,9 @@ sub _load_tables {
 
                 # check if there are still clashes
                 my %by_moniker;
-                
+
                 while (my ($t, $m) = each %new_monikers) {
-                    push @{ $by_moniker{$m} }, $t; 
+                    push @{ $by_moniker{$m} }, $t;
                 }
 
                 foreach my $m (grep @{ $by_moniker{$_} } > 1, keys %by_moniker) {
@@ -1600,7 +1600,7 @@ sub _reload_classes {
     $self->_dump_to_dir(map { $self->classes->{$_->sql_name} } @tables);
 
     unshift @INC, $self->dump_directory;
-    
+
     my @to_register;
     my %have_source = map { $_ => $self->schema->source($_) }
         $self->schema->sources;
@@ -1608,7 +1608,7 @@ sub _reload_classes {
     for my $table (@tables) {
         my $moniker = $self->monikers->{$table->sql_name};
         my $class = $self->classes->{$table->sql_name};
-        
+
         {
             no warnings 'redefine';
             local *Class::C3::reinitialize = sub {};  # to speed things up, reinitialized below
@@ -1778,7 +1778,7 @@ sub _dump_to_dir {
     my $result_base_class = $self->result_base_class || 'DBIx::Class::Core';
 
     foreach my $src_class (@classes) {
-        my $src_text = 
+        my $src_text =
               qq|use utf8;\n|
             . qq|package $src_class;\n\n|
             . qq|# Created by DBIx::Class::Schema::Loader\n|
@@ -1833,7 +1833,7 @@ sub _sig_comment {
     my ($self, $version, $ts) = @_;
     return qq|\n\n# Created by DBIx::Class::Schema::Loader|
          . qq| v| . $version
-         . q| @ | . $ts 
+         . q| @ | . $ts
          . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
 }
 
@@ -1978,7 +1978,7 @@ sub _default_moose_custom_content {
     if (not $is_schema) {
         return qq|\n__PACKAGE__->meta->make_immutable;|;
     }
-    
+
     return qq|\n__PACKAGE__->meta->make_immutable(inline_constructor => 0);|;
 }
 
@@ -2704,12 +2704,12 @@ sub _make_pod {
     elsif ($method eq 'add_unique_constraint') {
         $self->_pod($class, '=head1 UNIQUE CONSTRAINTS')
             unless $self->{_uniqs_started}{$class};
-        
+
         my ($name, $cols) = @_;
 
         $self->_pod($class, "=head2 C<$name>");
         $self->_pod($class, '=over 4');
-        
+
         foreach my $col (@$cols) {
             $self->_pod($class, "=item \* L</$col>");
         }
@@ -2722,7 +2722,7 @@ sub _make_pod {
     elsif ($method eq 'set_primary_key') {
         $self->_pod($class, "=head1 PRIMARY KEY");
         $self->_pod($class, '=over 4');
-        
+
         foreach my $col (@_) {
             $self->_pod($class, "=item \* L</$col>");
         }
@@ -2777,7 +2777,7 @@ sub __table_comment {
     if (my $code = $self->can('_table_comment')) {
         return $self->_filter_comment($self->$code(@_));
     }
-    
+
     return '';
 }
 
index 1fa1a63..380e49c 100644 (file)
@@ -65,7 +65,7 @@ sub _system_schemas {
     return ($self->next::method(@_), 'mysql');
 }
 
-sub _tables_list { 
+sub _tables_list {
     my ($self, $opts) = @_;
 
     return $self->next::method($opts, undef, undef);
@@ -83,12 +83,12 @@ sub _table_fk_info {
     my $nqt = qr/[^"`]/;
 
     my (@reldata) = ($table_def =~
-        /CONSTRAINT ${qt}${nqt}+${qt} FOREIGN KEY \($qt(.*)$qt\) REFERENCES (?:$qt($nqt+)$qt\.)?$qt($nqt+)$qt \($qt(.+)$qt\)/ig
+        /CONSTRAINT ${qt}${nqt}+${qt} FOREIGN KEY \($qt(.*)$qt\) REFERENCES (?:$qt($nqt+)$qt\.)?$qt($nqt+)$qt \($qt(.+)$qt\)\s*(.*)/ig
     );
 
     my @rels;
     while (scalar @reldata > 0) {
-        my ($cols, $f_schema, $f_table, $f_cols) = splice @reldata, 0, 4;
+        my ($cols, $f_schema, $f_table, $f_cols, $rest) = splice @reldata, 0, 5;
 
         my @cols   = map { s/$qt//g; $self->_lc($_) }
             split(/$qt?\s*$qt?,$qt?\s*$qt?/, $cols);
@@ -129,10 +129,37 @@ sub _table_fk_info {
             );
         }
 
+        my %attrs;
+
+        if ($rest) {
+            my @on_clauses = $rest =~ /(ON DELETE|ON UPDATE) (RESTRICT|CASCADE|SET NULL|NO ACTION) ?/ig;
+
+            while (my ($clause, $value) = splice @on_clauses, 0, 2) {
+                $clause = lc $clause;
+                $clause =~ s/ /_/;
+
+                $value = uc $value;
+
+                $attrs{$clause} = $value;
+            }
+        }
+
+# The default behavior is RESTRICT. Specifying RESTRICT explicitly just removes
+# that ON clause from the SHOW CREATE TABLE output. For this reason, even
+# though the default for these clauses everywhere else in Schema::Loader is
+# CASCADE, we change the default here to RESTRICT in order to reproduce the
+# schema faithfully.
+        $attrs{on_delete}     ||= 'RESTRICT';
+        $attrs{on_update}     ||= 'RESTRICT';
+
+# MySQL does not have a DEFERRABLE attribute, but there is a way to defer FKs.
+        $attrs{is_deferrable}   = 1;
+
         push(@rels, {
             local_columns => \@cols,
             remote_columns => \@f_cols,
             remote_table => $remote_table,
+            attrs => \%attrs,
         });
     }
 
index 629970e..87afaef 100644 (file)
@@ -47,8 +47,8 @@ Arguments: $loader object
 
 =head2 generate_code
 
-Arguments: 
-    
+Arguments:
+
     [
         [ local_moniker1 (scalar), fk_info1 (arrayref), uniq_info1 (arrayref) ]
         [ local_moniker2 (scalar), fk_info2 (arrayref), uniq_info2 (arrayref) ]
@@ -249,24 +249,25 @@ sub _default_relationship_attrs { +{
     },
 } }
 
-# accessor for options to be passed to each generated relationship
-# type.  take single argument, the relationship type name, and returns
-# either a hashref (if some options are set), or nothing
+# Accessor for options to be passed to each generated relationship type. takes
+# the relationship type name and optionally any attributes from the database
+# (such as FK ON DELETE/UPDATE and DEFERRABLE clauses), and returns a
+# hashref or undef if nothing is set.
+#
+# The attributes from the database override the default attributes, which in
+# turn are overridden by user supplied attributes.
 sub _relationship_attrs {
-    my ( $self, $reltype ) = @_;
+    my ( $self, $reltype, $db_attrs ) = @_;
     my $r = $self->relationship_attrs;
 
     my %composite = (
         %{ $self->_default_relationship_attrs->{$reltype} || {} },
-        %{ $r->{all} || {} }
+        %{ $db_attrs || {} },
+        %{ $r->{all} || {} },
+        %{ $r->{$reltype} || {} },
     );
 
-    if( my $specific = $r->{$reltype} ) {
-        while( my ($k,$v) = each %$specific ) {
-            $composite{$k} = $v;
-        }
-    }
-    return \%composite;
+    return %composite ? \%composite : undef;
 }
 
 sub _strip_id_postfix {
@@ -278,10 +279,10 @@ sub _strip_id_postfix {
 }
 
 sub _remote_attrs {
-    my ($self, $local_moniker, $local_cols) = @_;
+    my ($self, $local_moniker, $local_cols, $fk_attrs) = @_;
 
-    # get our base set of attrs from _relationship_attrs, if present
-    my $attrs = $self->_relationship_attrs('belongs_to') || {};
+    # get our set of attrs from _relationship_attrs, which uses the FK attrs if available
+    my $attrs = $self->_relationship_attrs('belongs_to', $fk_attrs) || {};
 
     # If any referring column is nullable, make 'belongs_to' an
     # outer join, unless explicitly set by relationship_attrs
@@ -363,7 +364,7 @@ EOF
 
 sub generate_code {
     my ($self, $tables) = @_;
-    
+
     # make a copy to destroy
     my @tables = @$tables;
 
@@ -413,7 +414,7 @@ sub generate_code {
                   args => [ $remote_relname,
                             $remote_class,
                             \%cond,
-                            $self->_remote_attrs($local_moniker, $local_cols),
+                            $self->_remote_attrs($local_moniker, $local_cols, $rel->{attrs}),
                   ],
                   extra => {
                       local_class    => $local_class,
@@ -779,7 +780,7 @@ sub _disambiguate {
                 my $rel_name_mapped;
 
                 ($relname_new, $rel_name_mapped) = $self->_rel_name_map($relname_new, $rel->{method}, $local_class, $local_moniker, \@from_cols, $to_class, $remote_moniker, \@to_cols);
-                
+
                 my $mapped = $inflect_mapped || $rel_name_mapped;
 
                 warn <<"EOF" unless $mapped;
index 67af3ee..948d2c4 100644 (file)
@@ -180,10 +180,24 @@ my $tester = dbixcsl_common_tests->new(
                   PRIMARY KEY (`ISO3_code`)
                 ) $innodb
             },
+            # 4 through 10 are used for the multi-schema tests
+            qq{
+                create table mysql_loader_test11 (
+                    id int auto_increment primary key
+                ) $innodb
+            },
+            qq{
+                create table mysql_loader_test12 (
+                    id int auto_increment primary key,
+                    eleven_id int,
+                    foreign key (eleven_id) references mysql_loader_test11(id)
+                        on delete restrict on update set null
+                ) $innodb
+            },
         ],
         pre_drop_ddl => [ 'DROP VIEW mysql_loader_test2', ],
-        drop => [ 'mysql_loader-test1', 'mysql_loader_test3' ],
-        count => 5 + 30 * 2,
+        drop => [ 'mysql_loader-test1', 'mysql_loader_test3', 'mysql_loader_test11', 'mysql_loader_test12' ],
+        count => 8 + 30 * 2,
         run => sub {
             my ($monikers, $classes);
             ($schema, $monikers, $classes) = @_;
@@ -212,6 +226,17 @@ my $tester = dbixcsl_common_tests->new(
             like $code, qr/^=head2 id\n\n(.+:.+\n)+\nThe\nColumn\n\n/m,
                 'column comment and attrs';
 
+            # test on delete/update fk clause introspection
+            ok ((my $rel_info = $schema->source('MysqlLoaderTest12')->relationship_info('eleven')),
+                'got rel info');
+
+            is $rel_info->{attrs}{on_delete}, 'RESTRICT',
+                'ON DELETE clause introspected correctly';
+
+            is $rel_info->{attrs}{on_update}, 'SET NULL',
+                'ON UPDATE clause introspected correctly';
+
+            # multischema tests follow
             SKIP: {
                 my $dbh = $schema->storage->dbh;
 
index 2916131..5d6ca22 100644 (file)
@@ -55,7 +55,7 @@ sub new {
 
     # DB2 and Firebird don't support 'field type NULL'
     $self->{null} = 'NULL' unless defined $self->{null};
-    
+
     $self->{verbose} = $ENV{TEST_VERBOSE} || 0;
 
     # Optional extra tables and tests
@@ -105,7 +105,7 @@ sub run_tests {
             push @connect_info, [ @{$info}{qw/dsn user password connect_info_opts/ } ];
         }
     }
-    
+
     if ($ENV{SCHEMA_LOADER_TESTS_EXTRA_ONLY}) {
         $self->run_only_extra_tests(\@connect_info);
         return;
@@ -169,7 +169,7 @@ sub run_only_extra_tests {
 
         my $file_count = grep $_ =~ SOURCE_DDL, @{ $self->{extra}{create} || [] };
         $file_count++; # schema
-        
+
         if (not ($self->{vendor} eq 'mssql' && $dbh->{Driver}{Name} eq 'Sybase')) {
             $file_count++ for @{ $self->{data_type_tests}{table_names} || [] };
         }
@@ -271,11 +271,11 @@ sub setup_schema {
          eval qq{
              package @{[SCHEMA_CLASS]};
              use base qw/DBIx::Class::Schema::Loader/;
-     
+
              __PACKAGE__->loader_options(\%loader_opts);
              __PACKAGE__->connection(\@\$connect_info);
          };
+
         ok(!$@, "Loader initialization") or diag $@;
 
         find sub { return if -d; $file_count++ }, DUMP_DIR;
@@ -291,25 +291,25 @@ sub setup_schema {
 
             $expected_count += grep $_ =~ SOURCE_DDL,
                 @{ $self->{extra}{create} || [] };
-     
+
             $expected_count -= grep /CREATE TABLE/, @statements_inline_rels
                 if $self->{skip_rels} || $self->{no_inline_rels};
-     
+
             $expected_count -= grep /CREATE TABLE/, @statements_implicit_rels
                 if $self->{skip_rels} || $self->{no_implicit_rels};
-     
+
             $expected_count -= grep /CREATE TABLE/, ($self->{vendor} =~ /sqlite/ ? @statements_advanced_sqlite : @statements_advanced), @statements_reltests
                 if $self->{skip_rels};
         }
+
         is $file_count, $expected_count, 'correct number of files generated';
+
         my $warn_count = 2;
+
         $warn_count++ for grep /^Bad table or view/, @loader_warnings;
+
         $warn_count++ for grep /renaming \S+ relation/, @loader_warnings;
+
         $warn_count++ for grep /\b(?!loader_test9)\w+ has no primary key/i, @loader_warnings;
 
         $warn_count++ for grep /^Column '\w+' in table '\w+' collides with an inherited method\./, @loader_warnings;
@@ -721,7 +721,7 @@ qr/\n__PACKAGE__->load_components\("TestSchemaComponent", "\+TestSchemaComponent
         my $moniker36 = $monikers->{loader_test36};
         my $class36   = $classes->{loader_test36};
         my $rsobj36   = $conn->resultset($moniker36);
-        
+
         isa_ok( $rsobj3, "DBIx::Class::ResultSet" );
         isa_ok( $rsobj4, "DBIx::Class::ResultSet" );
         isa_ok( $rsobj5, "DBIx::Class::ResultSet" );
@@ -789,11 +789,13 @@ qr/\n__PACKAGE__->load_components\("TestSchemaComponent", "\+TestSchemaComponent
         ok ((not try { exists $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{is_deferrable} }),
             'has_many does not have is_deferrable');
 
-        is try { $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_delete} }, 'CASCADE',
-            "on_delete => 'CASCADE' on belongs_to by default";
+        like try { $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_delete} },
+            qr/^(?:CASCADE|RESTRICT)\z/,
+            "on_delete is either CASCADE or RESTRICT on belongs_to by default";
 
-        is try { $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_update} }, 'CASCADE',
-            "on_update => 'CASCADE' on belongs_to by default";
+        like try { $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_update} },
+            qr/^(?:CASCADE|RESTRICT)\z/,
+            "on_update is either CASCADE or RESTRICT on belongs_to by default";
 
         is try { $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{is_deferrable} }, 1,
             "is_deferrable => 1 on belongs_to by default";
@@ -899,11 +901,11 @@ qr/\n__PACKAGE__->(?:belongs_to|has_many|might_have|has_one|many_to_many)\(
         my $rs_rel17 = try { $obj16->search_related('loader_test17_loader16_ones') };
         isa_ok(try { $rs_rel17->single }, $class17);
         is(try { $rs_rel17->single->id }, 3, "search_related with multiple FKs from same table");
-        
+
         # XXX test m:m 18 <- 20 -> 19
         ok($class20->column_info('parent')->{is_foreign_key}, 'Foreign key detected');
         ok($class20->column_info('child')->{is_foreign_key}, 'Foreign key detected');
-        
+
         # XXX test double-fk m:m 21 <- 22 -> 21
         ok($class22->column_info('parent')->{is_foreign_key}, 'Foreign key detected');
         ok($class22->column_info('child')->{is_foreign_key}, 'Foreign key detected');
@@ -965,7 +967,7 @@ qr/\n__PACKAGE__->(?:belongs_to|has_many|might_have|has_one|many_to_many)\(
 
         ok($class32->column_info('rel1')->{is_foreign_key}, 'Foreign key detected');
         ok($class32->column_info('rel2')->{is_foreign_key}, 'Foreign key detected');
-        
+
         my $obj32 = try { $rsobj32->find(1, { prefetch => [qw/rel1 rel2/] }) }
             || try { $rsobj32->search({ id => 1 }, { prefetch => [qw/rel1 rel2/] })->single }
             || $rsobj32->search({ id => 1 })->single;
@@ -1026,7 +1028,7 @@ qr/\n__PACKAGE__->(?:belongs_to|has_many|might_have|has_one|many_to_many)\(
 
         SKIP: {
             skip 'Previous eval block failed', 3 if $@;
-    
+
             my $results = $rsobj10->search({ subject => 'xyzzy' });
             is( $results->count(), 1, 'No duplicate row created' );
 
@@ -1048,7 +1050,7 @@ qr/\n__PACKAGE__->(?:belongs_to|has_many|might_have|has_one|many_to_many)\(
             my $class13   = $classes->{loader_test13};
             my $rsobj13   = $conn->resultset($moniker13);
 
-            isa_ok( $rsobj12, "DBIx::Class::ResultSet" ); 
+            isa_ok( $rsobj12, "DBIx::Class::ResultSet" );
             isa_ok( $rsobj13, "DBIx::Class::ResultSet" );
 
             ok($class13->column_info('id')->{is_foreign_key}, 'Foreign key detected');
@@ -1104,7 +1106,7 @@ EOF
             my $class15   = $classes->{loader_test15};
             my $rsobj15   = $conn->resultset($moniker15);
 
-            isa_ok( $rsobj14, "DBIx::Class::ResultSet" ); 
+            isa_ok( $rsobj14, "DBIx::Class::ResultSet" );
             isa_ok( $rsobj15, "DBIx::Class::ResultSet" );
 
             ok($class15->column_info('loader_test14')->{is_foreign_key}, 'Foreign key detected');
@@ -1447,12 +1449,12 @@ sub create {
         $make_auto_inc->(qw/loader_test1s id/),
 
         q{ INSERT INTO loader_test1s (dat) VALUES('foo') },
-        q{ INSERT INTO loader_test1s (dat) VALUES('bar') }, 
-        q{ INSERT INTO loader_test1s (dat) VALUES('baz') }, 
+        q{ INSERT INTO loader_test1s (dat) VALUES('bar') },
+        q{ INSERT INTO loader_test1s (dat) VALUES('baz') },
 
         # also test method collision
         # crumb_crisp_coating is for col_accessor_map tests
-        qq{ 
+        qq{
             CREATE TABLE loader_test2 (
                 id $self->{auto_inc_pk},
                 dat VARCHAR(32) NOT NULL,
@@ -1471,10 +1473,10 @@ sub create {
         },
         $make_auto_inc->(qw/loader_test2 id/),
 
-        q{ INSERT INTO loader_test2 (dat, dat2) VALUES('aaa', 'zzz') }, 
-        q{ INSERT INTO loader_test2 (dat, dat2) VALUES('bbb', 'yyy') }, 
-        q{ INSERT INTO loader_test2 (dat, dat2) VALUES('ccc', 'xxx') }, 
-        q{ INSERT INTO loader_test2 (dat, dat2) VALUES('ddd', 'www') }, 
+        q{ INSERT INTO loader_test2 (dat, dat2) VALUES('aaa', 'zzz') },
+        q{ INSERT INTO loader_test2 (dat, dat2) VALUES('bbb', 'yyy') },
+        q{ INSERT INTO loader_test2 (dat, dat2) VALUES('ccc', 'xxx') },
+        q{ INSERT INTO loader_test2 (dat, dat2) VALUES('ddd', 'www') },
 
         qq{
             CREATE TABLE LOADER_test23 (
@@ -1549,10 +1551,10 @@ sub create {
             ) $self->{innodb}
         },
 
-        q{ INSERT INTO loader_test3 (id,dat) VALUES(1,'aaa') }, 
-        q{ INSERT INTO loader_test3 (id,dat) VALUES(2,'bbb') }, 
-        q{ INSERT INTO loader_test3 (id,dat) VALUES(3,'ccc') }, 
-        q{ INSERT INTO loader_test3 (id,dat) VALUES(4,'ddd') }, 
+        q{ INSERT INTO loader_test3 (id,dat) VALUES(1,'aaa') },
+        q{ INSERT INTO loader_test3 (id,dat) VALUES(2,'bbb') },
+        q{ INSERT INTO loader_test3 (id,dat) VALUES(3,'ccc') },
+        q{ INSERT INTO loader_test3 (id,dat) VALUES(4,'ddd') },
 
         qq{
             CREATE TABLE loader_test4 (
@@ -1568,7 +1570,7 @@ sub create {
         },
 
         q{ INSERT INTO loader_test4 (id,fkid,dat,belongs_to,set_primary_key) VALUES(123,1,'aaa',1,1) },
-        q{ INSERT INTO loader_test4 (id,fkid,dat,belongs_to,set_primary_key) VALUES(124,2,'bbb',2,2) }, 
+        q{ INSERT INTO loader_test4 (id,fkid,dat,belongs_to,set_primary_key) VALUES(124,2,'bbb',2,2) },
         q{ INSERT INTO loader_test4 (id,fkid,dat,belongs_to,set_primary_key) VALUES(125,3,'ccc',3,3) },
         q{ INSERT INTO loader_test4 (id,fkid,dat,belongs_to,set_primary_key) VALUES(126,4,'ddd',4,4) },
 
@@ -1926,7 +1928,7 @@ sub create {
                 dat VARCHAR(8)
             ) $self->{innodb}
         },
+
         q{ INSERT INTO loader_test14 (id,dat) VALUES (123,'aaa') },
 
         qq{
@@ -2003,7 +2005,7 @@ sub drop_tables {
         loader_test36
         loader_test50
     /;
-    
+
     my @tables_auto_inc = (
         [ qw/loader_test1s id/ ],
         [ qw/loader_test2 id/ ],
@@ -2039,7 +2041,7 @@ sub drop_tables {
         loader_test11
         loader_test10
     /;
-    
+
     my @tables_advanced_auto_inc = (
         [ qw/loader_test10 id10/ ],
         [ qw/loader_test11 id11/ ],
@@ -2095,11 +2097,11 @@ sub drop_tables {
 
             foreach my $table (keys %drop_constraints) {
                 # for MSSQL
-                $dbh->do("ALTER TABLE $table DROP $drop_constraints{$table}"); 
+                $dbh->do("ALTER TABLE $table DROP $drop_constraints{$table}");
                 # for Sybase and Access
-                $dbh->do("ALTER TABLE $table DROP CONSTRAINT $drop_constraints{$table}"); 
+                $dbh->do("ALTER TABLE $table DROP CONSTRAINT $drop_constraints{$table}");
                 # for MySQL
-                $dbh->do("ALTER TABLE $table DROP FOREIGN KEY $drop_constraints{$table}"); 
+                $dbh->do("ALTER TABLE $table DROP FOREIGN KEY $drop_constraints{$table}");
             }
 
             $self->drop_table($dbh, $_) for (@tables_reltests);
@@ -2152,17 +2154,17 @@ sub _custom_column_info {
     $table_name = lc ( $table_name );
     $column_name = lc ( $column_name );
 
-    if ( $table_name eq 'loader_test35' 
-        and $column_name eq 'an_int' 
+    if ( $table_name eq 'loader_test35'
+        and $column_name eq 'an_int'
     ){
         return { is_numeric => 1 }
     }
-    # Set inflate_datetime or  inflate_date to check 
+    # Set inflate_datetime or  inflate_date to check
     #   datetime_timezone and datetime_locale
     if ( $table_name eq 'loader_test36' ){
-        return { inflate_datetime => 1 } if 
+        return { inflate_datetime => 1 } if
             ( $column_name eq 'b_char_as_data' );
-        return { inflate_date => 1 } if 
+        return { inflate_date => 1 } if
             ( $column_name eq 'c_char_as_data' );
     }
 
@@ -2235,7 +2237,7 @@ sub setup_data_type_tests {
             $type_alias =~ s/\W//g;
 
             my $col_name = 'col_' . $type_alias;
-            
+
             if (@size) {
                 my $size_name = join '_', apply { s/\W//g } @size;